/*
 * Copyright (c) 2015, EURECOM (www.eurecom.fr)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those
 * of the authors and should not be interpreted as representing official policies,
 * either expressed or implied, of the FreeBSD Project.
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>


#include "TLVEncoder.h"
#include "TLVDecoder.h"
#include "MobileIdentity.h"

static int decode_imsi_mobile_identity(ImsiMobileIdentity_t *imsi, uint8_t *buffer);
static int decode_imei_mobile_identity(ImeiMobileIdentity_t *imei, uint8_t *buffer);
static int decode_imeisv_mobile_identity(ImeisvMobileIdentity_t *imeisv, uint8_t *buffer);
static int decode_tmsi_mobile_identity(TmsiMobileIdentity_t *tmsi, uint8_t *buffer);
static int decode_tmgi_mobile_identity(TmgiMobileIdentity_t *tmgi, uint8_t *buffer);
static int decode_no_mobile_identity(NoMobileIdentity_t *no_id, uint8_t *buffer);

static int encode_imsi_mobile_identity(ImsiMobileIdentity_t *imsi, uint8_t *buffer);
static int encode_imei_mobile_identity(ImeiMobileIdentity_t *imei, uint8_t *buffer);
static int encode_imeisv_mobile_identity(ImeisvMobileIdentity_t *imeisv, uint8_t *buffer);
static int encode_tmsi_mobile_identity(TmsiMobileIdentity_t *tmsi, uint8_t *buffer);
static int encode_tmgi_mobile_identity(TmgiMobileIdentity_t *tmgi, uint8_t *buffer);
static int encode_no_mobile_identity(NoMobileIdentity_t *no_id, uint8_t *buffer);

int decode_mobile_identity(MobileIdentity *mobileidentity, uint8_t iei, uint8_t *buffer, uint32_t len)
{
  int decoded_rc = TLV_DECODE_VALUE_DOESNT_MATCH;
  int decoded = 0;
  uint8_t ielen = 0;

  if (iei > 0) {
    CHECK_IEI_DECODER(iei, *buffer);
    decoded++;
  }

  ielen = *(buffer + decoded);
  decoded++;

  uint8_t typeofidentity = *(buffer + decoded) & 0x7;

  if (typeofidentity != MOBILE_IDENTITY_NOT_AVAILABLE) {
    CHECK_LENGTH_DECODER(len - decoded, ielen);

    if (typeofidentity == MOBILE_IDENTITY_IMSI) {
      decoded_rc = decode_imsi_mobile_identity(&mobileidentity->imsi,
                   buffer + decoded);
    } else if (typeofidentity == MOBILE_IDENTITY_IMEI) {
      decoded_rc = decode_imei_mobile_identity(&mobileidentity->imei,
                   buffer + decoded);
    } else if (typeofidentity == MOBILE_IDENTITY_IMEISV) {
      decoded_rc = decode_imeisv_mobile_identity(&mobileidentity->imeisv,
                   buffer + decoded);
    } else if (typeofidentity == MOBILE_IDENTITY_TMSI) {
      decoded_rc = decode_tmsi_mobile_identity(&mobileidentity->tmsi,
                   buffer + decoded);
    } else if (typeofidentity == MOBILE_IDENTITY_TMGI) {
      decoded_rc = decode_tmgi_mobile_identity(&mobileidentity->tmgi,
                   buffer + decoded);
    }
  } else if (ielen == MOBILE_IDENTITY_NOT_AVAILABLE_LTE_LENGTH) {
    decoded_rc = decode_no_mobile_identity(&mobileidentity->no_id,
                                           buffer + decoded);
  }

  if (decoded_rc < 0) {
    return decoded_rc;
  }

#if defined (NAS_DEBUG)
  dump_mobile_identity_xml(mobileidentity, iei);
#endif
  return (decoded + decoded_rc);
}

int encode_mobile_identity(MobileIdentity *mobileidentity, uint8_t iei, uint8_t *buffer, uint32_t len)
{
  uint8_t *lenPtr;
  int encoded_rc = TLV_ENCODE_VALUE_DOESNT_MATCH;
  uint32_t encoded = 0;
  /* Checking IEI and pointer */
  CHECK_PDU_POINTER_AND_LENGTH_ENCODER(buffer, MOBILE_IDENTITY_MINIMUM_LENGTH, len);
#if defined (NAS_DEBUG)
  dump_mobile_identity_xml(mobileidentity, iei);
