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

/*! \file flexran_agent_rrc.c
 * \brief FlexRAN agent Control Module RRC 
 * \author shahab SHARIAT BAGHERI
 * \date 2017
 * \version 0.1
 */

#include "flexran_agent_rrc.h"
#include "flexran_agent_ran_api.h"

#include "liblfds700.h"

#include "common/utils/LOG/log.h"

/*Trigger boolean for RRC measurement*/
bool triggered_rrc = false;

/*Array containing the Agent-RRC interfaces*/
AGENT_RRC_xface *agent_rrc_xface[NUM_MAX_ENB];


void flexran_agent_ue_state_change(mid_t mod_id, uint32_t rnti, uint8_t state_change) {
  int size;
  Protocol__FlexranMessage *msg = NULL;
  Protocol__FlexHeader *header = NULL;
  Protocol__FlexUeStateChange *ue_state_change_msg = NULL;
  Protocol__FlexUeConfig *config = NULL;
  void *data;
  int priority = 0;
  err_code_t err_code=0;

  int xid = 0;

  if (flexran_create_header(xid, PROTOCOL__FLEX_TYPE__FLPT_UE_STATE_CHANGE, &header) != 0)
    goto error;

  ue_state_change_msg = malloc(sizeof(Protocol__FlexUeStateChange));
  if(ue_state_change_msg == NULL) {
    goto error;
  }
  protocol__flex_ue_state_change__init(ue_state_change_msg);
  ue_state_change_msg->has_type = 1;
  ue_state_change_msg->type = state_change;

  config = malloc(sizeof(Protocol__FlexUeConfig));
  if (config == NULL) {
    goto error;
  }
  protocol__flex_ue_config__init(config);
  switch (state_change) {
  case PROTOCOL__FLEX_UE_STATE_CHANGE_TYPE__FLUESC_DEACTIVATED:
    config->has_rnti = 1;
    config->rnti = rnti;
    break;
  case PROTOCOL__FLEX_UE_STATE_CHANGE_TYPE__FLUESC_UPDATED:
  case PROTOCOL__FLEX_UE_STATE_CHANGE_TYPE__FLUESC_ACTIVATED:
    flexran_agent_fill_rrc_ue_config(mod_id, rnti, config);
    /* we don't call into the MAC CM here; it will be called later through an
     * ue_config_request */
    break;
  case PROTOCOL__FLEX_UE_STATE_CHANGE_TYPE__FLUESC_MOVED:
  default:
    LOG_E(FLEXRAN_AGENT, "state change FLUESC_MOVED or unknown state occured for RNTI %x\n",
          rnti);
  }

  ue_state_change_msg->config = config;
  msg = malloc(sizeof(Protocol__FlexranMessage));
  if (msg == NULL) {
    goto error;
  }
  protocol__flexran_message__init(msg);
  msg->msg_case = PROTOCOL__FLEXRAN_MESSAGE__MSG_UE_STATE_CHANGE_MSG;
  msg->msg_dir = PROTOCOL__FLEXRAN_DIRECTION__INITIATING_MESSAGE;
  msg->ue_state_change_msg = ue_state_change_msg;

  data = flexran_agent_pack_message(msg, &size);
  /*Send sr info using the MAC channel of the eNB*/
  if (flexran_agent_msg_send(mod_id, FLEXRAN_AGENT_DEFAULT, data, size, priority)) {
    err_code = PROTOCOL__FLEXRAN_ERR__MSG_ENQUEUING;
    goto error;
  }

  LOG_D(FLEXRAN_AGENT,"sent message with size %d\n", size);
  free(header);
  return;
 error:
  if (err_code != 0)
     LOG_E(FLEXRAN_AGENT, "Could not send UE state message becasue of %d for RNTI %x\n",
           err_code, rnti);
  if (header){
    free(header);
  }
  if (ue_state_change_msg) {
    free(ue_state_change_msg);
  }
  if (config) {
    free(config);
  }
  if (msg) {
    free(msg);
  }
}


int flexran_agent_destroy_ue_state_change(Protocol__FlexranMessage *msg) {
  if(msg->msg_case != PROTOCOL__FLEXRAN_MESSAGE__MSG_UE_STATE_CHANGE_MSG)
    goto error;
  free(msg->ue_state_change_msg->header);
  if (msg->ue_state_change_msg->config->capabilities)
    free(msg->ue_state_change_msg->config->capabilities);
  if (msg->ue_state_change_msg->config->info) {
    if (msg->ue_state_change_msg->config->info->cell_individual_offset) {
      free(msg->ue_state_change_msg->config->info->cell_individual_offset);
    }
    if (msg->ue_state_change_msg->config->info->event) {
      if (msg->ue_state_change_msg->config->info->event->a3) {
        free(msg->ue_state_change_msg->config->info->event->a3);
      }
      free(msg->ue_state_change_msg->config->info->event);
    }
    free(msg->ue_state_change_msg->config->info);
  }
  free(msg->ue_state_change_msg->config);
  free(msg->ue_state_change_msg);
  free(msg);
  return 0;

 error:
  //LOG_E(MAC, "%s: an error occured\n", __FUNCTION__);
  return -1;
}

