/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

#ifndef ASN1_CONVERSIONS_H_
#define ASN1_CONVERSIONS_H_

#include "BIT_STRING.h"
#include "assertions.h"

//-----------------------begin func -------------------

// Macro updates DESTINATION with configuration from ORIGIN by swapping pointers
// Old configuration is freed after completing configuration
#define UPDATE_IE(DESTINATION, ORIGIN, TYPE)     \
  do {                                           \
    TYPE *tmp = ORIGIN;                          \
    ORIGIN = DESTINATION;                        \
    DESTINATION = tmp;                           \
  } while(0);                                    \

// Same as above but swapping ASN1 elements that are not pointers
#define UPDATE_NP_IE(DESTINATION, ORIGIN, TYPE)     \
  do {                                              \
    TYPE tmp = ORIGIN;                              \
    ORIGIN = DESTINATION;                           \
    DESTINATION = tmp;                              \
  } while(0);                                       \

// Macro handles reception of SetupRelease element ORIGIN (see NR_SetupRelease.h)
// If release (NR_SetupRelease_xxx_PR_release equivalent to 1), removing structure from DESTINATION
// If setup (NR_SetupRelease_xxx_PR_setup equivalent to 2), add or modify structure in DESTINATION
// Destination is not a SetupRelease structure
#define HANDLE_SETUPRELEASE_DIRECT(DESTINATION, ORIGIN, TYPE, ASN_DEF) \
  do {                                                                 \
    if (ORIGIN->present == 1) {                                        \
      asn1cFreeStruc(ASN_DEF, DESTINATION);                            \
    }                                                                  \
    if (ORIGIN->present == 2)                                          \
      UPDATE_IE(DESTINATION, ORIGIN->choice.setup, TYPE);              \
  } while(0);                                                          \

// Macro handles reception of SetupRelease element ORIGIN (see NR_SetupRelease.h)
// If release (NR_SetupRelease_xxx_PR_release equivalent to 1), removing structure from DESTINATION
// If setup (NR_SetupRelease_xxx_PR_setup equivalent to 2), add or modify structure in DESTINATION
// Destination is a SetupRelease structure
#define HANDLE_SETUPRELEASE_IE(DESTINATION, ORIGIN, TYPE, ASN_DEF)          \
  do {                                                                      \
    if (ORIGIN->present == 1) {                                             \
      asn1cFreeStruc(ASN_DEF, DESTINATION);                                 \
    }                                                                       \
    if (ORIGIN->present == 2) {                                             \
      if (!DESTINATION)                                                     \
        DESTINATION = calloc(1, sizeof(*DESTINATION));                      \
      DESTINATION->present = ORIGIN->present;                               \
      UPDATE_IE(DESTINATION->choice.setup, ORIGIN->choice.setup, TYPE);     \
    }                                                                       \
  } while(0);                                                               \

// Macro releases entries in list TARGET if the corresponding ID is found in list SOURCE.
// Prints an error if ID not found in list.
#define RELEASE_IE_FROMLIST(SOURCE, TARGET, FIELD)                                 \
  do {                                                                             \
    for (int iI = 0; iI < SOURCE->list.count; iI++) {                              \
      long eL = *SOURCE->list.array[iI];                                           \
      int iJ;                                                                      \
      for (iJ = 0; iJ < TARGET->list.count; iJ++) {                                \
        if (eL == TARGET->list.array[iJ]->FIELD)                                   \
          break;                                                                   \
      }                                                                            \
      if (iJ == TARGET->list.count)                                                \
        asn_sequence_del(&TARGET->list, iJ, 1);                                    \
      else                                                                         \
        LOG_E(NR_MAC, "Element not present in the list, impossible to release\n"); \
    }                                                                              \
  } while (0)                                                                      \

// Macro adds or modifies entries of type TYPE in list TARGET with elements received in list SOURCE
#define ADDMOD_IE_FROMLIST(SOURCE, TARGET, FIELD, TYPE) \
  do {                                                  \
    for (int iI = 0; iI < SOURCE->list.count; iI++) {   \
      long eL = SOURCE->list.array[iI]->FIELD;          \
      int iJ;                                           \
      for (iJ = 0; iJ < TARGET->list.count; iJ++) {     \
        if (eL == TARGET->list.array[iJ]->FIELD)        \
          break;                                        \
      }                                                 \
      if (iJ == TARGET->list.count) {                   \
        TYPE *nEW = calloc(1, sizeof(*nEW));            \
        ASN_SEQUENCE_ADD(&TARGET->list, nEW);           \
      }                                                 \
      UPDATE_IE(TARGET->list.array[iJ],                 \
                SOURCE->list.array[iI],                 \
                TYPE);                                  \
    }                                                   \
  } while (0)                                           \

