/*
 * 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
 */

#include "f1ap_rrc_message_transfer.h"

#include "f1ap_lib_common.h"
#include "f1ap_lib_includes.h"
#include "f1ap_messages_types.h"

#include "common/utils/assertions.h"
#include "openair3/UTILS/conversions.h"
#include "common/utils/oai_asn1.h"
#include "common/utils/utils.h"

/* ====================================
 * F1AP Initial UL RRC Message Transfer
 * ==================================== */

/**
 * @brief Initial UL RRC Message Transfer encoding
 */
F1AP_F1AP_PDU_t *encode_initial_ul_rrc_message_transfer(const f1ap_initial_ul_rrc_message_t *msg)
{
  F1AP_F1AP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu));

  /* 0. Message Type */
  pdu->present = F1AP_F1AP_PDU_PR_initiatingMessage;
  asn1cCalloc(pdu->choice.initiatingMessage, tmp);
  tmp->procedureCode = F1AP_ProcedureCode_id_InitialULRRCMessageTransfer;
  tmp->criticality = F1AP_Criticality_ignore;
  tmp->value.present = F1AP_InitiatingMessage__value_PR_InitialULRRCMessageTransfer;
  F1AP_InitialULRRCMessageTransfer_t *out = &tmp->value.choice.InitialULRRCMessageTransfer;

  /* mandatory */
  /* c1. GNB_DU_UE_F1AP_ID */
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_InitialULRRCMessageTransferIEs_t, ie1);
  ie1->id = F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID;
  ie1->criticality = F1AP_Criticality_reject;
  ie1->value.present = F1AP_InitialULRRCMessageTransferIEs__value_PR_GNB_DU_UE_F1AP_ID;
  ie1->value.choice.GNB_DU_UE_F1AP_ID = msg->gNB_DU_ue_id;

  /* mandatory */
  /* c2. NRCGI */
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_InitialULRRCMessageTransferIEs_t, ie2);
  ie2->id = F1AP_ProtocolIE_ID_id_NRCGI;
  ie2->criticality = F1AP_Criticality_reject;
  ie2->value.present = F1AP_InitialULRRCMessageTransferIEs__value_PR_NRCGI;
  MCC_MNC_TO_PLMNID(msg->plmn.mcc, msg->plmn.mnc, msg->plmn.mnc_digit_length, &ie2->value.choice.NRCGI.pLMN_Identity);
  NR_CELL_ID_TO_BIT_STRING(msg->nr_cellid, &ie2->value.choice.NRCGI.nRCellIdentity);

  /* mandatory */
  /* c3. C_RNTI */ // 16
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_InitialULRRCMessageTransferIEs_t, ie3);
  ie3->id = F1AP_ProtocolIE_ID_id_C_RNTI;
  ie3->criticality = F1AP_Criticality_reject;
  ie3->value.present = F1AP_InitialULRRCMessageTransferIEs__value_PR_C_RNTI;
  ie3->value.choice.C_RNTI = msg->crnti;

  /* mandatory */
  /* c4. RRCContainer */
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_InitialULRRCMessageTransferIEs_t, ie4);
  ie4->id = F1AP_ProtocolIE_ID_id_RRCContainer;
  ie4->criticality = F1AP_Criticality_reject;
  ie4->value.present = F1AP_InitialULRRCMessageTransferIEs__value_PR_RRCContainer;
  OCTET_STRING_fromBuf(&ie4->value.choice.RRCContainer, (const char *)msg->rrc_container, msg->rrc_container_length);

  /* optional */
  /* c5. DUtoCURRCContainer */
  if (msg->du2cu_rrc_container != NULL) {
    asn1cSequenceAdd(out->protocolIEs.list, F1AP_InitialULRRCMessageTransferIEs_t, ie5);
    ie5->id = F1AP_ProtocolIE_ID_id_DUtoCURRCContainer;
    ie5->criticality = F1AP_Criticality_reject;
    ie5->value.present = F1AP_InitialULRRCMessageTransferIEs__value_PR_DUtoCURRCContainer;
    OCTET_STRING_fromBuf(&ie5->value.choice.DUtoCURRCContainer,
                         (const char *)msg->du2cu_rrc_container,
                         msg->du2cu_rrc_container_length);
  }

  /* mandatory */
  /* c6. Transaction ID (integer value) */
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_InitialULRRCMessageTransferIEs_t, ie6);
  ie6->id = F1AP_ProtocolIE_ID_id_TransactionID;
  ie6->criticality = F1AP_Criticality_ignore;
  ie6->value.present = F1AP_InitialULRRCMessageTransferIEs__value_PR_TransactionID;
  ie6->value.choice.TransactionID = msg->transaction_id;

  return pdu;
}