#endif

  if (iei > 0) {
    *buffer = iei;
    encoded++;
  }

  lenPtr  = (buffer + encoded);
  encoded ++;

  if (mobileidentity->no_id.typeofidentity != MOBILE_IDENTITY_NOT_AVAILABLE) {
    if (mobileidentity->imsi.typeofidentity == MOBILE_IDENTITY_IMSI) {
      encoded_rc = encode_imsi_mobile_identity(&mobileidentity->imsi,
                   buffer + encoded);
    } else if (mobileidentity->imei.typeofidentity == MOBILE_IDENTITY_IMEI) {
      encoded_rc = encode_imei_mobile_identity(&mobileidentity->imei,
                   buffer + encoded);
    } else if (mobileidentity->imeisv.typeofidentity == MOBILE_IDENTITY_IMEISV) {
      encoded_rc = encode_imeisv_mobile_identity(&mobileidentity->imeisv,
                   buffer + encoded);
    } else if (mobileidentity->tmsi.typeofidentity == MOBILE_IDENTITY_TMSI) {
      encoded_rc = encode_tmsi_mobile_identity(&mobileidentity->tmsi,
                   buffer + encoded);
    } else if (mobileidentity->tmgi.typeofidentity == MOBILE_IDENTITY_TMGI) {
      encoded_rc = encode_tmgi_mobile_identity(&mobileidentity->tmgi,
                   buffer + encoded);
    }

    if (encoded_rc > 0) {
      *lenPtr = encoded + encoded_rc - 1 - ((iei > 0) ? 1 : 0);
    }
  } else {
    encoded_rc = encode_no_mobile_identity(&mobileidentity->no_id,
                                           buffer + encoded);

    if (encoded_rc > 0) {
      *lenPtr = MOBILE_IDENTITY_NOT_AVAILABLE_LTE_LENGTH;
    }
  }

  if (encoded_rc < 0) {
    return encoded_rc;
  }

  return (encoded + encoded_rc);
}