/* this is called by RRC as a part of rrc xface  . The controller previously requested  this*/ 
void flexran_trigger_rrc_measurements (mid_t mod_id, LTE_MeasResults_t*  measResults) {

  // int                   priority = 0; // Warning Preventing
  // void                  *data;
  // int                   size;
  // err_code_t             err_code = -100;
  triggered_rrc = true;

  /* TODO do we need this at the current state? meas_stats is never put into a
   * protobuf message?!
  int num = flexran_get_rrc_num_ues (mod_id);
  rnti_t rntis[num];
  flexran_get_rrc_rnti_list(mod_id, rntis, num);

  meas_stats = malloc(sizeof(rrc_meas_stats) * num); 

  for (int i = 0; i < num; i++){
    const rnti_t rnti = rntis[i];
    meas_stats[i].rnti = rnti;
    meas_stats[i].meas_id = flexran_get_rrc_pcell_measid(mod_id, rnti);
    meas_stats[i].rsrp =  flexran_get_rrc_pcell_rsrp(mod_id, rnti) - 140;
    // measResults->measResultPCell.rsrpResult - 140;
    meas_stats[i].rsrq =  flexran_get_rrc_pcell_rsrq(mod_id, rnti)/2 - 20;
  */
    // repl->neigh_meas = NULL;

  // if (meas->measResultNeighCells != NULL) {
  //   /*
  //   * Neighboring cells measurements performed by UE.
  //   */
  //   NeighCellsMeasurements *neigh_meas;
  //   neigh_meas = malloc(sizeof(NeighCellsMeasurements));
  //   if (neigh_meas == NULL)
  //     goto error;
  //   neigh_cells_measurements__init(neigh_meas);

  //   /* EUTRAN RRC Measurements. */
  //   if (meas->measResultNeighCells->present ==
  //         MeasResults__measResultNeighCells_PR_measResultListEUTRA) {

  //     MeasResultListEUTRA_t meas_list = meas->measResultNeighCells->
  //                         choice.measResultListEUTRA;
  //     /* Set the number of EUTRAN measurements present in report. */
  //     neigh_meas->n_eutra_meas = meas_list.list.count;
  //     if (neigh_meas->n_eutra_meas > 0) {
  //       /* Initialize EUTRAN measurements. */
  //       EUTRAMeasurements **eutra_meas;
  //       eutra_meas = malloc(sizeof(EUTRAMeasurements *) *
  //                         neigh_meas->n_eutra_meas);
  //       for (i = 0; i < neigh_meas->n_eutra_meas; i++) {
  //         eutra_meas[i] = malloc(sizeof(EUTRAMeasurements));
  //         eutra_measurements__init(eutra_meas[i]);
  //         /* Fill in the physical cell identifier. */
  //         eutra_meas[i]->has_phys_cell_id = 1;
  //         eutra_meas[i]->phys_cell_id = meas_list.list.array[i]->
  //                                 physCellId;
  //         // log_i(agent,"PCI of Target %d", eutra_meas[i]->phys_cell_id);
  //         /* Check for Reference signal measurements. */
  //         if (&(meas_list.list.array[i]->measResult)) {
  //           /* Initialize Ref. signal measurements. */
  //           EUTRARefSignalMeas *meas_result;
  //           meas_result = malloc(sizeof(EUTRARefSignalMeas));
  //           eutra_ref_signal_meas__init(meas_result);

  //           if (meas_list.list.array[i]->measResult.rsrpResult) {
  //             meas_result->has_rsrp = 1;
  //             meas_result->rsrp = RSRP_meas_mapping[*(meas_list.
  //                   list.array[i]->measResult.rsrpResult)];
  //             // log_i(agent,"RSRP of Target %d", meas_result->rsrp);
  //           }

  //           if (meas_list.list.array[i]->measResult.rsrqResult) {
  //             meas_result->has_rsrq = 1;
  //             meas_result->rsrq = RSRQ_meas_mapping[*(meas_list.
  //                   list.array[i]->measResult.rsrqResult)];
  //             // log_i(agent,"RSRQ of Target %d", meas_result->rsrq);
  //           }
  //           eutra_meas[i]->meas_result = meas_result;
  //         }
  //         /* Check for CGI measurements. */
  //         if (meas_list.list.array[i]->cgi_Info) {
  //           /* Initialize CGI measurements. */
  //           EUTRACgiMeasurements *cgi_meas;
  //           cgi_meas = malloc(sizeof(EUTRACgiMeasurements));
  //           eutra_cgi_measurements__init(cgi_meas);

  //           /* EUTRA Cell Global Identity (CGI). */
  //           CellGlobalIdEUTRA *cgi;
  //           cgi = malloc(sizeof(CellGlobalIdEUTRA));
  //           cell_global_id__eutra__init(cgi);

  //           cgi->has_cell_id = 1;
  //           CellIdentity_t cId = meas_list.list.array[i]->
  //                     cgi_Info->cellGlobalId.cellIdentity;
  //           cgi->cell_id = (cId.buf[0] << 20) + (cId.buf[1] << 12) +
  //                   (cId.buf[2] << 4) + (cId.buf[3] >> 4);

  //           /* Public land mobile network identifier of neighbor
  //            * cell.
  //            */
  //           PlmnIdentity *plmn_id;
  //           plmn_id = malloc(sizeof(PlmnIdentity));
  //           plmn_identity__init(plmn_id);

  //           MNC_t mnc = meas_list.list.array[i]->
  //                 cgi_Info->cellGlobalId.plmn_Identity.mnc;

  //           plmn_id->has_mnc = 1;
  //           plmn_id->mnc = 0;
  //           for (m = 0; m < mnc.list.count; m++) {
  //             plmn_id->mnc += *mnc.list.array[m] *
  //               ((uint32_t) pow(10, mnc.list.count - m - 1));
  //           }

  //           MCC_t *mcc = meas_list.list.array[i]->
  //                 cgi_Info->cellGlobalId.plmn_Identity.mcc;

  //           plmn_id->has_mcc = 1;
  //           plmn_id->mcc = 0;
  //           for (m = 0; m < mcc->list.count; m++) {
  //             plmn_id->mcc += *mcc->list.array[m] *
  //               ((uint32_t) pow(10, mcc->list.count - m - 1));
  //           }

  //           TrackingAreaCode_t tac = meas_list.list.array[i]->
  //                         cgi_Info->trackingAreaCode;

  //           cgi_meas->has_tracking_area_code = 1;
  //           cgi_meas->tracking_area_code = (tac.buf[0] << 8) +
  //                               (tac.buf[1]);

  //           PLMN_IdentityList2_t *plmn_l = meas_list.list.array[i]->
  //                         cgi_Info->plmn_IdentityList;

  //           cgi_meas->n_plmn_id = plmn_l->list.count;
  //           /* Set the PLMN ID list in CGI measurements. */
  //           PlmnIdentity **plmn_id_l;
  //           plmn_id_l = malloc(sizeof(PlmnIdentity *) *
  //                           cgi_meas->n_plmn_id);

  //           MNC_t mnc2;
  //           MCC_t *mcc2;
  //           for (m = 0; m < cgi_meas->n_plmn_id; m++) {
  //             plmn_id_l[m] = malloc(sizeof(PlmnIdentity));
  //             plmn_identity__init(plmn_id_l[m]);

  //             mnc2 = plmn_l->list.array[m]->mnc;
  //             plmn_id_l[m]->has_mnc = 1;
  //             plmn_id_l[m]->mnc = 0;
  //             for (k = 0; k < mnc2.list.count; k++) {
  //               plmn_id_l[m]->mnc += *mnc2.list.array[k] *
  //               ((uint32_t) pow(10, mnc2.list.count - k - 1));
  //             }

  //             mcc2 = plmn_l->list.array[m]->mcc;
  //             plmn_id_l[m]->has_mcc = 1;
  //             plmn_id_l[m]->mcc = 0;
  //             for (k = 0; k < mcc2->list.count; k++) {
  //               plmn_id_l[m]->mcc += *mcc2->list.array[k] *
  //               ((uint32_t) pow(10, mcc2->list.count - k - 1));
  //             }
  //           }
  //           cgi_meas->plmn_id = plmn_id_l;
  //           eutra_meas[i]->cgi_meas = cgi_meas;
  //         }
  //       }
  //       neigh_meas->eutra_meas = eutra_meas;
  //     }
  //   }
  //   repl->neigh_meas = neigh_meas;
  // }
  /* Attach the RRC measurement reply message to RRC measurements message. */
  // mrrc_meas->repl = repl;
  /* Attach RRC measurement message to triggered event message. */
  // te->mrrc_meas = mrrc_meas;
  // te->has_action = 0;
  /* Attach the triggered event message to main message. */
  // reply->te = te;

  /* Send the report to controller. */
  // if (flexran_agent_msg_send(b_id, reply) < 0) {
  //   goto error;
  // }

  /* Free the measurement report received from UE. */
  // ASN_STRUCT_FREE(asn_DEF_MeasResults, p->meas);
  /* Free the params. */
  // free(p);


  // stats_reply_msg->cell_report = cell_report;
    
  // stats_reply_msg->ue_report = ue_report;
  
  // msg = malloc(sizeof(Protocol__FlexranMessage));
  // if(msg == NULL)
  //   goto error;
  // protocol__flexran_message__init(msg);
  // msg->msg_case = PROTOCOL__FLEXRAN_MESSAGE__MSG_STATS_REPLY_MSG;
  // msg->msg_dir = PROTOCOL__FLEXRAN_DIRECTION__SUCCESSFUL_OUTCOME;
  // msg->stats_reply_msg = stats_reply_msg;
  
  // data = flexran_agent_pack_message(msg, &size);
  
  
  // if (flexran_agent_msg_send(mod_id, FLEXRAN_AGENT_DEFAULT, data, size, priority)) {
  
  //   err_code = PROTOCOL__FLEXRAN_ERR__MSG_ENQUEUING;
  //   goto error;
  // }
  
  //  LOG_I(FLEXRAN_AGENT,"RRC Trigger is done  \n");

  return;

  // error:

    // LOG_E(FLEXRAN_AGENT, "Could not send UE state message becasue of %d \n",err_code);
    /* Free the measurement report received from UE. */
    // ASN_STRUCT_FREE(asn_DEF_MeasResults, p->meas);
    /* Free the params. */
    // free(p);
    // return -1;
}