/**
 * @brief Initial UL RRC Message Transfer decoding
 */
bool decode_initial_ul_rrc_message_transfer(const F1AP_F1AP_PDU_t *pdu, f1ap_initial_ul_rrc_message_t *out)
{
  DevAssert(out != NULL);
  memset(out, 0, sizeof(*out));

  F1AP_InitialULRRCMessageTransfer_t *container = &pdu->choice.initiatingMessage->value.choice.InitialULRRCMessageTransfer;
  F1AP_InitialULRRCMessageTransferIEs_t *ie;

  F1AP_LIB_FIND_IE(F1AP_InitialULRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, true);
  out->gNB_DU_ue_id = ie->value.choice.GNB_DU_UE_F1AP_ID;

  F1AP_LIB_FIND_IE(F1AP_InitialULRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_NRCGI, true);
  PLMNID_TO_MCC_MNC(&ie->value.choice.NRCGI.pLMN_Identity, out->plmn.mcc, out->plmn.mnc, out->plmn.mnc_digit_length);
  BIT_STRING_TO_NR_CELL_IDENTITY(&ie->value.choice.NRCGI.nRCellIdentity, out->nr_cellid);

  F1AP_LIB_FIND_IE(F1AP_InitialULRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_C_RNTI, true);
  out->crnti = ie->value.choice.C_RNTI;

  F1AP_LIB_FIND_IE(F1AP_InitialULRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_RRCContainer, true);
  out->rrc_container = cp_octet_string(&ie->value.choice.RRCContainer, &out->rrc_container_length);

  F1AP_LIB_FIND_IE(F1AP_InitialULRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_DUtoCURRCContainer, false);
  if (ie != NULL)
    out->du2cu_rrc_container = cp_octet_string(&ie->value.choice.DUtoCURRCContainer, &out->du2cu_rrc_container_length);

  F1AP_LIB_FIND_IE(F1AP_InitialULRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_TransactionID, true);
  out->transaction_id = ie->value.choice.TransactionID;

  return true;
}

/**
 * @brief Initial UL RRC Message Transfer deep copy
 */
f1ap_initial_ul_rrc_message_t cp_initial_ul_rrc_message_transfer(const f1ap_initial_ul_rrc_message_t *msg)
{
  uint8_t *rrc_container = calloc_or_fail(msg->rrc_container_length, sizeof(*rrc_container));
  memcpy(rrc_container, msg->rrc_container, msg->rrc_container_length);
  uint8_t *du2cu_rrc_container = NULL;
  if (msg->du2cu_rrc_container_length > 0) {
    DevAssert(msg->du2cu_rrc_container != NULL);
    du2cu_rrc_container = calloc_or_fail(msg->du2cu_rrc_container_length, sizeof(*du2cu_rrc_container));
    memcpy(du2cu_rrc_container, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length);
  }
  f1ap_initial_ul_rrc_message_t cp = {
    .gNB_DU_ue_id = msg->gNB_DU_ue_id,
    .plmn = msg->plmn,
    .nr_cellid = msg->nr_cellid,
    .crnti = msg->crnti,
    .rrc_container = rrc_container,
    .rrc_container_length = msg->rrc_container_length,
    .du2cu_rrc_container = du2cu_rrc_container,
    .du2cu_rrc_container_length = msg->du2cu_rrc_container_length,
    .transaction_id = msg->transaction_id
  };
  return cp;
}

/**
 * @brief Initial UL RRC Message Transfer equality check
 */