void dump_mobile_identity_xml(MobileIdentity *mobileidentity, uint8_t iei)
{
  printf("<Mobile Identity>\n");

  if (iei > 0)
    /* Don't display IEI if = 0 */
    printf("    <IEI>0x%X</IEI>\n", iei);

  if (mobileidentity->imsi.typeofidentity == MOBILE_IDENTITY_IMSI) {
    ImsiMobileIdentity_t* imsi = &mobileidentity->imsi;
    printf("    <odd even>%u</odd even>\n", imsi->oddeven);
    printf("    <Type of identity>IMSI</Type of identity>\n");
    printf("    <digit1>%u</digit1>\n", imsi->digit1);
    printf("    <digit2>%u</digit2>\n", imsi->digit2);
    printf("    <digit3>%u</digit3>\n", imsi->digit3);
    printf("    <digit4>%u</digit4>\n", imsi->digit4);
    printf("    <digit5>%u</digit5>\n", imsi->digit5);
    printf("    <digit6>%u</digit6>\n", imsi->digit6);
    printf("    <digit7>%u</digit7>\n", imsi->digit7);
    printf("    <digit8>%u</digit8>\n", imsi->digit8);
    printf("    <digit9>%u</digit9>\n", imsi->digit9);
    printf("    <digit10>%u</digit10>\n", imsi->digit10);
    printf("    <digit11>%u</digit11>\n", imsi->digit11);
    printf("    <digit12>%u</digit12>\n", imsi->digit12);
    printf("    <digit13>%u</digit13>\n", imsi->digit13);
    printf("    <digit14>%u</digit14>\n", imsi->digit14);
    printf("    <digit15>%u</digit15>\n", imsi->digit15);
  } else if (mobileidentity->imei.typeofidentity == MOBILE_IDENTITY_IMEI) {
    ImeiMobileIdentity_t* imei = &mobileidentity->imei;
    printf("    <odd even>%u</odd even>\n", imei->oddeven);
    printf("    <Type of identity>IMEI</Type of identity>\n");
    printf("    <digit1>%u</digit1>\n", imei->digit1);
    printf("    <digit2>%u</digit2>\n", imei->digit2);
    printf("    <digit3>%u</digit3>\n", imei->digit3);
    printf("    <digit4>%u</digit4>\n", imei->digit4);
    printf("    <digit5>%u</digit5>\n", imei->digit5);
    printf("    <digit6>%u</digit6>\n", imei->digit6);
    printf("    <digit7>%u</digit7>\n", imei->digit7);
    printf("    <digit8>%u</digit8>\n", imei->digit8);
    printf("    <digit9>%u</digit9>\n", imei->digit9);
    printf("    <digit10>%u</digit10>\n", imei->digit10);
    printf("    <digit11>%u</digit11>\n", imei->digit11);
    printf("    <digit12>%u</digit12>\n", imei->digit12);
    printf("    <digit13>%u</digit13>\n", imei->digit13);
    printf("    <digit14>%u</digit14>\n", imei->digit14);
    printf("    <digit15>%u</digit15>\n", imei->digit15);
  } else if (mobileidentity->imeisv.typeofidentity == MOBILE_IDENTITY_IMEISV) {
    ImeisvMobileIdentity_t* imeisv = &mobileidentity->imeisv;
    printf("    <odd even>%u</odd even>\n", imeisv->oddeven);
    printf("    <Type of identity>IMEISV</Type of identity>\n");
    printf("    <digit1>%u</digit1>\n", imeisv->digit1);
    printf("    <digit2>%u</digit2>\n", imeisv->digit2);
    printf("    <digit3>%u</digit3>\n", imeisv->digit3);
    printf("    <digit4>%u</digit4>\n", imeisv->digit4);
    printf("    <digit5>%u</digit5>\n", imeisv->digit5);
    printf("    <digit6>%u</digit6>\n", imeisv->digit6);
    printf("    <digit7>%u</digit7>\n", imeisv->digit7);
    printf("    <digit8>%u</digit8>\n", imeisv->digit8);
    printf("    <digit9>%u</digit9>\n", imeisv->digit9);
    printf("    <digit10>%u</digit10>\n", imeisv->digit10);
    printf("    <digit11>%u</digit11>\n", imeisv->digit11);
    printf("    <digit12>%u</digit12>\n", imeisv->digit12);
    printf("    <digit13>%u</digit13>\n", imeisv->digit13);
    printf("    <digit14>%u</digit14>\n", imeisv->digit14);
    printf("    <digit15>%u</digit15>\n", imeisv->digit15);
  } else if (mobileidentity->tmsi.typeofidentity == MOBILE_IDENTITY_TMSI) {
    TmsiMobileIdentity_t* tmsi = &mobileidentity->tmsi;
    printf("    <odd even>%u</odd even>\n", tmsi->oddeven);
    printf("    <Type of identity>TMSI</Type of identity>\n");
    printf("    <digit1>%u</digit1>\n", tmsi->digit1);
    printf("    <digit2>%u</digit2>\n", tmsi->digit2);
    printf("    <digit3>%u</digit3>\n", tmsi->digit3);
    printf("    <digit4>%u</digit4>\n", tmsi->digit4);
    printf("    <digit5>%u</digit5>\n", tmsi->digit5);
    printf("    <digit6>%u</digit6>\n", tmsi->digit6);
    printf("    <digit7>%u</digit7>\n", tmsi->digit7);
    printf("    <digit8>%u</digit8>\n", tmsi->digit8);
    printf("    <digit9>%u</digit9>\n", tmsi->digit9);
    printf("    <digit10>%u</digit10>\n", tmsi->digit10);
    printf("    <digit11>%u</digit11>\n", tmsi->digit11);
    printf("    <digit12>%u</digit12>\n", tmsi->digit12);
    printf("    <digit13>%u</digit13>\n", tmsi->digit13);
    printf("    <digit14>%u</digit14>\n", tmsi->digit14);
    printf("    <digit15>%u</digit15>\n", tmsi->digit15);
  } else if (mobileidentity->tmgi.typeofidentity == MOBILE_IDENTITY_TMGI) {
    TmgiMobileIdentity_t* tmgi = &mobileidentity->tmgi;
    printf("    <MBMS session ID indication>%u</MBMS session ID indication>\n", tmgi->mbmssessionidindication);
    printf("    <MCC MNC indication>%u</MCC MNC indication>\n",
           tmgi->mccmncindication);
    printf("    <Odd even>%u</Odd even>\n",
           tmgi->oddeven);
    printf("    <Type of identity>TMGI</Type of identity>\n");
    printf("    <MBMS service ID>%u</MBMS service ID>\n",
           tmgi->mbmsserviceid);
    printf("    <MCC digit 2>%u</MCC digit 2>\n", tmgi->mccdigit2);
    printf("    <MCC digit 1>%u</MCC digit 1>\n", tmgi->mccdigit1);
    printf("    <MNC digit 3>%u</MNC digit 3>\n", tmgi->mncdigit3);
    printf("    <MCC digit 3>%u</MCC digit 3>\n", tmgi->mccdigit3);
    printf("    <MNC digit 2>%u</MNC digit 2>\n", tmgi->mncdigit2);
    printf("    <MNC digit 1>%u</MNC digit 1>\n", tmgi->mncdigit1);
    printf("    <MBMS session ID>%u</MBMS session ID>\n",
           tmgi->mbmssessionid);
  } else {
    printf("    Wrong type of mobile identity (%u)\n", mobileidentity->imsi.typeofidentity);
  }

  printf("</Mobile Identity>\n");
}