int flexran_agent_rrc_stats_reply(mid_t mod_id,       
                                  Protocol__FlexUeStatsReport **ue_report,
                                  int n_ue,
                                  uint32_t ue_flags) {
  if (n_ue > 0) {
    for (int i = 0; i < n_ue; i++) {
      const rnti_t rnti = ue_report[i]->rnti;
      
      /* Check flag for creation of buffer status report */
      if (ue_flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_RRC_MEASUREMENTS) {
      	
        /*Source cell EUTRA Measurements*/
        Protocol__FlexRrcMeasurements *rrc_measurements;
      	rrc_measurements = malloc(sizeof(Protocol__FlexRrcMeasurements));
      	if (rrc_measurements == NULL)
      	  goto error;
      	protocol__flex_rrc_measurements__init(rrc_measurements);
      	
        rrc_measurements->measid = flexran_get_rrc_pcell_measid(mod_id, rnti);
      	rrc_measurements->has_measid = 1;
      	
        rrc_measurements->pcell_rsrp = flexran_get_rrc_pcell_rsrp(mod_id, rnti);
      	rrc_measurements->has_pcell_rsrp = 1;
      	
        rrc_measurements->pcell_rsrq = flexran_get_rrc_pcell_rsrq(mod_id, rnti);
      	rrc_measurements->has_pcell_rsrq = 1 ;
        
        /* Neighbouring cells EUTRA Measurements*/
        Protocol__FlexNeighCellsMeasurements *neigh_meas;
        neigh_meas = malloc(sizeof(Protocol__FlexNeighCellsMeasurements));
        if (neigh_meas == NULL) {
          free(rrc_measurements);
          rrc_measurements = NULL;
          goto error;
        }
        protocol__flex_neigh_cells_measurements__init(neigh_meas);
        
        neigh_meas->n_eutra_meas = flexran_get_rrc_num_ncell(mod_id, rnti);

        Protocol__FlexEutraMeasurements **eutra_meas = NULL;

        if (neigh_meas->n_eutra_meas > 0) {
          eutra_meas = malloc(sizeof(Protocol__FlexEutraMeasurements) * neigh_meas->n_eutra_meas);
          if (eutra_meas == NULL) {
            free(neigh_meas);
            free(rrc_measurements);
            rrc_measurements = NULL;
            goto error;
          }
          
          for (int j = 0; j < neigh_meas->n_eutra_meas; j++ ) {
            eutra_meas[j] = malloc(sizeof(Protocol__FlexEutraMeasurements));
            if (eutra_meas[j] == NULL) {
              for (int k = 0 ; k < j ; k++)
                free(eutra_meas[k]);
              free(eutra_meas);
              free(neigh_meas);
              free(rrc_measurements);
              rrc_measurements = NULL;
              goto error;
            }
            protocol__flex_eutra_measurements__init(eutra_meas[j]);

            /* Fill in the physical cell identifier. */
            eutra_meas[j]->phys_cell_id = flexran_get_rrc_neigh_phy_cell_id(mod_id, rnti, j);
            eutra_meas[j]->has_phys_cell_id = 1;

            /* The following is not correctly implemented */
            //if (flexran_get_rrc_neigh_cgi(mod_id, rnti, j)) {
            //  /* Initialize CGI measurements. */
            //  Protocol__FlexEutraCgiMeasurements *cgi_meas;
            //  cgi_meas = malloc(sizeof(Protocol__FlexEutraCgiMeasurements));

            //  if (cgi_meas) {
            //    protocol__flex_eutra_cgi_measurements__init(cgi_meas);

            //    cgi_meas->tracking_area_code = flexran_get_rrc_neigh_cgi_tac(mod_id, rnti, j);
            //    cgi_meas->has_tracking_area_code = 1;

            //    /* EUTRA Cell Global Identity (CGI) */
            //    Protocol__FlexCellGlobalEutraId *cgi;
            //    cgi = malloc(sizeof(Protocol__FlexCellGlobalEutraId));

            //    if (cgi) {
            //      protocol__flex_cell_global_eutra_id__init(cgi);

            //      cgi->cell_id = flexran_get_rrc_neigh_cgi_cell_id(mod_id, rnti, j);
            //      cgi->has_cell_id = 1;

            //      /* PLMN for neighbouring cell */
            //      Protocol__FlexPlmnIdentity *plmn_id;
            //      plmn_id = malloc(sizeof(Protocol__FlexPlmnIdentity));

            //      if (plmn_id) {
            //        protocol__flex_plmn_identity__init(plmn_id);

            //        plmn_id->mcc = 0;
            //        plmn_id->n_mcc = flexran_get_rrc_neigh_cgi_num_mcc(mod_id, rnti, j);

            //        for (int m = 0; m < plmn_id->n_mcc; m++) {
            //          plmn_id->mcc += flexran_get_rrc_neigh_cgi_mcc(mod_id, rnti, j, m);
            //        }

            //        plmn_id->mnc = 0;
            //        plmn_id->n_mnc = flexran_get_rrc_neigh_cgi_num_mnc(mod_id, rnti, j);

            //        for (int m = 0; m < plmn_id->n_mnc; m++) {
            //          plmn_id->mnc += flexran_get_rrc_neigh_cgi_mnc(mod_id, rnti, j, m);
            //        }

            //        cgi->plmn_id = plmn_id;
            //      }
            //      cgi_meas->cgi = cgi;
            //    }
            //    eutra_meas[j]->cgi_meas = cgi_meas;
            //  }
            //}

            /*RSRP/RSRQ of the neighbouring cell */
            Protocol__FlexEutraRefSignalMeas *meas_result;
            meas_result = malloc(sizeof(Protocol__FlexEutraRefSignalMeas));

            if (meas_result) {
              protocol__flex_eutra_ref_signal_meas__init(meas_result);

              meas_result->rsrp = flexran_get_rrc_neigh_rsrp(mod_id, rnti, j);
              meas_result->has_rsrp = 1;

              meas_result->rsrq = flexran_get_rrc_neigh_rsrq(mod_id, rnti, j);
              meas_result->has_rsrq = 1;

              eutra_meas[j]->meas_result = meas_result;
            }
          }

          neigh_meas->eutra_meas = eutra_meas;

          rrc_measurements->neigh_meas = neigh_meas;
        } else {
           free(neigh_meas);
        }

        ue_report[i]->rrc_measurements = rrc_measurements;
        ue_report[i]->flags |= PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_RRC_MEASUREMENTS;
      }
    }
  }
  return 0;
 error:
  for (int i = 0; i < n_ue; i++) {
    if (ue_report[i]->rrc_measurements && ue_report[i]->rrc_measurements->neigh_meas != NULL) {
      for (int j = 0; j < ue_report[i]->rrc_measurements->neigh_meas->n_eutra_meas; j++) {
        free(ue_report[i]->rrc_measurements->neigh_meas->eutra_meas[j]);
      }
    free(ue_report[i]->rrc_measurements->neigh_meas);
    }
  }

  if (ue_report != NULL)
    free(ue_report);
  return -1;
}