bool eq_initial_ul_rrc_message_transfer(const f1ap_initial_ul_rrc_message_t *a, const f1ap_initial_ul_rrc_message_t *b)
{
  _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id);
  if (!eq_f1ap_plmn(&a->plmn, &b->plmn))
    return false;
  _F1_EQ_CHECK_LONG(a->nr_cellid, b->nr_cellid);
  _F1_EQ_CHECK_INT(a->crnti, b->crnti);
  _F1_EQ_CHECK_INT(a->rrc_container_length, b->rrc_container_length);
  _F1_EQ_CHECK_LONG(a->nr_cellid, b->nr_cellid);
  if (memcmp(a->rrc_container, b->rrc_container, a->rrc_container_length) != 0)
    return false;
  _F1_EQ_CHECK_INT(a->du2cu_rrc_container_length, b->du2cu_rrc_container_length);
  if (memcmp(a->du2cu_rrc_container, b->du2cu_rrc_container, a->du2cu_rrc_container_length) != 0)
    return false;
  _F1_EQ_CHECK_INT(a->transaction_id, b->transaction_id);
  return true;
}

/**
 * @brief Initial UL RRC Message Transfer memory management
 */
void free_initial_ul_rrc_message_transfer(const f1ap_initial_ul_rrc_message_t *msg)
{
  DevAssert(msg != NULL);
  free(msg->rrc_container);
  free(msg->du2cu_rrc_container);
}

/* ====================================
 * F1AP UL RRC Message Transfer
 * ==================================== */

/**
 * @brief UL RRC Message Transfer encoding (9.2.3.3 of 3GPP TS 38.473)
 *        gNB-DU → gNB-CU
 */
F1AP_F1AP_PDU_t *encode_ul_rrc_message_transfer(const f1ap_ul_rrc_message_t *msg)
{

  F1AP_F1AP_PDU_t *pdu = calloc(1, sizeof(*pdu));
  AssertFatal(pdu != NULL, "out of memory\n");

  /* Create */
  /* 0. Message Type */
  pdu->present = F1AP_F1AP_PDU_PR_initiatingMessage;
  asn1cCalloc(pdu->choice.initiatingMessage, tmp);
  tmp->procedureCode = F1AP_ProcedureCode_id_ULRRCMessageTransfer;
  tmp->criticality = F1AP_Criticality_ignore;
  tmp->value.present = F1AP_InitiatingMessage__value_PR_ULRRCMessageTransfer;
  F1AP_ULRRCMessageTransfer_t *out = &tmp->value.choice.ULRRCMessageTransfer;
  // gNB-CU UE F1AP ID (M)
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_ULRRCMessageTransferIEs_t, ie1);
  ie1->id = F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID;
  ie1->criticality = F1AP_Criticality_reject;
  ie1->value.present = F1AP_ULRRCMessageTransferIEs__value_PR_GNB_CU_UE_F1AP_ID;
  ie1->value.choice.GNB_CU_UE_F1AP_ID = msg->gNB_CU_ue_id;
  // gNB-DU UE F1AP ID (M)
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_ULRRCMessageTransferIEs_t, ie2);
  ie2->id = F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID;
  ie2->criticality = F1AP_Criticality_reject;
  ie2->value.present = F1AP_ULRRCMessageTransferIEs__value_PR_GNB_DU_UE_F1AP_ID;
  ie2->value.choice.GNB_DU_UE_F1AP_ID = msg->gNB_DU_ue_id;
  // SRB ID (M)
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_ULRRCMessageTransferIEs_t, ie3);
  ie3->id = F1AP_ProtocolIE_ID_id_SRBID;
  ie3->criticality = F1AP_Criticality_reject;
  ie3->value.present = F1AP_ULRRCMessageTransferIEs__value_PR_SRBID;
  ie3->value.choice.SRBID = msg->srb_id;
  // RRC-Container (M)
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_ULRRCMessageTransferIEs_t, ie4);
  ie4->id = F1AP_ProtocolIE_ID_id_RRCContainer;
  ie4->criticality = F1AP_Criticality_reject;
  ie4->value.present = F1AP_ULRRCMessageTransferIEs__value_PR_RRCContainer;
  OCTET_STRING_fromBuf(&ie4->value.choice.RRCContainer, (const char *)msg->rrc_container, msg->rrc_container_length);

  return pdu;
}