static int decode_imsi_mobile_identity(ImsiMobileIdentity_t *imsi, uint8_t *buffer)
{
  int decoded = 0;
  imsi->typeofidentity = *(buffer + decoded) & 0x7;

  if (imsi->typeofidentity != MOBILE_IDENTITY_IMSI) {
    return (TLV_ENCODE_VALUE_DOESNT_MATCH);
  }

  imsi->oddeven = (*(buffer + decoded) >> 3) & 0x1;
  imsi->digit1 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imsi->digit2 = *(buffer + decoded) & 0xf;
  imsi->digit3 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imsi->digit4 = *(buffer + decoded) & 0xf;
  imsi->digit5 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imsi->digit6 = *(buffer + decoded) & 0xf;
  imsi->digit7 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imsi->digit8 = *(buffer + decoded) & 0xf;
  imsi->digit9 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imsi->digit10 = *(buffer + decoded) & 0xf;
  imsi->digit11 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imsi->digit12 = *(buffer + decoded) & 0xf;
  imsi->digit13 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imsi->digit14 = *(buffer + decoded) & 0xf;
  imsi->digit15 = (*(buffer + decoded) >> 4) & 0xf;

  /*
   * IMSI is coded using BCD coding. If the number of identity digits is
   * even then bits 5 to 8 of the last octet shall be filled with an end
   * mark coded as "1111".
   */
  if ((imsi->oddeven == MOBILE_IDENTITY_EVEN) && (imsi->digit15 != 0x0f)) {
    return (TLV_ENCODE_VALUE_DOESNT_MATCH);
  }

  decoded++;
  return decoded;
}

static int decode_imei_mobile_identity(ImeiMobileIdentity_t *imei, uint8_t *buffer)
{
  int decoded = 0;
  imei->typeofidentity = *(buffer + decoded) & 0x7;

  if (imei->typeofidentity != MOBILE_IDENTITY_IMEI) {
    return (TLV_ENCODE_VALUE_DOESNT_MATCH);
  }

  imei->oddeven = (*(buffer + decoded) >> 3) & 0x1;
  imei->digit1 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imei->digit2 = *(buffer + decoded) & 0xf;
  imei->digit3 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imei->digit4 = *(buffer + decoded) & 0xf;
  imei->digit5 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imei->digit6 = *(buffer + decoded) & 0xf;
  imei->digit7 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imei->digit8 = *(buffer + decoded) & 0xf;
  imei->digit9 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imei->digit10 = *(buffer + decoded) & 0xf;
  imei->digit11 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imei->digit12 = *(buffer + decoded) & 0xf;
  imei->digit13 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imei->digit14 = *(buffer + decoded) & 0xf;
  imei->digit15 = (*(buffer + decoded) >> 4) & 0xf;

  /*
   * IMEI is coded using BCD coding. If the number of identity digits is
   * even then bits 5 to 8 of the last octet shall be filled with an end
   * mark coded as "1111".
   */
  if ((imei->oddeven == MOBILE_IDENTITY_EVEN) && (imei->digit15 != 0x0f)) {
    return (TLV_ENCODE_VALUE_DOESNT_MATCH);
  }

  decoded++;
  return decoded;
}

