/*******************************************************************************

  Eurecom OpenAirInterface
  Copyright(c) 1999 - 2013 Eurecom

  This program is free software; you can redistribute it and/or modify it
  under the terms and conditions of the GNU General Public License,
  version 2, as published by the Free Software Foundation.

  This program is distributed in the hope it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  more details.

  You should have received a copy of the GNU General Public License along with
  this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.

  The full GNU General Public License is included in this distribution in
  the file called "COPYING".

  Contact Information
  Openair Admin: openair_admin@eurecom.fr
  Openair Tech : openair_tech@eurecom.fr
  Forums       : http://forums.eurecom.fr/openairinterface
  Address      : EURECOM, Campus SophiaTech, 450 Route des Chappes
                 06410 Biot FRANCE

*******************************************************************************/

#include "assertions.h"

#ifndef CONVERSIONS_H_
#define CONVERSIONS_H_

/* Endianness conversions for 16 and 32 bits integers from host to network order */
#if (BYTE_ORDER == LITTLE_ENDIAN)
# define hton_int32(x)   \
    (((x & 0x000000FF) << 24) | ((x & 0x0000FF00) << 8) |  \
    ((x & 0x00FF0000) >> 8) | ((x & 0xFF000000) >> 24))

# define hton_int16(x)   \
    (((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8)

# define ntoh_int32_buf(bUF)        \
    ((*(bUF)) << 24) | ((*((bUF) + 1)) << 16) | ((*((bUF) + 2)) << 8)   \
  | (*((bUF) + 3))
#else
# define hton_int32(x) (x)
# define hton_int16(x) (x)
#endif

#define BUFFER_TO_INT8(buf, x) (x = ((buf)[0]))

#define INT8_TO_BUFFER(x, buf) ((buf)[0] = (x))

/* Convert an integer on 16 bits to the given bUFFER */
#define INT16_TO_BUFFER(x, buf) \
do {                            \
    (buf)[0] = (x) >> 8;        \
    (buf)[1] = (x);             \
} while(0)

/* Convert an array of char containing vALUE to x */
#define BUFFER_TO_INT16(buf, x) \
do {                            \
    x = ((buf)[0] << 8)  |      \
        ((buf)[1]);             \
} while(0)

/* Convert an integer on 32 bits to the given bUFFER */
#define INT32_TO_BUFFER(x, buf) \
do {                            \
    (buf)[0] = (x) >> 24;       \
    (buf)[1] = (x) >> 16;       \
    (buf)[2] = (x) >> 8;        \
    (buf)[3] = (x);             \
} while(0)

/* Convert an array of char containing vALUE to x */
#define BUFFER_TO_INT32(buf, x) \
do {                            \
    x = ((buf)[0] << 24) |      \
        ((buf)[1] << 16) |      \
        ((buf)[2] << 8)  |      \
        ((buf)[3]);             \
} while(0)

/* Convert an integer on 32 bits to an octet string from asn1c tool */
#define INT32_TO_OCTET_STRING(x, asn)           \
do {                                            \
    (asn)->buf = calloc(4, sizeof(uint8_t));    \
    INT32_TO_BUFFER(x, ((asn)->buf));           \
    (asn)->size = 4;                            \
} while(0)

#define INT32_TO_BIT_STRING(x, asn) \
do {                                \
    INT32_TO_OCTET_STRING(x, asn);  \
    (asn)->bits_unused = 0;         \
} while(0)

#define INT16_TO_OCTET_STRING(x, asn)           \
do {                                            \
    (asn)->buf = calloc(2, sizeof(uint8_t));    \
    (asn)->size = 2;							\
    INT16_TO_BUFFER(x, (asn)->buf);             \
} while(0)

#define INT8_TO_OCTET_STRING(x, asn)            \
do {                                            \
    (asn)->buf = calloc(1, sizeof(uint8_t));    \
    (asn)->size = 1;                            \
    INT8_TO_BUFFER(x, (asn)->buf);              \
} while(0)

#define MME_CODE_TO_OCTET_STRING INT8_TO_OCTET_STRING
#define M_TMSI_TO_OCTET_STRING   INT32_TO_OCTET_STRING
#define MME_GID_TO_OCTET_STRING  INT16_TO_OCTET_STRING

#define OCTET_STRING_TO_INT8(asn, x)    \
do {                                    \
    DevCheck((asn)->size == 1, (asn)->size, 0, 0);           \
    BUFFER_TO_INT8((asn)->buf, x);    \
} while(0)

#define OCTET_STRING_TO_INT16(asn, x)   \
do {                                    \
    DevCheck((asn)->size == 2, (asn)->size, 0, 0);           \
    BUFFER_TO_INT16((asn)->buf, x);    \
} while(0)

#define OCTET_STRING_TO_INT32(asn, x)   \
do {                                    \
    DevCheck((asn)->size == 4, (asn)->size, 0, 0);           \
    BUFFER_TO_INT32((asn)->buf, x);    \
} while(0)

#define BIT_STRING_TO_INT32(asn, x)     \
do {                                    \
    DevCheck((asn)->bits_unused == 0, (asn)->bits_unused, 0, 0);    \
    OCTET_STRING_TO_INT32(asn, x);      \
} while(0)

#define BIT_STRING_TO_CELL_IDENTITY(asn, x)                         \
do {                                                                \
    DevCheck((asn)->bits_unused == 4, (asn)->bits_unused, 4, 0);    \
    x = ((asn)->buf[0] << 20) | ((asn)->buf[1] << 12) |             \
        ((asn)->buf[2] << 4) | (asn)->buf[3];                       \
} while(0)

#define MCC_HUNDREDS(vALUE) \
    ((vALUE) / 100)
/* When MNC is only composed of 2 digits, set the hundreds unit to 0xf */
#define MNC_HUNDREDS(vALUE) \
    (((vALUE) / 100) == 0 ? 15 : (vALUE) / 100)
#define MCC_MNC_DECIMAL(vALUE) \
    (((vALUE) / 10) % 10)
#define MCC_MNC_DIGIT(vALUE) \
    ((vALUE) % 10)

#define MCC_TO_BUFFER(mCC, bUFFER)      \
do {                                    \
    DevAssert(bUFFER != NULL);          \
    (bUFFER)[0] = MCC_HUNDREDS(mCC);    \
    (bUFFER)[1] = MCC_MNC_DECIMAL(mCC); \
    (bUFFER)[2] = MCC_MNC_DIGIT(mCC);   \
} while(0)

#define MCC_MNC_TO_PLMNID(mCC, mNC, oCTETsTRING)                               \
do {                                                                           \
    (oCTETsTRING)->buf = calloc(3, sizeof(uint8_t));                           \
    (oCTETsTRING)->buf[0] = (MCC_MNC_DECIMAL(mCC) << 4) | MCC_HUNDREDS(mCC);   \
    (oCTETsTRING)->buf[1] = (MNC_HUNDREDS(mNC) << 4) | MCC_MNC_DIGIT(mCC);     \
    (oCTETsTRING)->buf[2] = (MCC_MNC_DIGIT(mNC) << 4) | MCC_MNC_DECIMAL(mNC);  \
    (oCTETsTRING)->size = 3;                                                   \
} while(0)

#define MCC_MNC_TO_TBCD(mCC, mNC, tBCDsTRING)                        \
do {                                                                 \
    char _buf[3];                                                    \
    _buf[0] = (MCC_MNC_DECIMAL(mCC) << 4) | MCC_HUNDREDS(mCC);       \
    _buf[1] = (MNC_HUNDREDS(mNC) << 4) | MCC_MNC_DIGIT(mCC);         \
    _buf[2] = (MCC_MNC_DIGIT(mNC) << 4) | MCC_MNC_DECIMAL(mNC);      \
    OCTET_STRING_fromBuf(tBCDsTRING, _buf, 3);                       \
} while(0)

#define TBCD_TO_MCC_MNC(tBCDsTRING, mCC, mNC)                    \
do {                                                             \
    int mNC_hundred;                                             \
    DevAssert((tBCDsTRING)->size == 3);                          \
    mNC_hundred = (((tBCDsTRING)->buf[1] & 0xf0) >> 4);          \
    if (mNC_hundred == 0xf) mNC_hundred = 0;                     \
    mCC = (((((tBCDsTRING)->buf[0]) & 0xf0) >> 4) * 10) +        \
        ((((tBCDsTRING)->buf[0]) & 0x0f) * 100) +                \
        (((tBCDsTRING)->buf[1]) & 0x0f);                         \
    mNC = (mNC_hundred * 100) +                                  \
        ((((tBCDsTRING)->buf[2]) & 0xf0) >> 4) +                 \
        ((((tBCDsTRING)->buf[2]) & 0x0f) * 10);                  \
} while(0)

#define TBCD_TO_PLMN_T(tBCDsTRING, pLMN)                            \
do {                                                                \
    DevAssert((tBCDsTRING)->size == 3);                             \
    (pLMN)->MCCdigit2 = (((tBCDsTRING)->buf[0] & 0xf0) >> 4);       \
    (pLMN)->MCCdigit3 = ((tBCDsTRING)->buf[0] & 0x0f);              \
    (pLMN)->MCCdigit1 = (tBCDsTRING)->buf[1] & 0x0f;                \
    (pLMN)->MNCdigit3 = (((tBCDsTRING)->buf[1] & 0xf0) >> 4) == 0xF \
    ? 0 : (((tBCDsTRING)->buf[1] & 0xf0) >> 4);       \
    (pLMN)->MNCdigit2 = (((tBCDsTRING)->buf[2] & 0xf0) >> 4);       \
    (pLMN)->MNCdigit1 = ((tBCDsTRING)->buf[2] & 0x0f);              \
} while(0)

#define PLMN_T_TO_TBCD(pLMN, tBCDsTRING)                            \
do {                                                                \
    tBCDsTRING[0] = (pLMN.MCCdigit2 << 4) | pLMN.MCCdigit3;         \
    tBCDsTRING[1] = ((pLMN.MNCdigit1 == 0 ? 0xF : pLMN.MNCdigit1) << 4) \
    | pLMN.MCCdigit1;                                               \
    tBCDsTRING[2] = (pLMN.MNCdigit2 << 4) | pLMN.MNCdigit3;         \
} while(0)

#define PLMN_T_TO_MCC_MNC(pLMN, mCC, mNC)               \
do {                                                    \
    mCC = pLMN.MCCdigit3 * 100 + pLMN.MCCdigit2 * 10 + pLMN.MCCdigit1;  \
    mNC = (pLMN.MNCdigit3 == 0xF ? 0 : pLMN.MNCdigit3 * 100)            \
    + pLMN.MNCdigit2 * 10 + pLMN.MNCdigit1;  \
} while(0)

#define MACRO_ENB_ID_TO_BIT_STRING(mACRO, bITsTRING)    \
do {                                                    \
    (bITsTRING)->buf = calloc(3, sizeof(uint8_t));      \
    (bITsTRING)->buf[0] = ((mACRO) >> 12);              \
    (bITsTRING)->buf[1] = (mACRO) >> 4;                 \
    (bITsTRING)->buf[2] = (mACRO) & 0x0f;               \
    (bITsTRING)->size = 3;                              \
    (bITsTRING)->bits_unused = 4;                       \
} while(0)

#define MACRO_ENB_ID_TO_CELL_IDENTITY(mACRO, bITsTRING)    \
do {                                                    \
    (bITsTRING)->buf = calloc(4, sizeof(uint8_t));      \
    (bITsTRING)->buf[0] = 0;                            \
    (bITsTRING)->buf[1] = ((mACRO) >> 12);              \
    (bITsTRING)->buf[2] = (mACRO) >> 4;                 \
    (bITsTRING)->buf[3] = (mACRO) & 0x0f;               \
    (bITsTRING)->size = 4;                              \
    (bITsTRING)->bits_unused = 4;                       \
} while(0)

/* Used to format an uint32_t containing an ipv4 address */
#define IPV4_ADDR    "%u.%u.%u.%u"
#define IPV4_ADDR_FORMAT(aDDRESS)               \
    (uint8_t)((aDDRESS)  & 0x000000ff),         \
    (uint8_t)(((aDDRESS) & 0x0000ff00) >> 8 ),  \
    (uint8_t)(((aDDRESS) & 0x00ff0000) >> 16),  \
    (uint8_t)(((aDDRESS) & 0xff000000) >> 24)

#define IPV4_ADDR_DISPLAY_8(aDDRESS)            \
    (aDDRESS)[0], (aDDRESS)[1], (aDDRESS)[2], (aDDRESS)[3]

#define TAC_TO_ASN1 INT16_TO_OCTET_STRING
#define GTP_TEID_TO_ASN1 INT32_TO_OCTET_STRING
#define OCTET_STRING_TO_TAC OCTET_STRING_TO_INT16

inline
void hexa_to_ascii(uint8_t *from, char *to, size_t length);

int ascii_to_hex(uint8_t *dst, const char *h);

#endif /* CONVERSIONS_H_ */