/**
 * @brief UL RRC Message Transfer decoding (9.2.3.3 of 3GPP TS 38.473)
 *        gNB-DU → gNB-CU
 */
bool decode_ul_rrc_message_transfer(const F1AP_F1AP_PDU_t *pdu, f1ap_ul_rrc_message_t *out)
{
  DevAssert(out != NULL);
  memset(out, 0, sizeof(*out));
  DevAssert(pdu != NULL);

  F1AP_ULRRCMessageTransfer_t *container = &pdu->choice.initiatingMessage->value.choice.ULRRCMessageTransfer;
  F1AP_ULRRCMessageTransferIEs_t *ie;

  // gNB-CU UE F1AP ID (M)
  F1AP_LIB_FIND_IE(F1AP_ULRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID, true);
  out->gNB_CU_ue_id = ie->value.choice.GNB_CU_UE_F1AP_ID;
  // gNB-DU UE F1AP ID (M)
  F1AP_LIB_FIND_IE(F1AP_ULRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, true);
  out->gNB_DU_ue_id = ie->value.choice.GNB_DU_UE_F1AP_ID;
  // SRB ID (M)
  F1AP_LIB_FIND_IE(F1AP_ULRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_SRBID, true);
  out->srb_id = ie->value.choice.SRBID;
  // RRC-Container (M)
  F1AP_LIB_FIND_IE(F1AP_ULRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_RRCContainer, true);
  out->rrc_container = cp_octet_string(&ie->value.choice.RRCContainer, &out->rrc_container_length);

  return true;
}

/**
 * @brief UL RRC Message Transfer deep copy
 */
f1ap_ul_rrc_message_t cp_ul_rrc_message_transfer(const f1ap_ul_rrc_message_t *msg)
{
  uint8_t *rrc_container = calloc_or_fail(msg->rrc_container_length, sizeof(*rrc_container));
  memcpy(rrc_container, msg->rrc_container, msg->rrc_container_length);
  f1ap_ul_rrc_message_t cp = {
      .gNB_DU_ue_id = msg->gNB_DU_ue_id,
      .gNB_CU_ue_id = msg->gNB_CU_ue_id,
      .srb_id = msg->srb_id,
      .rrc_container = rrc_container,
      .rrc_container_length = msg->rrc_container_length,
  };
  return cp;
}

/**
 * @brief UL RRC Message Transfer equality check
 */
bool eq_ul_rrc_message_transfer(const f1ap_ul_rrc_message_t *a, const f1ap_ul_rrc_message_t *b)
{
  _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id);
  _F1_EQ_CHECK_INT(a->gNB_CU_ue_id, b->gNB_CU_ue_id);
  _F1_EQ_CHECK_INT(a->rrc_container_length, b->rrc_container_length);
  _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id);
  if (memcmp(a->rrc_container, b->rrc_container, a->rrc_container_length) != 0)
    return false;
  return true;
}

/**
 * @brief UL RRC Message Transfer memory management
 */
void free_ul_rrc_message_transfer(const f1ap_ul_rrc_message_t *msg)
{
  DevAssert(msg != NULL);
  free(msg->rrc_container);
}

/* ============================
 * F1AP DL RRC Message Transfer
 * ============================ */

/**
 * @brief DL RRC Message Transfer encoding (9.2.3.2 of 3GPP TS 38.473)
 *        gNB-CU → gNB-DU
 */