static int decode_imeisv_mobile_identity(ImeisvMobileIdentity_t *imeisv, uint8_t *buffer)
{
  int decoded = 0;
  imeisv->typeofidentity = *(buffer + decoded) & 0x7;

  if (imeisv->typeofidentity != MOBILE_IDENTITY_IMEISV) {
    return (TLV_ENCODE_VALUE_DOESNT_MATCH);
  }

  imeisv->oddeven = (*(buffer + decoded) >> 3) & 0x1;
  imeisv->digit1 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imeisv->digit2 = *(buffer + decoded) & 0xf;
  imeisv->digit3 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imeisv->digit4 = *(buffer + decoded) & 0xf;
  imeisv->digit5 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imeisv->digit6 = *(buffer + decoded) & 0xf;
  imeisv->digit7 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imeisv->digit8 = *(buffer + decoded) & 0xf;
  imeisv->digit9 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imeisv->digit10 = *(buffer + decoded) & 0xf;
  imeisv->digit11 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imeisv->digit12 = *(buffer + decoded) & 0xf;
  imeisv->digit13 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  imeisv->digit14 = *(buffer + decoded) & 0xf;
  imeisv->digit15 = (*(buffer + decoded) >> 4) & 0xf;

  /*
   * IMEISV is coded using BCD coding. If the number of identity digits is
   * even then bits 5 to 8 of the last octet shall be filled with an end
   * mark coded as "1111".
   */
  if ((imeisv->oddeven == MOBILE_IDENTITY_EVEN) && (imeisv->digit15 != 0x0f)) {
    return (TLV_ENCODE_VALUE_DOESNT_MATCH);
  }

  decoded++;
  return decoded;
}

static int decode_tmsi_mobile_identity(TmsiMobileIdentity_t *tmsi, uint8_t *buffer)
{
  int decoded = 0;
  tmsi->typeofidentity = *(buffer + decoded) & 0x7;

  if (tmsi->typeofidentity != MOBILE_IDENTITY_TMSI) {
    return (TLV_ENCODE_VALUE_DOESNT_MATCH);
  }

  tmsi->oddeven = (*(buffer + decoded) >> 3) & 0x1;
  tmsi->digit1 = (*(buffer + decoded) >> 4) & 0xf;

  /*
   * If the mobile identity is the TMSI/P-TMSI/M-TMSI then bits 5 to 8
   * of octet 3 are coded as "1111".
   */
  if (tmsi->digit1 != 0xf) {
    return (TLV_ENCODE_VALUE_DOESNT_MATCH);
  }

  decoded++;
  tmsi->digit2 = *(buffer + decoded) & 0xf;
  tmsi->digit3 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  tmsi->digit4 = *(buffer + decoded) & 0xf;
  tmsi->digit5 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  tmsi->digit6 = *(buffer + decoded) & 0xf;
  tmsi->digit7 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  tmsi->digit8 = *(buffer + decoded) & 0xf;
  tmsi->digit9 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  tmsi->digit10 = *(buffer + decoded) & 0xf;
  tmsi->digit11 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  tmsi->digit12 = *(buffer + decoded) & 0xf;
  tmsi->digit13 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  tmsi->digit14 = *(buffer + decoded) & 0xf;
  tmsi->digit15 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  return decoded;
}

static int decode_tmgi_mobile_identity(TmgiMobileIdentity_t *tmgi, uint8_t *buffer)
{
  int decoded = 0;
  tmgi->spare = (*(buffer + decoded) >> 6) & 0x2;

  /*
   * Spare bits are coded with 0s
   */
  if (tmgi->spare != 0) {
    return (TLV_ENCODE_VALUE_DOESNT_MATCH);
  }

  tmgi->mbmssessionidindication = (*(buffer + decoded) >> 5) & 0x1;
  tmgi->mccmncindication = (*(buffer + decoded) >> 4) & 0x1;
  tmgi->oddeven = (*(buffer + decoded) >> 3) & 0x1;
  tmgi->typeofidentity = *(buffer + decoded) & 0x7;

  if (tmgi->typeofidentity != MOBILE_IDENTITY_TMGI) {
    return (TLV_ENCODE_VALUE_DOESNT_MATCH);
  }

  decoded++;
  //IES_DECODE_U24(tmgi->mbmsserviceid, *(buffer + decoded));
  IES_DECODE_U24(buffer, decoded, tmgi->mbmsserviceid);
  tmgi->mccdigit2 = (*(buffer + decoded) >> 4) & 0xf;
  tmgi->mccdigit1 = *(buffer + decoded) & 0xf;
  decoded++;
  tmgi->mncdigit3 = (*(buffer + decoded) >> 4) & 0xf;
  tmgi->mccdigit3 = *(buffer + decoded) & 0xf;
  decoded++;
  tmgi->mncdigit2 = (*(buffer + decoded) >> 4) & 0xf;
  tmgi->mncdigit1 = *(buffer + decoded) & 0xf;
  decoded++;
  tmgi->mbmssessionid = *(buffer + decoded);
  decoded++;
  return decoded;
}

