/******************************************************************************/
// @copyright   Copyright Notification
//   No part may be reproduced except as authorized by written permission.
//   The copyright and the foregoing restriction extend to reproduction in all media.
//   Trademark 2010, 3GPP Organizational Partners (ARIB, ATIS, CCSA, ETSI, TTA, TTC).
//   All rights reserved.
// @version: IWD_10wk37
// $Date: 2010-09-16 23:11:35 +0200 (Thu, 16 Sep 2010) $
// $Rev: 3943 $
/******************************************************************************/

module EUTRA_AuxiliaryFunctions {

  import from EUTRA_RRC_ASN1_Definitions language "ASN.1:1997" all;
  import from CommonDefs all;
  import from EUTRA_CommonDefs all;
//   import from EUTRA_ASP_TypeDefs all;
  import from NAS_CommonTypeDefs all;
  import from NAS_CommonTemplates all;
  import from EPS_NAS_Constants all;
  import from EPS_NAS_TypeDefs all;
  import from Parameters all;
//  import from NAS_AuxiliaryDefsAndFunctions all;
  import from CommonIP all;

  //============================================================================
  group ConversionsAndInstantiations {
  //----------------------------------------------------------------------------
  /*
   * @desc      convert parameters contained in GutiParameters_Type into an octetstring being used in NAS messages
   *
   * @param     p_GutiParams   parameters to construct the Guti string (see 24.301 cl. 9.9.3.12)
   * @return    octetstring representing the Guti
   * @status    APPROVED
   */
  function f_GutiParameters2GutiString(GutiParameters_Type p_GutiParams) return octetstring
  {
    var NAS_PlmnId v_PlmnId := f_Asn2Nas_PlmnId(p_GutiParams.PLMN_Identity);  // 3 octets
    var EPS_MmeGi  v_MmeGi  := bit2oct(p_GutiParams.MME_GroupId);             // 2 octets
    var EPS_Mmec   v_Mmec   := bit2oct(p_GutiParams.MME_Code);                // 1 octets
    var EPS_M_Tmsi v_Mtmsi  := bit2oct(p_GutiParams.M_TMSI);                  // 4 octets
    return v_PlmnId & v_MmeGi & v_Mmec & v_Mtmsi;
  }

  /*
   * @desc      convert from GutiParameters_Type to NAS MobileIdentity
   *            returned IE does not contain an IEI
   * @param     p_IEI
   * @param     p_GutiParams                       .. parameters which are required to construct a GUTI
   * @return    template (value) MobileIdentity    .. representing the given GutiParameters_Type
   * @status    APPROVED
   */
  function f_GutiParameters2MobileIdentity(template (omit) IEI8_Type p_IEI,
                                           GutiParameters_Type p_GutiParams) return template (value) MobileIdentity
  {
    var octetstring v_GutiString := f_GutiParameters2GutiString(p_GutiParams);
    return cs_MobileIdentityGuti(p_IEI, v_GutiString);
  }

  /*
   * @desc      Convert the hexstring IMSI into the ASN.1 sequence of digits
   * @param     p_Hex
   * @return    IMSI           ASN.1 type IMSI
   * @status    APPROVED
   */
  function f_ConvertImsi2ASN (hexstring p_Hex) return IMSI
  {
    var IMSI v_ReturnValue;
    var integer I;
    var integer v_Length := lengthof(p_Hex);

    if (v_Length < 6 or v_Length > 21) {
      FatalError(__FILE__, __LINE__, "invalid length of IMSI");
    }
    for (I := 0; I < v_Length; I := I + 1) {
      v_ReturnValue[I] := hex2int(p_Hex[I]);
    }
    return v_ReturnValue;
  }

  //------------------------------------------------------------------------------------------
  /*
   * @desc      Converts between NAS internal representation of MCC to the MCC ASN.1 type
   * @param     p_MCC   MCC as plain string of 3 decimal digits
   * @return    MCC as ASN.1 type (defined as array of 3 integers)
   * @status    APPROVED
   */
  function f_ConvertMCC_Nas2Asn(NAS_Mcc p_MCC) return MCC
  {
    return { hex2int(p_MCC[2]), hex2int(p_MCC[1]), hex2int(p_MCC[0]) };
  }

  function f_ConvertMCC_Asn2Nas(MCC p_MCC) return NAS_Mcc
  { /* @desc    Converts MCC ASN.1 type to NAS internal representation
     * @param   p_MCC MCC as ASN.1 type (defined as array of 3 integers)
     * @return  MCC as plain string of 3 decimal digits */
    return int2hex(p_MCC[2], 1) & int2hex(p_MCC[1], 1) & int2hex(p_MCC[0], 1);
  }

  //------------------------------------------------------------------------------------------
  /*
   * @desc      Converts a PLMN ID from ASN.1 to NAS representation.
   * @param     p_PlmnId   .. PLMN ID as defined in LTE RRC ASN.1.
   * @return    NAS_PlmnId .. NAS representation of the PLMN ID (O3_Type).
   * @status    APPROVED
   */
  function f_Asn2Nas_PlmnId(PLMN_Identity p_PlmnId) return NAS_PlmnId
  {
    var hexstring v_MCC := '000'H; // initialised with 3 digits which will be overwritten
    var hexstring v_MNC := 'FFF'H; // initialised with 3 'F'H => if lengthof(p_PlmnId.mnc) < 3 the remaining digits are 'F'H
    var MCC_MNC_Digit v_Digit;     // MCC-MNC-Digit ::= INTEGER (0..9)
    var integer I;

    for (I := 0; (I < lengthof(p_PlmnId.mcc)) and(I < lengthof(v_MCC)); I := I + 1) {
      /* p_PlmnId.mcc consists of 3 digits; nevertheless to be sure v_MCC is checked as well */
      v_Digit := p_PlmnId.mcc[I];
      v_MCC[I] := int2hex(v_Digit, 1);
    }

    for (I := 0; (I < lengthof(p_PlmnId.mnc)) and (I < lengthof(v_MNC)); I := I + 1) {
      /* p_PlmnId.mcc consists of 2 or 3 digits; nevertheless to be sure v_MNC is checked as well */
      v_Digit := p_PlmnId.mnc[I];
      v_MNC[I] := int2hex(v_Digit, 1);
    }

    // build PLMN ID representation as used in NAS
    // TAI/TAI list/MobileIdentity[GUTI]/...
    return hex2oct(v_MCC[1] & v_MCC[0] &
                   v_MNC[2] & v_MCC[2] &
                   v_MNC[1] & v_MNC[0]);
  }

  //------------------------------------------------------------------------------------------
  /*
   * @desc      Creates a new unique M-TMSI based on the current one.
   * @param     p_M_Tmsi      Current M-TMSI value.
   * @return    M_TMSI_Type   New M-TMSI (which is guarantied to be different from p_M_Tmsi).
   * @status    APPROVED
   */
  function f_CreateNewM_Tmsi(M_TMSI_Type p_M_Tmsi) return M_TMSI_Type
  {
    return p_M_Tmsi <@ 1;
  }

  //------------------------------------------------------------------------------------------
  /*
   * @desc      Creates a new unique S-TMSI based on the current one by
   *            incrementing MMEC and changing S-TMSI to a new value.
   * @param     p_S_Tmsi      Current S-TMSI value.
   * @return    S_TMSI        New S-TMSI (which is guarantied to be different from p_S_Tmsi).
   * @status    APPROVED
   */
  function f_CreateNewS_Tmsi(S_TMSI p_S_Tmsi) return S_TMSI
  {
    var S_TMSI v_S_TMSI := {
      mmec := int2bit(((bit2int(p_S_Tmsi.mmec) + 1) mod 255), 8),
      m_TMSI := f_CreateNewM_Tmsi(p_S_Tmsi.m_TMSI)
    };
    return v_S_TMSI;
  }

  //------------------------------------------------------------------------------------------
  /*
   * @desc      Creates a new GutiParameters_Type variable which is identical to the given GUTI
   *            except that both MMEC and M-TMSI (i.e. S-TMSI) will be updated to new values.
   * @param     p_GutiParams             Current GUTI.
   * @return    GutiParameters_Type      New GUTI.
   * @status    APPROVED
   */
  function f_CreateGutiParamsWithUpdatedSTmsi(GutiParameters_Type p_GutiParams) return GutiParameters_Type
  {
    var GutiParameters_Type v_NewGutiParams := p_GutiParams;
    var S_TMSI v_S_Tmsi := {
      mmec := p_GutiParams.MME_Code,
      m_TMSI := p_GutiParams.M_TMSI
    };

    v_S_Tmsi := f_CreateNewS_Tmsi(v_S_Tmsi);

    v_NewGutiParams.MME_Code := v_S_Tmsi.mmec;
    v_NewGutiParams.M_TMSI := v_S_Tmsi.m_TMSI;

    return v_NewGutiParams;
  }

  //------------------------------------------------------------------------------------------
  /*
   * @desc      PDN Address TS 24.301 clause 9.9.4.9 (TV version IEI is omit)
   *            Default value according to TS 36.508 table 6.6.1-1 (which requires a 'static' value)
   * @param     p_IPallocationViaNas
   * @param     p_LoopbackTest      (default value: false)
   * @return    PDN_Address
   * @status    APPROVED
   */
  function f_GetPDNAddress (boolean p_IPallocationViaNas,
                            boolean p_LoopbackTest := false) return PDN_Address
  {
    var O16_Type v_Addr6;
    var PDN_Address v_PDN_Address := {
      iei := omit, // '59'O,
      iel := '05'O,                  // as for IPv4
      spare := tsc_Spare5,
      typeValue := tsc_PdnType_IPv4, // as for IPv4
      adressInfo := '00000000'O      // IP assignment via DHCP
    };
    if ( pc_IPv4 and (not pc_IPv6)) {
      v_PDN_Address.typeValue := tsc_PdnType_IPv4;
      if (p_IPallocationViaNas){
        v_PDN_Address.adressInfo := f_Convert_IPv4Addr2OctString(px_IPv4_Address);
      }
    }  // IPv4
    if ( pc_IPv6 and (not pc_IPv4)) {
      v_Addr6 := f_Convert_IPv6Addr2OctString(px_IPv6_Address);
      v_PDN_Address.iel := '09'O;
      v_PDN_Address.typeValue := tsc_PdnType_IPv6;
      // Set only the IPv6 interface identifier, which are the 8 least significant octets of theIPv6 address
      if (p_IPallocationViaNas){
        v_PDN_Address.adressInfo := substr( v_Addr6, 8, 8 );
      }
    } // IPv6
    if ( pc_IPv6 and pc_IPv4) {
      // Address info consists of 8 octets IPv6 interface identifier followed with the 4 octets IPv4 address
      // See TS 24.301 clause 9.9.4.9
      v_Addr6 := f_Convert_IPv6Addr2OctString(px_IPv6_Address);
      v_PDN_Address.iel := '0D'O;
      v_PDN_Address.typeValue := tsc_PdnType_IPv4v6;
      if (p_IPallocationViaNas){
        v_PDN_Address.adressInfo := substr( v_Addr6, 8, 8 ) & f_Convert_IPv4Addr2OctString(px_IPv4_Address);  // @sic R5s090322 sic@
        } else {
        v_PDN_Address.adressInfo := substr( v_Addr6, 8, 8 ) & '00000000'O;
        }
    } // IPv4v6
    
    // Special case for loopback tests, 36.508 Table 4.5.2A.4-3
    // @sic R5-101045, R5s100189 sic@
    if ( p_LoopbackTest and px_IPv4viaNAS_TestMode ){
      v_PDN_Address.typeValue := tsc_PdnType_IPv4;
      v_PDN_Address.adressInfo := f_Convert_IPv4Addr2OctString(px_IPv4_Address);
    }
    return (v_PDN_Address);
  };

  //------------------------------------
  /*
   * @desc      Function used to check whether the UE requests in its PCO an address allocation via NAS signalling
   *            If the UE does not send PCO, then address assignment shall be via DHCP. TS 36.508 table 4.7.3-6.
   * @param     p_Pco
   * @return    boolean
   * @status    APPROVED
   */
  function f_CheckPCOforIPallocationViaNas( template (omit) ProtocolConfigOptions p_Pco) return boolean
  {
    var ProtocolConfigOptions v_Pco;
    var integer i;

    if (isvalue(p_Pco)) {
      v_Pco := valueof (p_Pco);
      for (i := 0; i < sizeof(v_Pco.pco); i := i + 1 ) {
        if(v_Pco.pco[i].protocolID == '000B'O) { // TS 24.008 clause 10.5.6.3  // @sic R5s090322 sic@
         return false;  // @sic R5s090322 sic@
        }
      }
    }
    return true;  // @sic R5s090322 sic@
  }

  //------------------------------------
  /*
   * @desc      Function used to check whether the UE requests a P-CSCF address in its PCO
   *            If the UE does not support IMS, or doesn't request a P-CSCF address, the function returns the default PCO, PPP
   *            When IMS is introduced into LTE, the correct PCSCF PIXIT needs to be used here
   * @param     p_Pco
   * @return    template (value) ProtocolConfigOptions
   * @status    APPROVED
   */
  function f_GetDefPCOforPCSCFAddress( template (omit) ProtocolConfigOptions p_Pco) return template (value) ProtocolConfigOptions
  {
    var ProtocolConfigOptions v_Rcvd;
    var template (value) ProtocolConfigOptions v_Pco := cs_ProtocolConfigOptions_PPP;
    var octetstring v_Addressv4 := f_Convert_IPv4Addr2OctString(px_IPv4_Address); // TO DO Is this the right PIXIT????
    var octetstring v_Addressv6 := f_Convert_IPv6Addr2OctString(px_IPv6_Address); // TO DO Is this the right PIXIT????
    var integer i;

    if (pc_IMS and isvalue(p_Pco)) {
      v_Rcvd := valueof (p_Pco);
      for (i := 0; i < sizeof(v_Rcvd.pco); i := i + 1 ) {
        if(v_Rcvd.pco[i].protocolID == tsc_PCO_Id_PCSCFv4) { // TS 24.008 clause 10.5.6.3
         v_Pco.pco := {cs_PCO_Container_PCSCFv4 (int2oct(lengthof(v_Addressv4), 1), v_Addressv4)};
         break;
        }
        else if(v_Rcvd.pco[i].protocolID == tsc_PCO_Id_PCSCFv6) { // TS 24.008 clause 10.5.6.3
         v_Pco.pco := {cs_PCO_Container_PCSCFv6 (int2oct(lengthof(v_Addressv6), 1), v_Addressv6)};
         break;
        }
      }
    }
    return v_Pco;
  }

  //------------------------------------
  /*
   * @desc      check esmInfoTransferFlag in PDN_CONNECTIVITY_REQUEST
   * @param     p_PDN_CONNECTIVITY_REQUEST
   * @return    boolean
   * @status    APPROVED
   */
  function f_CheckEsmInfoTransferFlag(PDN_CONNECTIVITY_REQUEST p_PDN_CONNECTIVITY_REQUEST) return boolean
  {
    if (ispresent(p_PDN_CONNECTIVITY_REQUEST.esmInfoTransferFlag)) {
      if (p_PDN_CONNECTIVITY_REQUEST.esmInfoTransferFlag.eitValue == '1'B) {
        return true;
      }
    }
    return false;
  }

  } // end of group ConvertionsAndInstantionations
  //============================================================================
}