F1AP_F1AP_PDU_t *encode_dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *msg)
{
  F1AP_F1AP_PDU_t *pdu = calloc(1, sizeof(*pdu));
  AssertFatal(pdu != NULL, "out of memory\n");

  /* Create */
  /* 0. Message Type */
  pdu->present = F1AP_F1AP_PDU_PR_initiatingMessage;
  asn1cCalloc(pdu->choice.initiatingMessage, tmp);
  tmp->procedureCode = F1AP_ProcedureCode_id_DLRRCMessageTransfer;
  tmp->criticality = F1AP_Criticality_ignore;
  tmp->value.present = F1AP_InitiatingMessage__value_PR_DLRRCMessageTransfer;
  F1AP_DLRRCMessageTransfer_t *out = &tmp->value.choice.DLRRCMessageTransfer;
  // gNB-CU UE F1AP ID (M)
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_DLRRCMessageTransferIEs_t, ie1);
  ie1->id = F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID;
  ie1->criticality = F1AP_Criticality_reject;
  ie1->value.present = F1AP_DLRRCMessageTransferIEs__value_PR_GNB_CU_UE_F1AP_ID;
  ie1->value.choice.GNB_CU_UE_F1AP_ID = msg->gNB_CU_ue_id;
  // gNB-DU UE F1AP ID (M)
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_DLRRCMessageTransferIEs_t, ie2);
  ie2->id = F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID;
  ie2->criticality = F1AP_Criticality_reject;
  ie2->value.present = F1AP_DLRRCMessageTransferIEs__value_PR_GNB_DU_UE_F1AP_ID;
  ie2->value.choice.GNB_DU_UE_F1AP_ID = msg->gNB_DU_ue_id;
  // old gNB-DU UE F1AP ID (O)
  if (msg->old_gNB_DU_ue_id) {
    asn1cSequenceAdd(out->protocolIEs.list, F1AP_DLRRCMessageTransferIEs_t, ie3);
    ie3->id = F1AP_ProtocolIE_ID_id_oldgNB_DU_UE_F1AP_ID;
    ie3->criticality = F1AP_Criticality_reject;
    ie3->value.present = F1AP_DLRRCMessageTransferIEs__value_PR_GNB_DU_UE_F1AP_ID_1;
    ie3->value.choice.GNB_DU_UE_F1AP_ID_1 = *msg->old_gNB_DU_ue_id;
  }
  // SRB ID (M)
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_DLRRCMessageTransferIEs_t, ie4);
  ie4->id = F1AP_ProtocolIE_ID_id_SRBID;
  ie4->criticality = F1AP_Criticality_reject;
  ie4->value.present = F1AP_DLRRCMessageTransferIEs__value_PR_SRBID;
  ie4->value.choice.SRBID = msg->srb_id;
  // Execute Duplication (O)
  if (msg->execute_duplication) {
    asn1cSequenceAdd(out->protocolIEs.list, F1AP_DLRRCMessageTransferIEs_t, ie5);
    ie5->id = F1AP_ProtocolIE_ID_id_ExecuteDuplication;
    ie5->criticality = F1AP_Criticality_ignore;
    ie5->value.present = F1AP_DLRRCMessageTransferIEs__value_PR_ExecuteDuplication;
    ie5->value.choice.ExecuteDuplication = F1AP_ExecuteDuplication_true;
  }
  // RRC-Container (M)
  asn1cSequenceAdd(out->protocolIEs.list, F1AP_DLRRCMessageTransferIEs_t, ie6);
  ie6->id = F1AP_ProtocolIE_ID_id_RRCContainer;
  ie6->criticality = F1AP_Criticality_reject;
  ie6->value.present = F1AP_DLRRCMessageTransferIEs__value_PR_RRCContainer;
  OCTET_STRING_fromBuf(&ie6->value.choice.RRCContainer, (const char *)msg->rrc_container, msg->rrc_container_length);

  return pdu;
}

/**
 * @brief DL RRC Message Transfer decoding (9.2.3.2 of 3GPP TS 38.473)
 *        gNB-CU → gNB-DU
 */