int flexran_agent_rrc_destroy_stats_reply(Protocol__FlexStatsReply *reply)
{
  for (int i = 0; i < reply->n_ue_report; i++){
    if (reply->ue_report[i]->rrc_measurements && reply->ue_report[i]->rrc_measurements->neigh_meas) {
      for (int j = 0; j < reply->ue_report[i]->rrc_measurements->neigh_meas->n_eutra_meas; j++) {
        //if (reply->ue_report[i]->rrc_measurements->neigh_meas->eutra_meas[j]->cgi_meas) {
        //  if (reply->ue_report[i]->rrc_measurements->neigh_meas->eutra_meas[j]->cgi_meas->cgi) {
        //    if (reply->ue_report[i]->rrc_measurements->neigh_meas->eutra_meas[j]->cgi_meas->plmn_id) {
        //      free(reply->ue_report[i]->rrc_measurements->neigh_meas->eutra_meas[j]->cgi_meas->cgi->plmn_id);
        //    }
        //    free(reply->ue_report[i]->rrc_measurements->neigh_meas->eutra_meas[j]->cgi_meas->cgi);
        //  }
        //  free(reply->ue_report[i]->rrc_measurements->neigh_meas->eutra_meas[j]->cgi_meas);
        //}
        if (reply->ue_report[i]->rrc_measurements->neigh_meas->eutra_meas[j]->meas_result)  {
          free(reply->ue_report[i]->rrc_measurements->neigh_meas->eutra_meas[j]->meas_result);
        }
        free(reply->ue_report[i]->rrc_measurements->neigh_meas->eutra_meas[j]);
      }
      free(reply->ue_report[i]->rrc_measurements->neigh_meas->eutra_meas);
      free(reply->ue_report[i]->rrc_measurements->neigh_meas);
      free(reply->ue_report[i]->rrc_measurements);
    }
  }
  return 0;
}