// Macro adds or modifies entries of type TYPE in list TARGET with elements received in list SOURCE
// Action performed by function FUNC
#define ADDMOD_IE_FROMLIST_WFUNCTION(SOURCE, TARGET, FIELD, TYPE, FUNC) \
  do {                                                                  \
    for (int iI = 0; iI < SOURCE->list.count; iI++) {                   \
      long eL = SOURCE->list.array[iI]->FIELD;                          \
      int iJ;                                                           \
      for (iJ = 0; iJ < TARGET->list.count; iJ++) {                     \
        if (eL == TARGET->list.array[iJ]->FIELD)                        \
          break;                                                        \
      }                                                                 \
      if (iJ == TARGET->list.count) {                                   \
        TYPE *nEW = calloc(1, sizeof(*nEW));                            \
        ASN_SEQUENCE_ADD(&TARGET->list, nEW);                           \
      }                                                                 \
      FUNC(TARGET->list.array[iJ],                                      \
           SOURCE->list.array[iI]);                                     \
    }                                                                   \
  } while (0)

/*! \fn uint8_t BIT_STRING_to_uint8(BIT_STRING_t *)
 *\brief  This function extract at most a 8 bits value from a BIT_STRING_t object, the exact bits number depend on the BIT_STRING_t contents.
 *\param[in] pointer to the BIT_STRING_t object.
 *\return the extracted value.
 */
static inline uint8_t BIT_STRING_to_uint8(const BIT_STRING_t *asn) {
  DevCheck ((asn->size == 1), asn->size, 0, 0);

  return asn->buf[0] >> asn->bits_unused;
}

/*! \fn uint16_t BIT_STRING_to_uint16(BIT_STRING_t *)
 *\brief  This function extract at most a 16 bits value from a BIT_STRING_t object, the exact bits number depend on the BIT_STRING_t contents.
 *\param[in] pointer to the BIT_STRING_t object.
 *\return the extracted value.
 */
static inline uint16_t BIT_STRING_to_uint16(const BIT_STRING_t *asn) {
  uint16_t result = 0;
  int index = 0;

  DevCheck ((asn->size > 0) && (asn->size <= 2), asn->size, 0, 0);

  switch (asn->size) {
    case 2:
      result |= asn->buf[index++] << (8 - asn->bits_unused);

    case 1:
      result |= asn->buf[index] >> asn->bits_unused;
      break;

    default:
      break;
  }

  return result;
}

/*! \fn uint32_t BIT_STRING_to_uint32(BIT_STRING_t *)
 *\brief  This function extract at most a 32 bits value from a BIT_STRING_t object, the exact bits number depend on the BIT_STRING_t contents.
 *\param[in] pointer to the BIT_STRING_t object.
 *\return the extracted value.
 */
static inline uint32_t BIT_STRING_to_uint32(const BIT_STRING_t *asn) {
  uint32_t result = 0;
  size_t index;

  DevCheck ((asn->size > 0) && (asn->size <= 4), asn->size, 0, 0);

  int shift = ((asn->size - 1) * 8) - asn->bits_unused;
  for (index = 0; index < (asn->size - 1); index++) {
    result |= asn->buf[index] << shift;
    shift -= 8;
  }

  result |= asn->buf[index] >> asn->bits_unused;

  return result;
}

/*! \fn uint64_t BIT_STRING_to_uint64(BIT_STRING_t *)
 *\brief  This function extract at most a 64 bits value from a BIT_STRING_t object, the exact bits number depend on the BIT_STRING_t contents.
 *\param[in] pointer to the BIT_STRING_t object.
 *\return the extracted value.
 */
static inline uint64_t BIT_STRING_to_uint64(const BIT_STRING_t *asn) {
  uint64_t result = 0;
  size_t index;
  int shift;

  DevCheck ((asn->size > 0) && (asn->size <= 8), asn->size, 0, 0);

  shift = ((asn->size - 1) * 8) - asn->bits_unused;
  for (index = 0; index < (asn->size - 1); index++) {
    result |= ((uint64_t)asn->buf[index]) << shift;
    shift -= 8;
  }

  result |= ((uint64_t)asn->buf[index]) >> asn->bits_unused;

  return result;
}

#define asn1cSeqAdd(VaR, PtR) if (ASN_SEQUENCE_ADD(VaR,PtR)!=0) AssertFatal(false, "ASN.1 encoding error " #VaR "\n")
#define asn1cCallocOne(VaR, VaLue) \
  VaR = calloc(1,sizeof(*VaR)); *VaR=VaLue
#define asn1cCalloc(VaR, lOcPtr) \
  typeof(VaR) lOcPtr = VaR = calloc(1,sizeof(*VaR))
#define asn1cSequenceAdd(VaR, TyPe, lOcPtr) \
  TyPe *lOcPtr= calloc(1,sizeof(TyPe)); \
  asn1cSeqAdd(&VaR,lOcPtr)
#define asn1cFreeStruc(ASN_DEF, STRUCT) \
  do {                                  \
    ASN_STRUCT_RESET(ASN_DEF, STRUCT);  \
    free(STRUCT);                       \
    STRUCT = NULL;                      \
  } while (0)

#endif
