/******************************************************************************/
// @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 17:37:41 +0200 (Thu, 16 Sep 2010) $
// $Rev: 3942 $
/******************************************************************************/

module UTRANGERAN_CommonFunctions {
  import from UTRAN_RRC_ASN1_Definitions language "ASN.1:1997" all;
  import from CommonDefs all;
  import from NAS_24008TypeDefs all;
  import from NAS_CommonTypeDefs all;
  import from NAS_24008Templates all;
  import from Parameters all;
//  import from NAS_AuxiliaryDefsAndFunctions all;
  import from CommonIP all;

// -----------------------------------------------------------------------------
  /*
   * @desc      PktDataProtoAddr TS 24.008 clause 10.5.6.4 (TV version IEI is omit)
   * @param     p_IPallocationViaNas
   * @return    PktDataProtoAddr
   */
  function f_UG_GetPktDataProtoAddr (boolean p_IPallocationViaNas) return template (value) PktDataProtoAddr
  {
    var template (value) PktDataProtoAddr v_PDN_Address := cs_PktDataProtoAddrMT('06'O, tsc_PDP_TypeNoIPv4, '00000000'O); // Start with ipv4 values for now
    if ( pc_IPv4 and (not pc_IPv6)) {
      v_PDN_Address.pDP_TypeNo := tsc_PDP_TypeNoIPv4;
      if (p_IPallocationViaNas){
        v_PDN_Address.addrInfo := f_Convert_IPv4Addr2OctString(px_IPv4_Address);
      }
    }  // IPv4
    if ( pc_IPv6 and (not pc_IPv4)) {
      v_PDN_Address.iel := '12'O;
      v_PDN_Address.pDP_TypeNo := tsc_PDP_TypeNoIPv6;
      if (p_IPallocationViaNas){
        v_PDN_Address.addrInfo := f_Convert_IPv6Addr2OctString(px_IPv6_Address);
      }
    } // 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.008 clause 10.5.6.4
      v_PDN_Address.iel := '16'O;
      v_PDN_Address.pDP_TypeNo := tsc_PDP_TypeNoIPv4v6;
      if (p_IPallocationViaNas){
        v_PDN_Address.addrInfo := f_Convert_IPv6Addr2OctString(px_IPv6_Address) & f_Convert_IPv4Addr2OctString(px_IPv4_Address);
        } else {
        v_PDN_Address.addrInfo := f_Convert_IPv6Addr2OctString(px_IPv6_Address) & '00000000'O;
        }
    } // IPv4v6
    return (v_PDN_Address);
  };

  //------------------------------------------------------------------------------------------

  function f_U_Asn2Nas_PlmnId(PLMN_Identity p_PlmnId) return NAS_PlmnId
  {
    // This is a copy of the EUTRAN function, but this version will use the UTRAN ASN

    // @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 representation of the PLMN ID (O3_Type).
    //
    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 Digit v_Digit;
    var integer I;

    for (I := 0; (I < lengthof(p_PlmnId.mcc)) and(I < lengthof(v_MCC)); I := I + 1) {
      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) {
      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]);
  }

  //----------------------------------------------------------------------------
  function f_U_ConvertImsi2ASN (hexstring p_Hex) return IMSI_GSM_MAP
  { // @desc   Convert the hexstring IMSI into the ASN.1 sequence of digits
    // This is similar to the EUTRAN function, but using the UTRAN ASN.1
    var IMSI_GSM_MAP 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      Function to decode the Routing Area Update Request message in to type ROUTINGAREAUPDATEREQUEST
   * @param     p_Octetstring
   * @return    ROUTINGAREAUPDATEREQUEST
   * @status
   */
  function f_DecodeRauReq_Msg ( octetstring p_Octetstring ) return ROUTINGAREAUPDATEREQUEST {
    var ROUTINGAREAUPDATEREQUEST v_ReceivedRauReq_Msg;
    var bitstring v_Bitstring := oct2bit ( p_Octetstring );
    var integer v_Result := decvalue ( v_Bitstring, v_ReceivedRauReq_Msg );
    if (v_Result != 0) {
      FatalError(__FILE__, __LINE__, "Routing Area Request message cannot be decoded");
    }
    return v_ReceivedRauReq_Msg;
  }

  /*
   * @desc      Function to decode the Routing Area Update Request message in to type ROUTINGAREAUPDATEREQUEST and to match
   *            the decoded octetstring with the template provided in p_ExpectedRauReq_Msg
   * @param     p_Octetstring
   * @param     p_ExpectedRauReq_Msg
   * @return    boolean       ... message to be used in case of FAIL
   * @status
   */
  function f_DecodeAndMatch_RauReq_Msg ( octetstring                       p_Octetstring,
                                         template ROUTINGAREAUPDATEREQUEST p_ExpectedRauReq_Msg ) return boolean {

    var ROUTINGAREAUPDATEREQUEST v_ReceivedRauReq_Msg;
    var boolean v_Match := false;

    //Decode the message/octetstring
    v_ReceivedRauReq_Msg := f_DecodeRauReq_Msg ( p_Octetstring );

    //Match the received decoded template with the expected template
    if ( match ( v_ReceivedRauReq_Msg, p_ExpectedRauReq_Msg ) ) {
      v_Match := true;
    }
    return v_Match;
  }

  /*
   * @desc      Function to decode the Location Update Request message in to type LOCATIONUPDATINGREQUEST and to match
   *            the decoded octetstring with the template provided in p_ExpectedReq_Msg
   * @param     p_Octetstring
   * @param     p_ExpectedReq_Msg
   * @return    boolean       ... message to be used in case of FAIL
   * @status
   */
  function f_DecodeAndMatch_LocUpdReq_Msg ( octetstring                       p_Octetstring,
                                         template LOCATIONUPDATINGREQUEST p_ExpectedReq_Msg ) return boolean {

    var LOCATIONUPDATINGREQUEST v_ReceivedLocReq_Msg;
    var boolean v_Match := false;
    var bitstring v_Bitstring := oct2bit(p_Octetstring);
    
    //Decode the message/octetstring
    if (decvalue(v_Bitstring, v_ReceivedLocReq_Msg) != 0) {
      FatalError(__FILE__, __LINE__, "Location Updating Request message cannot be decoded");
    }

    //Match the received decoded template with the expected template
    if ( match ( v_ReceivedLocReq_Msg, p_ExpectedReq_Msg ) ) {
      v_Match := true;
    }
    return v_Match;
  }
  //----------------------------------------------------------------------------
  /*
   * @desc      to calculate timer tolerance acc. to 34.108 cl. 4.2.3
   * @param     p_Timer
   * @return    float
   * @status
   */
  function fl_UG_GetTimerTolerance(float p_Timer) return float
  {
    var float v_LargeTolerance := p_Timer * 0.1;
    var float v_LittleTolerance := (2.0 * 0.01) + 0.055;  /* 2*TTI + 55ms */

    if (v_LittleTolerance <= v_LargeTolerance) {
      return v_LargeTolerance;
    }
    else {
      return v_LittleTolerance;
    }
  }
  //----------------------------------------------------------------------------
  /*
   * @desc      to calculate lower bound of timer tolerance acc. to 34.108 cl. 4.2.3
   * @param     p_Timer
   * @return    float
   * @status
   */
  function f_UG_SetTimerToleranceMin(float p_Timer) return float
  {
    var float v_Tolerance := fl_UG_GetTimerTolerance(p_Timer);
    if (p_Timer < v_Tolerance) {
      return 0.0;
    }
    return p_Timer - v_Tolerance;
}
  //----------------------------------------------------------------------------
  /*
   * @desc      to calculate upper bound of timer tolerance acc. to 34.108 cl. 4.2.3
   * @param     p_Timer
   * @return    float
   * @status
   */
  function f_UG_SetTimerToleranceMax(float p_Timer) return float
  {
    return p_Timer + fl_UG_GetTimerTolerance (p_Timer);
  }
}