int flexran_agent_rrc_gtp_stats_reply(mid_t mod_id,
                                      Protocol__FlexUeStatsReport **ue_report,
                                      int n_ue,
                                      uint32_t ue_flags) {
  /* This function fills the GTP part of the statistics. The necessary
   * information is, for our purposes, completely maintained in the RRC layer.
   * It would be possible to add a GTP module that handles this, though. */
  if (n_ue > 0) {
    for (int i = 0; i < n_ue; i++) {
      const rnti_t rnti = ue_report[i]->rnti;

      /* Check flag for creation of buffer status report */
      if (ue_flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_GTP_STATS) {

        /* get number of rabs for this UE */
        const int num_e_rab = flexran_agent_rrc_gtp_num_e_rab(mod_id, rnti);
        Protocol__FlexGtpStats **gtp_stats = NULL;
        if (num_e_rab > 0) {
          gtp_stats = calloc(num_e_rab, sizeof(Protocol__FlexGtpStats *));
          if (!gtp_stats) goto error;
          for (int r = 0; r < num_e_rab; ++r) {
            gtp_stats[r] = malloc(sizeof(Protocol__FlexGtpStats));
            if (!gtp_stats[r]) goto error;
            protocol__flex_gtp_stats__init(gtp_stats[r]);
            gtp_stats[r]->e_rab_id = flexran_agent_rrc_gtp_get_e_rab_id(mod_id, rnti, r);
            gtp_stats[r]->has_e_rab_id = 1;
            gtp_stats[r]->teid_enb = flexran_agent_rrc_gtp_get_teid_enb(mod_id, rnti, r);
            gtp_stats[r]->has_teid_enb = 1;
            gtp_stats[r]->addr_enb = NULL;
            gtp_stats[r]->teid_sgw = flexran_agent_rrc_gtp_get_teid_sgw(mod_id, rnti, r);
            gtp_stats[r]->has_teid_sgw = 1;
            gtp_stats[r]->addr_sgw = NULL;
          }
        }
        ue_report[i]->n_gtp_stats = num_e_rab;
        ue_report[i]->gtp_stats = gtp_stats;
        ue_report[i]->flags |= PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_GTP_STATS;
      }
    }
  }
  return 0;
error:
  for (int i = 0; i < n_ue; i++) {
    if (!ue_report[i]->gtp_stats) continue;
    for (int r = 0; r < ue_report[i]->n_gtp_stats; ++r) {
      if (ue_report[i]->gtp_stats[r]) {
        free(ue_report[i]->gtp_stats[r]);
        ue_report[i]->gtp_stats[r] = NULL;
      }
    }
    free(ue_report[i]->gtp_stats);
    ue_report[i]->gtp_stats = NULL;
  }
  return -1;
}