bool decode_dl_rrc_message_transfer(const F1AP_F1AP_PDU_t *pdu, f1ap_dl_rrc_message_t *out)
{
  DevAssert(out != NULL);
  memset(out, 0, sizeof(*out));

  DevAssert(pdu != NULL);
  F1AP_DLRRCMessageTransfer_t *container = &pdu->choice.initiatingMessage->value.choice.DLRRCMessageTransfer;
  F1AP_DLRRCMessageTransferIEs_t *ie;
  // gNB-CU UE F1AP ID (M)
  F1AP_LIB_FIND_IE(F1AP_DLRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_gNB_CU_UE_F1AP_ID, true);
  out->gNB_CU_ue_id = ie->value.choice.GNB_CU_UE_F1AP_ID;
  // gNB-DU UE F1AP ID (M)
  F1AP_LIB_FIND_IE(F1AP_DLRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_gNB_DU_UE_F1AP_ID, true);
  out->gNB_DU_ue_id = ie->value.choice.GNB_DU_UE_F1AP_ID;
  // old gNB-DU UE F1AP ID (O)
  out->old_gNB_DU_ue_id = NULL;
  F1AP_LIB_FIND_IE(F1AP_DLRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_oldgNB_DU_UE_F1AP_ID, false);
  if (ie) {
    out->old_gNB_DU_ue_id = malloc_or_fail(sizeof(*out->old_gNB_DU_ue_id));
    *out->old_gNB_DU_ue_id = ie->value.choice.GNB_DU_UE_F1AP_ID_1;
  }
  // SRB ID (M)
  F1AP_LIB_FIND_IE(F1AP_DLRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_SRBID, true);
  out->srb_id = ie->value.choice.SRBID;
  // Execute Duplication (O)
  F1AP_LIB_FIND_IE(F1AP_DLRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_ExecuteDuplication, false);
  if (ie) {
    out->execute_duplication = ie->value.choice.ExecuteDuplication;
  }
  // RRC-Container (M)
  F1AP_LIB_FIND_IE(F1AP_DLRRCMessageTransferIEs_t, ie, container, F1AP_ProtocolIE_ID_id_RRCContainer, true);
  out->rrc_container = cp_octet_string(&ie->value.choice.RRCContainer, &out->rrc_container_length);

  return true;
}

/**
 * @brief DL RRC Message Transfer deep copy
 */
f1ap_dl_rrc_message_t cp_dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *msg)
{
  uint8_t *rrc_container = calloc_or_fail(msg->rrc_container_length, sizeof(*rrc_container));
  memcpy(rrc_container, msg->rrc_container, msg->rrc_container_length);

  uint32_t *old_gNB_DU_ue_id = NULL;
  if (msg->old_gNB_DU_ue_id) {
    old_gNB_DU_ue_id = malloc_or_fail(sizeof(*old_gNB_DU_ue_id));
    *old_gNB_DU_ue_id = *(msg->old_gNB_DU_ue_id);
  }

  f1ap_dl_rrc_message_t cp = {
      .gNB_DU_ue_id = msg->gNB_DU_ue_id,
      .gNB_CU_ue_id = msg->gNB_CU_ue_id,
      .old_gNB_DU_ue_id = old_gNB_DU_ue_id,
      .srb_id = msg->srb_id,
      .execute_duplication = msg->execute_duplication,
      .rrc_container = rrc_container,
      .rrc_container_length = msg->rrc_container_length,
  };

  return cp;
}

/**
 * @brief DL RRC Message Transfer equality check
 */
bool eq_dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *a, const f1ap_dl_rrc_message_t *b)
{
  _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id);
  _F1_EQ_CHECK_INT(a->gNB_CU_ue_id, b->gNB_CU_ue_id);
  _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id);
  _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id);
  _F1_EQ_CHECK_INT(a->gNB_DU_ue_id, b->gNB_DU_ue_id);
  if (!(a->old_gNB_DU_ue_id == NULL && b->old_gNB_DU_ue_id == NULL))
    _F1_EQ_CHECK_INT(*a->old_gNB_DU_ue_id, *b->old_gNB_DU_ue_id);
  _F1_EQ_CHECK_INT(a->srb_id, b->srb_id);
  _F1_EQ_CHECK_INT(a->execute_duplication, b->execute_duplication);
  _F1_EQ_CHECK_INT(a->rrc_container_length, b->rrc_container_length);
  if (memcmp(a->rrc_container, b->rrc_container, a->rrc_container_length) != 0)
    return false;
  return true;
}

/**
 * @brief DL RRC Message Transfer memory management
 */
void free_dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *msg)
{
  DevAssert(msg != NULL);
  free(msg->rrc_container);
  free(msg->old_gNB_DU_ue_id);
}