static int decode_no_mobile_identity(NoMobileIdentity_t *no_id, uint8_t *buffer)
{
  int decoded = 0;
  no_id->typeofidentity = *(buffer + decoded) & 0x7;

  if (no_id->typeofidentity != MOBILE_IDENTITY_NOT_AVAILABLE) {
    return (TLV_ENCODE_VALUE_DOESNT_MATCH);
  }

  no_id->oddeven = (*(buffer + decoded) >> 3) & 0x1;
  no_id->digit1 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  no_id->digit2 = *(buffer + decoded) & 0xf;
  no_id->digit3 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  no_id->digit4 = *(buffer + decoded) & 0xf;
  no_id->digit5 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  no_id->digit6 = *(buffer + decoded) & 0xf;
  no_id->digit7 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  no_id->digit8 = *(buffer + decoded) & 0xf;
  no_id->digit9 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  no_id->digit10 = *(buffer + decoded) & 0xf;
  no_id->digit11 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  no_id->digit12 = *(buffer + decoded) & 0xf;
  no_id->digit13 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  no_id->digit14 = *(buffer + decoded) & 0xf;
  no_id->digit15 = (*(buffer + decoded) >> 4) & 0xf;
  decoded++;
  return decoded;
}

static int encode_imsi_mobile_identity(ImsiMobileIdentity_t *imsi, uint8_t *buffer)
{
  uint32_t encoded = 0;
  *(buffer + encoded) = 0x00 | (imsi->digit1 << 4) | (imsi->oddeven << 3) |
                        (imsi->typeofidentity);
  encoded++;
  *(buffer + encoded) = 0x00 | (imsi->digit3 << 4) | imsi->digit2;
  encoded++;
  *(buffer + encoded) = 0x00 | (imsi->digit5 << 4) | imsi->digit4;
  encoded++;
  *(buffer + encoded) = 0x00 | (imsi->digit7 << 4) | imsi->digit6;
  encoded++;
  *(buffer + encoded) = 0x00 | (imsi->digit9 << 4) | imsi->digit8;
  encoded++;
  *(buffer + encoded) = 0x00 | (imsi->digit11 << 4) | imsi->digit10;
  encoded++;
  *(buffer + encoded) = 0x00 | (imsi->digit13 << 4) | imsi->digit12;
  encoded++;

  if (imsi->oddeven != MOBILE_IDENTITY_EVEN) {
    *(buffer + encoded) = 0x00 | (imsi->digit15 << 4) | imsi->digit14;
  } else {
    *(buffer + encoded) = 0xf0 | imsi->digit14;
  }

  encoded++;
  return encoded;
}

static int encode_imei_mobile_identity(ImeiMobileIdentity_t *imei, uint8_t *buffer)
{
  uint32_t encoded = 0;
  *(buffer + encoded) = 0x00 | (imei->digit1 << 4) | (imei->oddeven << 3) |
                        (imei->typeofidentity);
  encoded++;
  *(buffer + encoded) = 0x00 | (imei->digit3 << 4) | imei->digit2;
  encoded++;
  *(buffer + encoded) = 0x00 | (imei->digit5 << 4) | imei->digit4;
  encoded++;
  *(buffer + encoded) = 0x00 | (imei->digit7 << 4) | imei->digit6;
  encoded++;
  *(buffer + encoded) = 0x00 | (imei->digit9 << 4) | imei->digit8;
  encoded++;
  *(buffer + encoded) = 0x00 | (imei->digit11 << 4) | imei->digit10;
  encoded++;
  *(buffer + encoded) = 0x00 | (imei->digit13 << 4) | imei->digit12;
  encoded++;

  if (imei->oddeven != MOBILE_IDENTITY_EVEN) {
    *(buffer + encoded) = 0x00 | (imei->digit15 << 4) | imei->digit14;
  } else {
    *(buffer + encoded) = 0xf0 | imei->digit14;
  }

  encoded++;
  return encoded;
}