int flexran_agent_rrc_gtp_destroy_stats_reply(Protocol__FlexStatsReply *reply) {
  for (int i = 0; i < reply->n_ue_report; ++i) {
    if (!reply->ue_report[i]->n_gtp_stats == 0) continue;

    for (int r = 0; r < reply->ue_report[i]->n_gtp_stats; ++r) {
      //if (reply->ue_report[i]->gtp_stats[r]->addr_enb)
      //  free(reply->ue_report[i]->gtp_stats[r]->addr_enb);
      //if (reply->ue_report[i]->gtp_stats[r]->addr_sgw)
      //  free(reply->ue_report[i]->gtp_stats[r]->addr_sgw);
      free(reply->ue_report[i]->gtp_stats[r]);
    }
  }
  return 0;
}

void flexran_agent_fill_rrc_ue_config(mid_t mod_id, rnti_t rnti,
    Protocol__FlexUeConfig *ue_conf)
{
  if (ue_conf->has_rnti && ue_conf->rnti != rnti) {
    LOG_E(FLEXRAN_AGENT, "ue_config existing RNTI %x does not match RRC RNTI %x\n",
          ue_conf->rnti, rnti);
    return;
  }
  ue_conf->has_rnti = 1;
  ue_conf->rnti = rnti;
  ue_conf->imsi = flexran_get_ue_imsi(mod_id, rnti);
  ue_conf->has_imsi = 1;

  //TODO: Set the DRX configuration (optional)
  //Not supported for now, so we do not set it

  ue_conf->time_alignment_timer = flexran_get_time_alignment_timer(mod_id, rnti);
  ue_conf->has_time_alignment_timer = 1;

  ue_conf->meas_gap_config_pattern = flexran_get_meas_gap_config(mod_id, rnti);
  ue_conf->has_meas_gap_config_pattern = 1;

  if(ue_conf->meas_gap_config_pattern != PROTOCOL__FLEX_MEAS_GAP_CONFIG_PATTERN__FLMGCP_OFF) {
    ue_conf->meas_gap_config_sf_offset = flexran_get_meas_gap_config_offset(mod_id, rnti);
    ue_conf->has_meas_gap_config_sf_offset = 1;
  }

  //TODO: Set the SPS configuration (Optional)
  //Not supported for now, so we do not set it

  //TODO: Set the SR configuration (Optional)
  //We do not set it for now

  //TODO: Set the CQI configuration (Optional)
  //We do not set it for now

  ue_conf->transmission_mode = flexran_get_ue_transmission_mode(mod_id, rnti);
  ue_conf->has_transmission_mode = 1;

  Protocol__FlexUeCapabilities *c_capabilities;
  c_capabilities = malloc(sizeof(Protocol__FlexUeCapabilities));
  if (c_capabilities) {
    protocol__flex_ue_capabilities__init(c_capabilities);

    c_capabilities->has_half_duplex = 1;
    c_capabilities->half_duplex = flexran_get_half_duplex(mod_id, rnti);

    c_capabilities->has_intra_sf_hopping = 1;
    c_capabilities->intra_sf_hopping = flexran_get_intra_sf_hopping(mod_id, rnti);

    c_capabilities->has_type2_sb_1 = 1;
    c_capabilities->type2_sb_1 = flexran_get_type2_sb_1(mod_id, rnti);

    c_capabilities->has_ue_category = 1;
    c_capabilities->ue_category = flexran_get_ue_category(mod_id, rnti);

    c_capabilities->has_res_alloc_type1 = 1;
    c_capabilities->res_alloc_type1 = flexran_get_res_alloc_type1(mod_id, rnti);

    ue_conf->capabilities = c_capabilities;
  }

  ue_conf->has_ue_transmission_antenna = 1;
  ue_conf->ue_transmission_antenna = flexran_get_ue_transmission_antenna(mod_id, rnti);

  ue_conf->has_tti_bundling = 1;
  ue_conf->tti_bundling = flexran_get_tti_bundling(mod_id, rnti);

  ue_conf->has_max_harq_tx = 1;
  ue_conf->max_harq_tx = flexran_get_maxHARQ_TX(mod_id, rnti);

  ue_conf->has_beta_offset_ack_index = 1;
  ue_conf->beta_offset_ack_index = flexran_get_beta_offset_ack_index(mod_id, rnti);

  ue_conf->has_beta_offset_ri_index = 1;
  ue_conf->beta_offset_ri_index = flexran_get_beta_offset_ri_index(mod_id, rnti);

  ue_conf->has_beta_offset_cqi_index = 1;
  ue_conf->beta_offset_cqi_index = flexran_get_beta_offset_cqi_index(mod_id, rnti);

  /* assume primary carrier */
  ue_conf->has_ack_nack_simultaneous_trans = 1;
  ue_conf->ack_nack_simultaneous_trans = flexran_get_ack_nack_simultaneous_trans(mod_id,0);

  ue_conf->has_simultaneous_ack_nack_cqi = 1;
  ue_conf->simultaneous_ack_nack_cqi = flexran_get_simultaneous_ack_nack_cqi(mod_id, rnti);

  ue_conf->has_aperiodic_cqi_rep_mode = 1;
  ue_conf->aperiodic_cqi_rep_mode = flexran_get_aperiodic_cqi_rep_mode(mod_id, rnti);

  ue_conf->has_tdd_ack_nack_feedback = 1;
  ue_conf->tdd_ack_nack_feedback = flexran_get_tdd_ack_nack_feedback_mode(mod_id, rnti);

  ue_conf->has_ack_nack_repetition_factor = 1;
  ue_conf->ack_nack_repetition_factor = flexran_get_ack_nack_repetition_factor(mod_id, rnti);

  ue_conf->has_extended_bsr_size = 1;
  ue_conf->extended_bsr_size = flexran_get_extended_bsr_size(mod_id, rnti);

  Protocol__FlexMeasurementInfo *meas_info;
  meas_info = malloc(sizeof(Protocol__FlexMeasurementInfo));

  if (meas_info) {
    protocol__flex_measurement_info__init(meas_info);

    meas_info->has_offset_freq_serving = 1;
    meas_info->offset_freq_serving = flexran_get_rrc_ofp(mod_id, rnti);

    meas_info->has_offset_freq_neighbouring = 1;
    meas_info->offset_freq_neighbouring = flexran_get_rrc_ofn(mod_id, rnti);

    int num_adj_cells = flexran_get_rrc_num_adj_cells(mod_id);
    meas_info->n_cell_individual_offset = num_adj_cells + 1;

    int64_t *cell_individual_offset;
    if (num_adj_cells > 0) {
      cell_individual_offset = malloc(sizeof(int64_t)*(num_adj_cells+1));
      if (cell_individual_offset) {
        cell_individual_offset[0] = flexran_get_rrc_ocp(mod_id, rnti);
        for (int i=0; i < num_adj_cells; i++) {
          cell_individual_offset[i+1] = flexran_get_rrc_ocn(mod_id, rnti,i);
        }
        meas_info->cell_individual_offset = cell_individual_offset;
      }
    }
    else {
      cell_individual_offset = malloc(sizeof(int64_t));
      if (cell_individual_offset) {
        *cell_individual_offset = flexran_get_rrc_ocp(mod_id, rnti);
        meas_info->cell_individual_offset = cell_individual_offset;
      }
    }

    meas_info->has_filter_coefficient_rsrp = 1;
    meas_info->filter_coefficient_rsrp = flexran_get_filter_coeff_rsrp(mod_id, rnti);

    meas_info->has_filter_coefficient_rsrq = 1;
    meas_info->filter_coefficient_rsrq = flexran_get_filter_coeff_rsrq(mod_id, rnti);

    Protocol__FlexMeasurementEvent *event;
    event = malloc(sizeof(Protocol__FlexMeasurementEvent));

    if (event) {
      protocol__flex_measurement_event__init(event);
      Protocol__FlexA3Event *a3_event;
      a3_event = malloc(sizeof(Protocol__FlexA3Event));
      if (a3_event) {
        protocol__flex_a3_event__init(a3_event);
        a3_event->has_a3_offset = 1;
        a3_event->a3_offset = flexran_get_rrc_a3_event_a3_offset(mod_id, rnti);

        a3_event->has_report_on_leave = 1;
        a3_event->report_on_leave = flexran_get_rrc_a3_event_reportOnLeave(mod_id, rnti);

        a3_event->has_hysteresis = 1;
        a3_event->hysteresis = flexran_get_rrc_a3_event_hysteresis(mod_id, rnti);

        a3_event->has_time_to_trigger = 1;
        a3_event->time_to_trigger = flexran_get_rrc_a3_event_timeToTrigger(mod_id, rnti);

        a3_event->has_max_report_cells = 1;
        a3_event->max_report_cells = flexran_get_rrc_a3_event_maxReportCells(mod_id, rnti);
        event->a3 = a3_event;
      }
      meas_info->event = event;
    }
    ue_conf->info = meas_info;
  }
}