static int encode_imeisv_mobile_identity(ImeisvMobileIdentity_t *imeisv, uint8_t *buffer)
{
  uint32_t encoded = 0;
  *(buffer + encoded) = 0x00 | (imeisv->digit1 << 4) | (imeisv->oddeven << 3) |
                        (imeisv->typeofidentity);
  encoded++;
  *(buffer + encoded) = 0x00 | (imeisv->digit3 << 4) | imeisv->digit2;
  encoded++;
  *(buffer + encoded) = 0x00 | (imeisv->digit5 << 4) | imeisv->digit4;
  encoded++;
  *(buffer + encoded) = 0x00 | (imeisv->digit7 << 4) | imeisv->digit6;
  encoded++;
  *(buffer + encoded) = 0x00 | (imeisv->digit9 << 4) | imeisv->digit8;
  encoded++;
  *(buffer + encoded) = 0x00 | (imeisv->digit11 << 4) | imeisv->digit10;
  encoded++;
  *(buffer + encoded) = 0x00 | (imeisv->digit13 << 4) | imeisv->digit12;
  encoded++;

  if (imeisv->oddeven != MOBILE_IDENTITY_EVEN) {
    *(buffer + encoded) = 0x00 | (imeisv->digit15 << 4) | imeisv->digit14;
  } else {
    *(buffer + encoded) = 0xf0 | imeisv->digit14;
  }

  encoded++;
  return encoded;
}

static int encode_tmsi_mobile_identity(TmsiMobileIdentity_t *tmsi, uint8_t *buffer)
{
  uint32_t encoded = 0;
  *(buffer + encoded) = 0xf0 | (tmsi->oddeven << 3) | (tmsi->typeofidentity);
  encoded++;
  *(buffer + encoded) = 0x00 | (tmsi->digit3 << 4) | tmsi->digit2;
  encoded++;
  *(buffer + encoded) = 0x00 | (tmsi->digit5 << 4) | tmsi->digit4;
  encoded++;
  *(buffer + encoded) = 0x00 | (tmsi->digit7 << 4) | tmsi->digit6;
  encoded++;
  *(buffer + encoded) = 0x00 | (tmsi->digit9 << 4) | tmsi->digit8;
  encoded++;
  *(buffer + encoded) = 0x00 | (tmsi->digit11 << 4) | tmsi->digit10;
  encoded++;
  *(buffer + encoded) = 0x00 | (tmsi->digit13 << 4) | tmsi->digit12;
  encoded++;
  *(buffer + encoded) = 0x00 | (tmsi->digit15 << 4) | tmsi->digit14;
  encoded++;
  return encoded;
}

static int encode_tmgi_mobile_identity(TmgiMobileIdentity_t *tmgi, uint8_t *buffer)
{
  uint32_t encoded = 0;
  *(buffer + encoded) = 0x00 |
                        ((tmgi->mbmssessionidindication & 0x1) << 5) |
                        ((tmgi->mccmncindication & 0x1) << 4) |
                        ((tmgi->oddeven & 0x1) << 3) |
                        (tmgi->typeofidentity & 0x7);
  encoded++;
  IES_ENCODE_U24(buffer, encoded, tmgi->mbmsserviceid);
  *(buffer + encoded) = 0x00 | ((tmgi->mccdigit2 & 0xf) << 4) |
                        (tmgi->mccdigit1 & 0xf);
  encoded++;
  *(buffer + encoded) = 0x00 | ((tmgi->mncdigit3 & 0xf) << 4) |
                        (tmgi->mccdigit3 & 0xf);
  encoded++;
  *(buffer + encoded) = 0x00 | ((tmgi->mncdigit2 & 0xf) << 4) |
                        (tmgi->mncdigit1 & 0xf);
  encoded++;
  *(buffer + encoded) = tmgi->mbmssessionid;
  encoded++;
  return encoded;
}

static int encode_no_mobile_identity(NoMobileIdentity_t *no_id, uint8_t *buffer)
{
  uint32_t encoded = 0;
  *(buffer + encoded) = no_id->typeofidentity;
  encoded++;
  *(buffer + encoded) = 0x00;
  encoded++;
  *(buffer + encoded) = 0x00;
  encoded++;
  *(buffer + encoded) = 0x00;
  encoded++;
  *(buffer + encoded) = 0x00;
  encoded++;
  *(buffer + encoded) = 0x00;
  encoded++;
  *(buffer + encoded) = 0x00;
  encoded++;
  *(buffer + encoded) = 0x00;
  encoded++;
  return encoded;
}