int flexran_agent_register_rrc_xface(mid_t mod_id)
{
  if (agent_rrc_xface[mod_id]) {
    LOG_E(FLEXRAN_AGENT, "RRC agent for eNB %d is already registered\n", mod_id);
    return -1;
  }
  AGENT_RRC_xface *xface = malloc(sizeof(AGENT_RRC_xface));
  if (!xface) {
    LOG_E(FLEXRAN_AGENT, "could not allocate memory for RRC agent xface %d\n", mod_id);
    return -1;
  }

//  xface->flexran_agent_send_update_rrc_stats = flexran_agent_send_update_rrc_stats;
  
  xface->flexran_agent_notify_ue_state_change = flexran_agent_ue_state_change;
  xface->flexran_trigger_rrc_measurements = flexran_trigger_rrc_measurements;

  agent_rrc_xface[mod_id] = xface;

  return 0;
}

void flexran_agent_fill_rrc_cell_config(mid_t mod_id, uint8_t cc_id,
    Protocol__FlexCellConfig *conf) {

  if (!conf->si_config) {
    conf->si_config = malloc(sizeof(Protocol__FlexSiConfig));
    if (conf->si_config)
      protocol__flex_si_config__init(conf->si_config);
  }

  if (conf->si_config) {
    // TODO THIS IS DU RRC
    conf->si_config->sib1_length = flexran_get_sib1_length(mod_id, cc_id);
    conf->si_config->has_sib1_length = 1;

    conf->si_config->si_window_length = (uint32_t) flexran_get_si_window_length(mod_id,  cc_id);
    conf->si_config->has_si_window_length = 1;

    conf->si_config->n_si_message = 0;

    /* Protocol__FlexSiMessage **si_message; */
    /* si_message = malloc(sizeof(Protocol__FlexSiMessage *) * si_config->n_si_message); */
    /* if(si_message == NULL) */
    /* 	goto error; */
    /* for(j = 0; j < si_config->n_si_message; j++){ */
    /* 	si_message[j] = malloc(sizeof(Protocol__FlexSiMessage)); */
    /* 	if(si_message[j] == NULL) */
    /* 	  goto error; */
    /* 	protocol__flex_si_message__init(si_message[j]); */
    /* 	//TODO: Fill in with actual value, Periodicity of SI msg in radio frames */
    /* 	si_message[j]->periodicity = 1;				//SIPeriod */
    /* 	si_message[j]->has_periodicity = 1; */
    /* 	//TODO: Fill in with actual value, rhe length of the SI message in bytes */
    /* 	si_message[j]->length = 10; */
    /* 	si_message[j]->has_length = 1; */
    /* } */
    /* if(si_config->n_si_message > 0){ */
    /* 	si_config->si_message = si_message; */
    /* } */
  }

  conf->ra_response_window_size = flexran_get_ra_ResponseWindowSize(mod_id, cc_id);
  conf->has_ra_response_window_size = 1;

  // belongs to MAC but is read in RRC
  conf->mac_contention_resolution_timer = flexran_get_mac_ContentionResolutionTimer(mod_id, cc_id);
  conf->has_mac_contention_resolution_timer = 1;

  conf->ul_pusch_power = flexran_agent_get_operating_pusch_p0 (mod_id, cc_id);
  conf->has_ul_pusch_power = 1;

  conf->n_plmn_id = flexran_get_rrc_num_plmn_ids(mod_id);
  conf->plmn_id = calloc(conf->n_plmn_id, sizeof(Protocol__FlexPlmn *));
  if (conf->plmn_id) {
    for (int i = 0; i < conf->n_plmn_id; i++) {
      conf->plmn_id[i] = malloc(sizeof(Protocol__FlexPlmn));
      if (!conf->plmn_id[i]) continue;
      protocol__flex_plmn__init(conf->plmn_id[i]);
      conf->plmn_id[i]->mcc = flexran_get_rrc_mcc(mod_id, i);
      conf->plmn_id[i]->has_mcc = 1;
      conf->plmn_id[i]->mnc = flexran_get_rrc_mnc(mod_id, i);
      conf->plmn_id[i]->has_mnc = 1;
      conf->plmn_id[i]->mnc_length = flexran_get_rrc_mnc_digit_length(mod_id, i);
      conf->plmn_id[i]->has_mnc_length = 1;
    }
  } else {
    conf->n_plmn_id = 0;
  }

  conf->x2_ho_net_control = flexran_get_x2_ho_net_control(mod_id);
  conf->has_x2_ho_net_control = 1;
}

int flexran_agent_unregister_rrc_xface(mid_t mod_id)
{
  if (!agent_rrc_xface[mod_id]) {
    LOG_E(FLEXRAN_AGENT, "RRC agent for eNB %d is not registered\n", mod_id);
    return -1;
  }
  //xface->agent_ctxt = NULL;
//  xface->flexran_agent_send_update_rrc_stats = NULL;

  agent_rrc_xface[mod_id]->flexran_agent_notify_ue_state_change = NULL;
  agent_rrc_xface[mod_id]->flexran_trigger_rrc_measurements = NULL;
  free(agent_rrc_xface[mod_id]);
  agent_rrc_xface[mod_id] = NULL;

  return 0;
}


