/*
 * 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_mac.c
 * \brief FlexRAN agent message handler for MAC layer
 * \author Xenofon Foukas, Mohamed Kassem and Navid Nikaein
 * \date 2016
 * \version 0.1
 */

#include "flexran_agent_mac.h"
#include "flexran_agent_extern.h"
#include "flexran_agent_common.h"
#include "flexran_agent_mac_internal.h"
#include "flexran_agent_net_comm.h"
#include "flexran_agent_timer.h"
#include "flexran_agent_ran_api.h"

#include "LAYER2/MAC/mac_proto.h"

#include "liblfds700.h"

#include "log.h"

/*Flags showing if a mac agent has already been registered*/
unsigned int mac_agent_registered[NUM_MAX_ENB];

/*Array containing the Agent-MAC interfaces*/
AGENT_MAC_xface *agent_mac_xface[NUM_MAX_ENB];

/* Ringbuffer related structs used for maintaining the dl mac config messages */
//message_queue_t *dl_mac_config_queue;
struct lfds700_misc_prng_state ps[NUM_MAX_ENB];
struct lfds700_ringbuffer_element *dl_mac_config_array[NUM_MAX_ENB];
struct lfds700_ringbuffer_state ringbuffer_state[NUM_MAX_ENB];


int flexran_agent_mac_stats_reply(mid_t mod_id,
          const report_config_t *report_config,
           Protocol__FlexUeStatsReport **ue_report,
           Protocol__FlexCellStatsReport **cell_report) {


  // Protocol__FlexHeader *header;
  int i, j, k;
  int cc_id = 0;
  int enb_id = mod_id;

  /* Allocate memory for list of UE reports */
  if (report_config->nr_ue > 0) {


          for (i = 0; i < report_config->nr_ue; i++) {

                ue_report[i]->rnti = report_config->ue_report_type[i].ue_rnti;
                ue_report[i]->has_rnti = 1;

                /* Check flag for creation of buffer status report */
                if (report_config->ue_report_type[i].ue_report_flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_BSR) {
                      //TODO should be automated
                        ue_report[i]->n_bsr = 4;
                        uint32_t *elem;
                        elem = (uint32_t *) malloc(sizeof(uint32_t)*ue_report[i]->n_bsr);
                        if (elem == NULL)
                               goto error;
                        for (j = 0; j < ue_report[i]->n_bsr; j++) {
                                // NN: we need to know the cc_id here, consider the first one
                                elem[j] = flexran_get_ue_bsr_ul_buffer_info (enb_id, i, j);
                        }

                        ue_report[i]->bsr = elem;
                }

                /* Check flag for creation of PHR report */
                if (report_config->ue_report_type[i].ue_report_flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_PHR) {
                        ue_report[i]->phr = flexran_get_ue_phr (enb_id, i); // eNB_UE_list->UE_template[UE_PCCID(enb_id,i)][i].phr_info;
                        ue_report[i]->has_phr = 1;

                }

                /* Check flag for creation of RLC buffer status report */
                if (report_config->ue_report_type[i].ue_report_flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_RLC_BS) {
                        ue_report[i]->n_rlc_report = 3; // Set this to the number of LCs for this UE. This needs to be generalized for for LCs
                        Protocol__FlexRlcBsr ** rlc_reports;
                        rlc_reports = malloc(sizeof(Protocol__FlexRlcBsr *) * ue_report[i]->n_rlc_report);
                        if (rlc_reports == NULL)
                              goto error;

                        // NN: see LAYER2/openair2_proc.c for rlc status
                        for (j = 0; j < ue_report[i]->n_rlc_report; j++) {

                              rlc_reports[j] = malloc(sizeof(Protocol__FlexRlcBsr));
                              if (rlc_reports[j] == NULL)
                                 goto error;
                              protocol__flex_rlc_bsr__init(rlc_reports[j]);
                              rlc_reports[j]->lc_id = j+1;
                              rlc_reports[j]->has_lc_id = 1;
                              rlc_reports[j]->tx_queue_size = flexran_get_tx_queue_size(enb_id, i, j + 1);
                              rlc_reports[j]->has_tx_queue_size = 1;

                              //TODO:Set tx queue head of line delay in ms
                              rlc_reports[j]->tx_queue_hol_delay = flexran_get_hol_delay(enb_id, i, j + 1);
                              rlc_reports[j]->has_tx_queue_hol_delay = 1;
                              //TODO:Set retransmission queue size in bytes
                              rlc_reports[j]->retransmission_queue_size = 10;
                              rlc_reports[j]->has_retransmission_queue_size = 0;
                              //TODO:Set retransmission queue head of line delay in ms
                              rlc_reports[j]->retransmission_queue_hol_delay = 100;
                              rlc_reports[j]->has_retransmission_queue_hol_delay = 0;
                              //TODO DONE:Set current size of the pending message in bytes
                              rlc_reports[j]->status_pdu_size = flexran_get_num_pdus_buffer(enb_id , i, j + 1);
                              rlc_reports[j]->has_status_pdu_size = 1;

                        }
                        // Add RLC buffer status reports to the full report
                        if (ue_report[i]->n_rlc_report > 0)
                            ue_report[i]->rlc_report = rlc_reports;


                }

                /* Check flag for creation of MAC CE buffer status report */
                if (report_config->ue_report_type[i].ue_report_flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_MAC_CE_BS) {
                        // TODO: Fill in the actual MAC CE buffer status report
                        ue_report[i]->pending_mac_ces = (flexran_get_MAC_CE_bitmap_TA(enb_id,i,0) | (0 << 1) | (0 << 2) | (0 << 3)) & 15;
                                      // Use as bitmap. Set one or more of the; /* Use as bitmap. Set one or more of the
                                       // PROTOCOL__FLEX_CE_TYPE__FLPCET_ values
                                       // found in stats_common.pb-c.h. See
                                       // flex_ce_type in FlexRAN specification
                        ue_report[i]->has_pending_mac_ces = 1;

                }

                /* Check flag for creation of DL CQI report */
                if (report_config->ue_report_type[i].ue_report_flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_DL_CQI) {
                        // TODO: Fill in the actual DL CQI report for the UE based on its configuration
                        Protocol__FlexDlCqiReport * dl_report;
                        dl_report = malloc(sizeof(Protocol__FlexDlCqiReport));
                        if (dl_report == NULL)
                          goto error;
                        protocol__flex_dl_cqi_report__init(dl_report);

                        dl_report->sfn_sn = flexran_get_sfn_sf(enb_id);
                        dl_report->has_sfn_sn = 1;
                        //Set the number of DL CQI reports for this UE. One for each CC
                        dl_report->n_csi_report = flexran_get_active_CC(enb_id,i);
                        dl_report->n_csi_report = 1 ;
                        //Create the actual CSI reports.
                        Protocol__FlexDlCsi **csi_reports;
                        csi_reports = malloc(sizeof(Protocol__FlexDlCsi *)*dl_report->n_csi_report);
                        if (csi_reports == NULL)
                          goto error;
                        for (j = 0; j < dl_report->n_csi_report; j++) {

                              csi_reports[j] = malloc(sizeof(Protocol__FlexDlCsi));
                              if (csi_reports[j] == NULL)
                                goto error;
                              protocol__flex_dl_csi__init(csi_reports[j]);
                              //The servCellIndex for this report
                              csi_reports[j]->serv_cell_index = j;
                              csi_reports[j]->has_serv_cell_index = 1;
                              //The rank indicator value for this cc
                              csi_reports[j]->ri = flexran_get_current_RI(enb_id,i,j);
                              csi_reports[j]->has_ri = 1;
                              //TODO: the type of CSI report based on the configuration of the UE
                              //For now we only support type P10, which only needs a wideband value
                              //The full set of types can be found in stats_common.pb-c.h and
                              //in the FlexRAN specifications
                              csi_reports[j]->type =  PROTOCOL__FLEX_CSI_TYPE__FLCSIT_P10;
                              csi_reports[j]->has_type = 1;
                              csi_reports[j]->report_case = PROTOCOL__FLEX_DL_CSI__REPORT_P10CSI;

                              if(csi_reports[j]->report_case == PROTOCOL__FLEX_DL_CSI__REPORT_P10CSI){

                                    Protocol__FlexCsiP10 *csi10;
                                    csi10 = malloc(sizeof(Protocol__FlexCsiP10));
                                    if (csi10 == NULL)
                                    goto error;
                                    protocol__flex_csi_p10__init(csi10);
                                    //TODO: set the wideband value
                                    // NN: this is also depends on cc_id
                                    csi10->wb_cqi = flexran_get_ue_wcqi (enb_id, i); //eNB_UE_list->eNB_UE_stats[UE_PCCID(enb_id,i)][i].dl_cqi;
                                    csi10->has_wb_cqi = 1;
                                    //Add the type of measurements to the csi report in the proper union type
                                    csi_reports[j]->p10csi = csi10;
                              }

                              else if(csi_reports[j]->report_case == PROTOCOL__FLEX_DL_CSI__REPORT_P11CSI){


                                    Protocol__FlexCsiP11 *csi11;
                                    csi11 = malloc(sizeof(Protocol__FlexCsiP11));
                                    if (csi11 == NULL)
                                    goto error;
                                    protocol__flex_csi_p11__init(csi11);

                                    csi11->wb_cqi = malloc(sizeof(csi11->wb_cqi));
				    csi11->n_wb_cqi = 1;
				    csi11->wb_cqi[0] = flexran_get_ue_wcqi (enb_id, i);
                                    // According To spec 36.213

                                    if (flexran_get_antenna_ports(enb_id, j) == 2 && csi_reports[j]->ri == 1) {
                                        // TODO PMI
                                        csi11->wb_pmi = flexran_get_ue_wpmi(enb_id, i, 0);
                                        csi11->has_wb_pmi = 1;

                                       }

                                      else if (flexran_get_antenna_ports(enb_id, j) == 2 && csi_reports[j]->ri == 2){
                                        // TODO PMI
                                        csi11->wb_pmi = flexran_get_ue_wpmi(enb_id, i, 0);
                                        csi11->has_wb_pmi = 1;

                                      }

                                      else if (flexran_get_antenna_ports(enb_id, j) == 4 && csi_reports[j]->ri == 2){
                                        // TODO PMI
                                        csi11->wb_pmi = flexran_get_ue_wpmi(enb_id, i, 0);
                                        csi11->has_wb_pmi = 1;


                                      }

                                      csi11->has_wb_pmi = 0;

                                      csi_reports[j]->p11csi = csi11;

                               }





                              else if(csi_reports[j]->report_case == PROTOCOL__FLEX_DL_CSI__REPORT_P20CSI){

                                    Protocol__FlexCsiP20 *csi20;
                                    csi20 = malloc(sizeof(Protocol__FlexCsiP20));
                                    if (csi20 == NULL)
                                    goto error;
                                    protocol__flex_csi_p20__init(csi20);

                                    csi20->wb_cqi = flexran_get_ue_wcqi (enb_id, i);
                                    csi20->has_wb_cqi = 1;


                                    csi20->bandwidth_part_index = 1 ;//TODO
                                    csi20->has_bandwidth_part_index = 1;

                                    csi20->sb_index = 1 ;//TODO
                                    csi20->has_sb_index = 1 ;


                                    csi_reports[j]->p20csi = csi20;


                              }

                              else if(csi_reports[j]->report_case == PROTOCOL__FLEX_DL_CSI__REPORT_P21CSI){

                                  // Protocol__FlexCsiP21 *csi21;
                                  // csi21 = malloc(sizeof(Protocol__FlexCsiP21));
                                  // if (csi21 == NULL)
                                  // goto error;
                                  // protocol__flex_csi_p21__init(csi21);

                                  // csi21->wb_cqi = flexran_get_ue_wcqi (enb_id, i);


                                  // csi21->wb_pmi = flexran_get_ue_pmi(enb_id); //TDO inside
                                  // csi21->has_wb_pmi = 1;

                                  // csi21->sb_cqi = 1; // TODO

                                  // csi21->bandwidth_part_index = 1 ; //TDO inside
                                  // csi21->has_bandwidth_part_index = 1 ;

                                  // csi21->sb_index = 1 ;//TODO
                                  // csi21->has_sb_index = 1 ;


                                  // csi_reports[j]->p20csi = csi21;

                              }

                              else if(csi_reports[j]->report_case == PROTOCOL__FLEX_DL_CSI__REPORT_A12CSI){


                                  // Protocol__FlexCsiA12 *csi12;
                                  // csi12 = malloc(sizeof(Protocol__FlexCsiA12));
                                  // if (csi12 == NULL)
                                  // goto error;
                                  // protocol__flex_csi_a12__init(csi12);

                                  // csi12->wb_cqi = flexran_get_ue_wcqi (enb_id, i);

                                  // csi12->sb_pmi = 1 ; //TODO inside

                                  // TODO continou
                              }

                              else if(csi_reports[j]->report_case == PROTOCOL__FLEX_DL_CSI__REPORT_A22CSI){

                                    // Protocol__FlexCsiA22 *csi22;
                                    // csi22 = malloc(sizeof(Protocol__FlexCsiA22));
                                    // if (csi22 == NULL)
                                    // goto error;
                                    // protocol__flex_csi_a22__init(csi22);

                                    // csi22->wb_cqi = flexran_get_ue_wcqi (enb_id, i);

                                    // csi22->sb_cqi = 1 ; //TODO inside

                                    // csi22->wb_pmi = flexran_get_ue_wcqi (enb_id, i);
                                    // csi22->has_wb_pmi = 1;

                                    // csi22->sb_pmi = 1 ; //TODO inside
                                    // csi22->has_wb_pmi = 1;

                                    // csi22->sb_list = flexran_get_ue_wcqi (enb_id, i);


                                }

                                else if(csi_reports[j]->report_case == PROTOCOL__FLEX_DL_CSI__REPORT_A20CSI){

                                    // Protocol__FlexCsiA20 *csi20;
                                    // csi20 = malloc(sizeof(Protocol__FlexCsiA20));
                                    // if (csi20 == NULL)
                                    // goto error;
                                    // protocol__flex_csi_a20__init(csi20);

                                    // csi20->wb_cqi = flexran_get_ue_wcqi (enb_id, i);
                                    // csi20->has_wb_cqi = 1;

                                    // csi20>sb_cqi = 1 ; //TODO inside
                                    // csi20>has_sb_cqi = 1 ;

                                    // csi20->sb_list = 1; // TODO inside


                                }

                                else if(csi_reports[j]->report_case == PROTOCOL__FLEX_DL_CSI__REPORT_A30CSI){

                                }

                                else if(csi_reports[j]->report_case == PROTOCOL__FLEX_DL_CSI__REPORT_A31CSI){

                                }

                        }
                     //Add the csi reports to the full DL CQI report
                    dl_report->csi_report = csi_reports;
                    //Add the DL CQI report to the stats report
                     ue_report[i]->dl_cqi_report = dl_report;

                }

                /* Check flag for creation of paging buffer status report */
                if (report_config->ue_report_type[i].ue_report_flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_PBS) {
                            //TODO: Fill in the actual paging buffer status report. For this field to be valid, the RNTI
                            //set in the report must be a P-RNTI
                            Protocol__FlexPagingBufferReport *paging_report;
                            paging_report = malloc(sizeof(Protocol__FlexPagingBufferReport));
                            if (paging_report == NULL)
                              goto error;
                            protocol__flex_paging_buffer_report__init(paging_report);
                            //Set the number of pending paging messages
                            paging_report->n_paging_info = 1;
                            //Provide a report for each pending paging message
                            Protocol__FlexPagingInfo **p_info;
                            p_info = malloc(sizeof(Protocol__FlexPagingInfo *) * paging_report->n_paging_info);
                            if (p_info == NULL)
                              goto error;

                            for (j = 0; j < paging_report->n_paging_info; j++) {

                                    p_info[j] = malloc(sizeof(Protocol__FlexPagingInfo));
                                    if(p_info[j] == NULL)
                                      goto error;
                                    protocol__flex_paging_info__init(p_info[j]);
                                    //TODO: Set paging index. This index is the same that will be used for the scheduling of the
                                    //paging message by the controller
                                    p_info[j]->paging_index = 10;
                                    p_info[j]->has_paging_index = 1;
                                    //TODO:Set the paging message size
                                    p_info[j]->paging_message_size = 100;
                                    p_info[j]->has_paging_message_size = 1;
                                    //TODO: Set the paging subframe
                                    p_info[j]->paging_subframe = 10;
                                    p_info[j]->has_paging_subframe = 1;
                                    //TODO: Set the carrier index for the pending paging message
                                    p_info[j]->carrier_index = 0;
                                    p_info[j]->has_carrier_index = 1;

                            }
                            //Add all paging info to the paging buffer rerport
                            paging_report->paging_info = p_info;
                            //Add the paging report to the UE report
                            ue_report[i]->pbr = paging_report;
                }

                  /* Check flag for creation of UL CQI report */
                if (report_config->ue_report_type[i].ue_report_flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_UL_CQI) {

                      //Fill in the full UL CQI report of the UE
                      Protocol__FlexUlCqiReport *full_ul_report;
                      full_ul_report = malloc(sizeof(Protocol__FlexUlCqiReport));
                      if(full_ul_report == NULL)
                        goto error;
                      protocol__flex_ul_cqi_report__init(full_ul_report);
                      //TODO:Set the SFN and SF of the generated report
                      full_ul_report->sfn_sn = flexran_get_sfn_sf(enb_id);
                      full_ul_report->has_sfn_sn = 1;
                      //TODO:Set the number of UL measurement reports based on the types of measurements
                      //configured for this UE and on the servCellIndex
                      full_ul_report->n_cqi_meas = 1;
                      Protocol__FlexUlCqi **ul_report;
                      ul_report = malloc(sizeof(Protocol__FlexUlCqi *) * full_ul_report->n_cqi_meas);
                      if(ul_report == NULL)
                        goto error;
                      //Fill each UL report of the UE for each of the configured report types
                      for(j = 0; j < full_ul_report->n_cqi_meas; j++) {

                              ul_report[j] = malloc(sizeof(Protocol__FlexUlCqi));
                              if(ul_report[j] == NULL)
                              goto error;
                              protocol__flex_ul_cqi__init(ul_report[j]);
                              //TODO: Set the type of the UL report. As an example set it to SRS UL report
                              // See enum flex_ul_cqi_type in FlexRAN specification for more details
                              ul_report[j]->type = PROTOCOL__FLEX_UL_CQI_TYPE__FLUCT_SRS;
                              ul_report[j]->has_type = 1;
                              //TODO:Set the number of SINR measurements based on the report type
                              //See struct flex_ul_cqi in FlexRAN specification for more details
                              ul_report[j]->n_sinr = 0;
                              uint32_t *sinr_meas;
                              sinr_meas = (uint32_t *) malloc(sizeof(uint32_t) * ul_report[j]->n_sinr);
                              if (sinr_meas == NULL)
                                goto error;
                              //TODO:Set the SINR measurements for the specified type
                              for (k = 0; k < ul_report[j]->n_sinr; k++) {
                                      sinr_meas[k] = 10;
                              }
                              ul_report[j]->sinr = sinr_meas;
                              //TODO: Set the servCellIndex for this report
                              ul_report[j]->serv_cell_index = 0;
                              ul_report[j]->has_serv_cell_index = 1;

                              //Set the list of UL reports of this UE to the full UL report
                              full_ul_report->cqi_meas = ul_report;

                              full_ul_report->n_pucch_dbm = MAX_NUM_CCs;
                              full_ul_report->pucch_dbm = malloc(sizeof(Protocol__FlexPucchDbm *) * full_ul_report->n_pucch_dbm);

                              for (j = 0; j < MAX_NUM_CCs; j++) {

                                      full_ul_report->pucch_dbm[j] = malloc(sizeof(Protocol__FlexPucchDbm));
                                      protocol__flex_pucch_dbm__init(full_ul_report->pucch_dbm[j]);
                                      full_ul_report->pucch_dbm[j]->has_serv_cell_index = 1;
                                      full_ul_report->pucch_dbm[j]->serv_cell_index = j;

                                      if(flexran_get_p0_pucch_dbm(enb_id,i, j) != -1){
                                        full_ul_report->pucch_dbm[j]->p0_pucch_dbm = flexran_get_p0_pucch_dbm(enb_id,i,j);
                                        full_ul_report->pucch_dbm[j]->has_p0_pucch_dbm = 1;
                                      }
                              }


                          }
                        //  Add full UL CQI report to the UE report
                        ue_report[i]->ul_cqi_report = full_ul_report;


                     }
                      if (report_config->ue_report_type[i].ue_report_flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_MAC_STATS) {

                            Protocol__FlexMacStats *macstats;
                            macstats = malloc(sizeof(Protocol__FlexMacStats));
                            if (macstats == NULL)
                              goto error;
                            protocol__flex_mac_stats__init(macstats);


                            macstats->total_bytes_sdus_dl = flexran_get_total_size_dl_mac_sdus(mod_id, i, cc_id);
                            macstats->has_total_bytes_sdus_dl = 1;

                            macstats->total_bytes_sdus_ul = flexran_get_total_size_ul_mac_sdus(mod_id, i, cc_id);
                            macstats->has_total_bytes_sdus_ul = 1;

                            macstats->tbs_dl = flexran_get_TBS_dl(mod_id, i, cc_id);
                            macstats->has_tbs_dl = 1;

                            macstats->tbs_ul = flexran_get_TBS_ul(mod_id, i, cc_id);
                            macstats->has_tbs_ul = 1;

                            macstats->prb_retx_dl = flexran_get_num_prb_retx_dl_per_ue(mod_id, i, cc_id);
                            macstats->has_prb_retx_dl = 1;

                            macstats->prb_retx_ul = flexran_get_num_prb_retx_ul_per_ue(mod_id, i, cc_id);
                            macstats->has_prb_retx_ul = 1;

                            macstats->prb_dl = flexran_get_num_prb_dl_tx_per_ue(mod_id, i, cc_id);
                            macstats->has_prb_dl = 1;

                            macstats->prb_ul = flexran_get_num_prb_ul_rx_per_ue(mod_id, i, cc_id);
                            macstats->has_prb_ul = 1;

                            macstats->mcs1_dl = flexran_get_mcs1_dl(mod_id, i, cc_id);
                            macstats->has_mcs1_dl = 1;

                            macstats->mcs2_dl = flexran_get_mcs2_dl(mod_id, i, cc_id);
                            macstats->has_mcs2_dl = 1;

                            macstats->mcs1_ul = flexran_get_mcs1_ul(mod_id, i, cc_id);
                            macstats->has_mcs1_ul = 1;

                            macstats->mcs2_ul = flexran_get_mcs2_ul(mod_id, i, cc_id);
                            macstats->has_mcs2_ul = 1;

                            macstats->total_prb_dl = flexran_get_total_prb_dl_tx_per_ue(mod_id, i, cc_id);
                            macstats->has_total_prb_dl = 1;

                            macstats->total_prb_ul = flexran_get_total_prb_ul_rx_per_ue(mod_id, i, cc_id);
                            macstats->has_total_prb_ul = 1;

                            macstats->total_pdu_dl = flexran_get_total_num_pdu_dl(mod_id, i, cc_id);
                            macstats->has_total_pdu_dl = 1;

                            macstats->total_pdu_ul = flexran_get_total_num_pdu_ul(mod_id, i, cc_id);
                            macstats->has_total_pdu_ul = 1;

                            macstats->total_tbs_dl = flexran_get_total_TBS_dl(mod_id, i, cc_id);
                            macstats->has_total_tbs_dl = 1;

                            macstats->total_tbs_ul = flexran_get_total_TBS_ul(mod_id, i, cc_id);
                            macstats->has_total_tbs_ul = 1;

                            macstats->harq_round = flexran_get_harq_round(mod_id, cc_id, i);
                            macstats->has_harq_round = 1;

                            Protocol__FlexMacSdusDl ** mac_sdus;
                            mac_sdus = malloc(sizeof(Protocol__FlexMacSdusDl) * flexran_get_num_mac_sdu_tx(mod_id, i, cc_id));
                            if (mac_sdus == NULL)
                                goto error;

                            macstats->n_mac_sdus_dl = flexran_get_num_mac_sdu_tx(mod_id, i, cc_id);

                            for (j = 0; j < macstats->n_mac_sdus_dl; j++){


                                mac_sdus[j] = malloc(sizeof(Protocol__FlexMacSdusDl));
                                protocol__flex_mac_sdus_dl__init(mac_sdus[j]);

                                mac_sdus[j]->lcid = flexran_get_mac_sdu_lcid_index(mod_id, i, cc_id, j);
                                mac_sdus[j]->has_lcid = 1;

                                mac_sdus[j]->sdu_length = flexran_get_mac_sdu_size(mod_id, i, cc_id, mac_sdus[j]->lcid);
                                mac_sdus[j]->has_sdu_length = 1;


                            }


                            macstats->mac_sdus_dl = mac_sdus;


                        ue_report[i]->mac_stats = macstats;

               }




             }




     }

  /* Allocate memory for list of cell reports */
  if (report_config->nr_cc > 0) {


            // Fill in the Cell reports
            for (i = 0; i < report_config->nr_cc; i++) {


                      /* Check flag for creation of noise and interference report */
                      if(report_config->cc_report_type[i].cc_report_flags & PROTOCOL__FLEX_CELL_STATS_TYPE__FLCST_NOISE_INTERFERENCE) {
                            // TODO: Fill in the actual noise and interference report for this cell
                            Protocol__FlexNoiseInterferenceReport *ni_report;
                            ni_report = malloc(sizeof(Protocol__FlexNoiseInterferenceReport));
                            if(ni_report == NULL)
                              goto error;
                            protocol__flex_noise_interference_report__init(ni_report);
                            // Current frame and subframe number
                            ni_report->sfn_sf = flexran_get_sfn_sf(enb_id);
                            ni_report->has_sfn_sf = 1;
                            //TODO:Received interference power in dbm
                            ni_report->rip = 0;
                            ni_report->has_rip = 1;
                            //TODO:Thermal noise power in dbm
                            ni_report->tnp = 0;
                            ni_report->has_tnp = 1;

                            ni_report->p0_nominal_pucch = flexran_get_p0_nominal_pucch(enb_id, 0);
                            ni_report->has_p0_nominal_pucch = 1;
                            cell_report[i]->noise_inter_report = ni_report;
                      }
            }




  }

  return 0;

 error:

  if (cell_report != NULL)
        free(cell_report);
  if (ue_report != NULL)
        free(ue_report);

  return -1;
}

int flexran_agent_mac_destroy_stats_reply(Protocol__FlexranMessage *msg) {
  //TODO: Need to deallocate memory for the stats reply message
  if(msg->msg_case != PROTOCOL__FLEXRAN_MESSAGE__MSG_STATS_REPLY_MSG)
    goto error;
  free(msg->stats_reply_msg->header);
  int i, j, k;

  Protocol__FlexStatsReply *reply = msg->stats_reply_msg;
  Protocol__FlexDlCqiReport *dl_report;
  Protocol__FlexUlCqiReport *ul_report;
  Protocol__FlexPagingBufferReport *paging_report;

  // Free the memory for the UE reports
  for (i = 0; i < reply->n_ue_report; i++) {
    free(reply->ue_report[i]->bsr);
    for (j = 0; j < reply->ue_report[i]->n_rlc_report; j++) {
      free(reply->ue_report[i]->rlc_report[j]);
    }
    free(reply->ue_report[i]->rlc_report);
    // If DL CQI report flag was set
    if (reply->ue_report[i]->flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_DL_CQI) {
      dl_report = reply->ue_report[i]->dl_cqi_report;
      // Delete all CSI reports
      for (j = 0; j < dl_report->n_csi_report; j++) {
	//Must free memory based on the type of report
	switch(dl_report->csi_report[j]->report_case) {
	case PROTOCOL__FLEX_DL_CSI__REPORT_P10CSI:
	  free(dl_report->csi_report[j]->p10csi);
	  break;
	case PROTOCOL__FLEX_DL_CSI__REPORT_P11CSI:
	  free(dl_report->csi_report[j]->p11csi->wb_cqi);
	  free(dl_report->csi_report[j]->p11csi);
	  break;
	case PROTOCOL__FLEX_DL_CSI__REPORT_P20CSI:
	  free(dl_report->csi_report[j]->p20csi);
	  break;
	case PROTOCOL__FLEX_DL_CSI__REPORT_P21CSI:
	  free(dl_report->csi_report[j]->p21csi->wb_cqi);
	  free(dl_report->csi_report[j]->p21csi->sb_cqi);
	  free(dl_report->csi_report[j]->p21csi);
	  break;
	case PROTOCOL__FLEX_DL_CSI__REPORT_A12CSI:
	  free(dl_report->csi_report[j]->a12csi->wb_cqi);
	  free(dl_report->csi_report[j]->a12csi->sb_pmi);
	  free(dl_report->csi_report[j]->a12csi);
	  break;
	case PROTOCOL__FLEX_DL_CSI__REPORT_A22CSI:
	  free(dl_report->csi_report[j]->a22csi->wb_cqi);
	  free(dl_report->csi_report[j]->a22csi->sb_cqi);
	  free(dl_report->csi_report[j]->a22csi->sb_list);
	  free(dl_report->csi_report[j]->a22csi);
	  break;
	case PROTOCOL__FLEX_DL_CSI__REPORT_A20CSI:
	  free(dl_report->csi_report[j]->a20csi->sb_list);
	  free(dl_report->csi_report[j]->a20csi);
	  break;
	case PROTOCOL__FLEX_DL_CSI__REPORT_A30CSI:
	  free(dl_report->csi_report[j]->a30csi->sb_cqi);
	  free(dl_report->csi_report[j]->a30csi);
	  break;
	case PROTOCOL__FLEX_DL_CSI__REPORT_A31CSI:
	  free(dl_report->csi_report[j]->a31csi->wb_cqi);
	  for (k = 0; k < dl_report->csi_report[j]->a31csi->n_sb_cqi; k++) {
	    free(dl_report->csi_report[j]->a31csi->sb_cqi[k]);
	  }
	  free(dl_report->csi_report[j]->a31csi->sb_cqi);
	  break;
	default:
	  break;
	}

	free(dl_report->csi_report[j]);
      }
      free(dl_report->csi_report);
      free(dl_report);
    }
    // If Paging buffer report flag was set
    if (reply->ue_report[i]->flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_PBS) {
      paging_report = reply->ue_report[i]->pbr;
      // Delete all paging buffer reports
      for (j = 0; j < paging_report->n_paging_info; j++) {
	free(paging_report->paging_info[j]);
      }
      free(paging_report->paging_info);
      free(paging_report);
    }
    // If UL CQI report flag was set
    if (reply->ue_report[i]->flags & PROTOCOL__FLEX_UE_STATS_TYPE__FLUST_UL_CQI) {
      ul_report = reply->ue_report[i]->ul_cqi_report;
      for (j = 0; j < ul_report->n_cqi_meas; j++) {
	free(ul_report->cqi_meas[j]->sinr);
	free(ul_report->cqi_meas[j]);
      }
      free(ul_report->cqi_meas);
      for (j = 0; j < ul_report->n_pucch_dbm; j++) {
	free(ul_report->pucch_dbm[j]);
      }
      free(ul_report->pucch_dbm);
    }
    free(reply->ue_report[i]);
  }
  free(reply->ue_report);

  // Free memory for all Cell reports
  for (i = 0; i < reply->n_cell_report; i++) {
    free(reply->cell_report[i]->noise_inter_report);
    free(reply->cell_report[i]);
  }
  free(reply->cell_report);

  free(reply);
  free(msg);
  return 0;

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

int flexran_agent_mac_sr_info(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg) {
  Protocol__FlexHeader *header = NULL;
  int i;
  const int xid = *((int *)params);

  Protocol__FlexUlSrInfo *ul_sr_info_msg;
  ul_sr_info_msg = malloc(sizeof(Protocol__FlexUlSrInfo));
  if (ul_sr_info_msg == NULL) {
    goto error;
  }
  protocol__flex_ul_sr_info__init(ul_sr_info_msg);

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

  ul_sr_info_msg->header = header;
  ul_sr_info_msg->has_sfn_sf = 1;
  ul_sr_info_msg->sfn_sf = flexran_get_sfn_sf(mod_id);
  /*TODO: Set the number of UEs that sent an SR */
  ul_sr_info_msg->n_rnti = 1;
  ul_sr_info_msg->rnti = (uint32_t *) malloc(ul_sr_info_msg->n_rnti * sizeof(uint32_t));

  if(ul_sr_info_msg->rnti == NULL) {
    goto error;
  }
  /*TODO:Set the rnti of the UEs that sent an SR */
  for (i = 0; i < ul_sr_info_msg->n_rnti; i++) {
    ul_sr_info_msg->rnti[i] = 1;
  }

  *msg = malloc(sizeof(Protocol__FlexranMessage));
  if(*msg == NULL)
    goto error;
  protocol__flexran_message__init(*msg);
  (*msg)->msg_case = PROTOCOL__FLEXRAN_MESSAGE__MSG_UL_SR_INFO_MSG;
  (*msg)->msg_dir =  PROTOCOL__FLEXRAN_DIRECTION__INITIATING_MESSAGE;
  (*msg)->ul_sr_info_msg = ul_sr_info_msg;
  return 0;

 error:
  // TODO: Need to make proper error handling
  if (header != NULL)
    free(header);
  if (ul_sr_info_msg != NULL) {
    free(ul_sr_info_msg->rnti);
    free(ul_sr_info_msg);
  }
  if(*msg != NULL)
    free(*msg);
  //LOG_E(MAC, "%s: an error occured\n", __FUNCTION__);
  return -1;
}

int flexran_agent_mac_destroy_sr_info(Protocol__FlexranMessage *msg) {
   if(msg->msg_case != PROTOCOL__FLEXRAN_MESSAGE__MSG_UL_SR_INFO_MSG)
     goto error;

   free(msg->ul_sr_info_msg->header);
   free(msg->ul_sr_info_msg->rnti);
   free(msg->ul_sr_info_msg);
   free(msg);
   return 0;

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

int flexran_agent_mac_sf_trigger(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg) {
  Protocol__FlexHeader *header = NULL;
  int i, j, UE_id;

  int available_harq[MAX_MOBILES_PER_ENB];

  const int xid = *((int *)params);


  Protocol__FlexSfTrigger *sf_trigger_msg;
  sf_trigger_msg = malloc(sizeof(Protocol__FlexSfTrigger));
  if (sf_trigger_msg == NULL) {
    goto error;
  }
  protocol__flex_sf_trigger__init(sf_trigger_msg);

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

  frame_t frame;
  sub_frame_t subframe;

  for (i = 0; i < MAX_MOBILES_PER_ENB; i++) {
    available_harq[i] = -1;
  }

  int ahead_of_time = 0;

  frame = (frame_t) flexran_get_current_system_frame_num(mod_id);
  subframe = (sub_frame_t) flexran_get_current_subframe(mod_id);

  subframe = ((subframe + ahead_of_time) % 10);

  if (subframe < flexran_get_current_subframe(mod_id)) {
    frame = (frame + 1) % 1024;
  }

  int additional_frames = ahead_of_time / 10;
  frame = (frame + additional_frames) % 1024;

  sf_trigger_msg->header = header;
  sf_trigger_msg->has_sfn_sf = 1;
  sf_trigger_msg->sfn_sf = flexran_get_future_sfn_sf(mod_id, 3);

  sf_trigger_msg->n_dl_info = 0;

  for (i = 0; i < MAX_MOBILES_PER_ENB; i++) {
    for (j = 0; j < 8; j++) {
      if (RC.mac && RC.mac[mod_id] && RC.mac[mod_id]->UE_list.eNB_UE_stats[UE_PCCID(mod_id,i)][i].harq_pid == 1) {
	available_harq[i] = j;
	sf_trigger_msg->n_dl_info++;
	break;
      }
    }
  }


  //  LOG_I(FLEXRAN_AGENT, "Sending subframe trigger for frame %d and subframe %d\n", flexran_get_current_frame(mod_id), (flexran_get_current_subframe(mod_id) + 1) % 10);

  /*TODO: Fill in the number of dl HARQ related info, based on the number of currently
   *transmitting UEs
   */
  //  sf_trigger_msg->n_dl_info = flexran_get_num_ues(mod_id);

  Protocol__FlexDlInfo **dl_info = NULL;

  if (sf_trigger_msg->n_dl_info > 0) {
    dl_info = malloc(sizeof(Protocol__FlexDlInfo *) * sf_trigger_msg->n_dl_info);
    if(dl_info == NULL)
      goto error;
    i = -1;
    //Fill the status of the current HARQ process for each UE
    for(UE_id = 0; UE_id < MAX_MOBILES_PER_ENB; UE_id++) {
      if (available_harq[UE_id] < 0) {
	continue;
      } else {
	i++;
      }
      dl_info[i] = malloc(sizeof(Protocol__FlexDlInfo));
      if(dl_info[i] == NULL)
	goto error;
      protocol__flex_dl_info__init(dl_info[i]);
      dl_info[i]->rnti = flexran_get_ue_crnti(mod_id, UE_id);
      dl_info[i]->has_rnti = 1;
      /*Fill in the right id of this round's HARQ process for this UE*/
      //      uint8_t harq_id;
      //uint8_t harq_status;
      //      flexran_get_harq(mod_id, UE_PCCID(mod_id,i), i, frame, subframe, &harq_id, &harq_status);


      dl_info[i]->harq_process_id = available_harq[UE_id];
      if (RC.mac && RC.mac[mod_id])
        RC.mac[mod_id]->UE_list.eNB_UE_stats[UE_PCCID(mod_id,i)][UE_id].harq_pid = 0;
      dl_info[i]->has_harq_process_id = 1;
      /* Fill in the status of the HARQ process (2 TBs)*/
      dl_info[i]->n_harq_status = 2;
      dl_info[i]->harq_status = malloc(sizeof(uint32_t) * dl_info[i]->n_harq_status);
      for (j = 0; j < dl_info[i]->n_harq_status; j++) {
        dl_info[i]->harq_status[j] = RC.mac[mod_id]->UE_list.UE_sched_ctrl[i].round[UE_PCCID(mod_id,i)][j];
        // TODO: This should be different per TB
      }
      //      LOG_I(FLEXRAN_AGENT, "Sending subframe trigger for frame %d and subframe %d and harq %d (round %d)\n", flexran_get_current_frame(mod_id), (flexran_get_current_subframe(mod_id) + 1) % 10, dl_info[i]->harq_process_id, dl_info[i]->harq_status[0]);
      if(dl_info[i]->harq_status[0] > 0) {
	//	LOG_I(FLEXRAN_AGENT, "[Frame %d][Subframe %d]Need to make a retransmission for harq %d (round %d)\n", flexran_get_current_frame(mod_id), flexran_get_current_subframe(mod_id), dl_info[i]->harq_process_id, dl_info[i]->harq_status[0]);
      }
      /*Fill in the serving cell index for this UE */
      dl_info[i]->serv_cell_index = UE_PCCID(mod_id,i);
      dl_info[i]->has_serv_cell_index = 1;
    }
  }

  sf_trigger_msg->dl_info = dl_info;

  /* Fill in the number of UL reception status related info, based on the number of currently
   * transmitting UEs
   */
  sf_trigger_msg->n_ul_info = flexran_get_num_ues(mod_id);

  Protocol__FlexUlInfo **ul_info = NULL;

  if (sf_trigger_msg->n_ul_info > 0) {
    ul_info = malloc(sizeof(Protocol__FlexUlInfo *) * sf_trigger_msg->n_ul_info);
    if(ul_info == NULL)
      goto error;
    //Fill the reception info for each transmitting UE
    for(i = 0; i < sf_trigger_msg->n_ul_info; i++) {
      ul_info[i] = malloc(sizeof(Protocol__FlexUlInfo));
      if(ul_info[i] == NULL)
	goto error;
      protocol__flex_ul_info__init(ul_info[i]);
      ul_info[i]->rnti = flexran_get_ue_crnti(mod_id, i);
      ul_info[i]->has_rnti = 1;
      /* Fill in the Tx power control command for this UE (if available),
       * primary carrier */
      if(flexran_get_tpc(mod_id, i, 0) != 1){
          /* assume primary carrier */
          ul_info[i]->tpc = flexran_get_tpc(mod_id, i, 0);
          ul_info[i]->has_tpc = 1;
      }
      else{
          /* assume primary carrier */
          ul_info[i]->tpc = flexran_get_tpc(mod_id, i, 0);
    	  ul_info[i]->has_tpc = 0;
      }
      /*TODO: fill in the amount of data in bytes in the MAC SDU received in this subframe for the
	given logical channel*/
      ul_info[i]->n_ul_reception = 0;
      ul_info[i]->ul_reception = malloc(sizeof(uint32_t) * ul_info[i]->n_ul_reception);
      for (j = 0; j < ul_info[i]->n_ul_reception; j++) {
	ul_info[i]->ul_reception[j] = 100;
      }
      /*TODO: Fill in the reception status for each UEs data*/
      ul_info[i]->reception_status = PROTOCOL__FLEX_RECEPTION_STATUS__FLRS_OK;
      ul_info[i]->has_reception_status = 1;
      /*Fill in the serving cell index for this UE */
      ul_info[i]->serv_cell_index = UE_PCCID(mod_id,i);
      ul_info[i]->has_serv_cell_index = 1;
    }
  }

  sf_trigger_msg->ul_info = ul_info;

  *msg = malloc(sizeof(Protocol__FlexranMessage));
  if(*msg == NULL)
    goto error;
  protocol__flexran_message__init(*msg);
  (*msg)->msg_case = PROTOCOL__FLEXRAN_MESSAGE__MSG_SF_TRIGGER_MSG;
  (*msg)->msg_dir =  PROTOCOL__FLEXRAN_DIRECTION__INITIATING_MESSAGE;
  (*msg)->sf_trigger_msg = sf_trigger_msg;
  return 0;

 error:
  if (header != NULL)
    free(header);
  if (sf_trigger_msg != NULL) {
    for (i = 0; i < sf_trigger_msg->n_dl_info; i++) {
      free(sf_trigger_msg->dl_info[i]->harq_status);
    }
    free(sf_trigger_msg->dl_info);
    free(sf_trigger_msg->ul_info);
    free(sf_trigger_msg);
  }
  if(*msg != NULL)
    free(*msg);
  //LOG_E(MAC, "%s: an error occured\n", __FUNCTION__);
  return -1;
}

int flexran_agent_mac_destroy_sf_trigger(Protocol__FlexranMessage *msg) {
  int i;
  if(msg->msg_case != PROTOCOL__FLEXRAN_MESSAGE__MSG_SF_TRIGGER_MSG)
    goto error;

  free(msg->sf_trigger_msg->header);
  for (i = 0; i < msg->sf_trigger_msg->n_dl_info; i++) {
    free(msg->sf_trigger_msg->dl_info[i]->harq_status);
    free(msg->sf_trigger_msg->dl_info[i]);
  }
  free(msg->sf_trigger_msg->dl_info);
  for (i = 0; i < msg->sf_trigger_msg->n_ul_info; i++) {
    free(msg->sf_trigger_msg->ul_info[i]->ul_reception);
    free(msg->sf_trigger_msg->ul_info[i]);
  }
  free(msg->sf_trigger_msg->ul_info);
  free(msg->sf_trigger_msg);
  free(msg);

  return 0;

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

int flexran_agent_mac_create_empty_dl_config(mid_t mod_id, Protocol__FlexranMessage **msg) {

  int xid = 0;
  Protocol__FlexHeader *header = NULL;
  if (flexran_create_header(xid, PROTOCOL__FLEX_TYPE__FLPT_DL_MAC_CONFIG, &header) != 0)
    goto error;

  Protocol__FlexDlMacConfig *dl_mac_config_msg;
  dl_mac_config_msg = malloc(sizeof(Protocol__FlexDlMacConfig));
  if (dl_mac_config_msg == NULL) {
    goto error;
  }
  protocol__flex_dl_mac_config__init(dl_mac_config_msg);

  dl_mac_config_msg->header = header;
  dl_mac_config_msg->has_sfn_sf = 1;
  dl_mac_config_msg->sfn_sf = flexran_get_sfn_sf(mod_id);

  *msg = malloc(sizeof(Protocol__FlexranMessage));
  if(*msg == NULL)
    goto error;
  protocol__flexran_message__init(*msg);
  (*msg)->msg_case = PROTOCOL__FLEXRAN_MESSAGE__MSG_DL_MAC_CONFIG_MSG;
  (*msg)->msg_dir =  PROTOCOL__FLEXRAN_DIRECTION__INITIATING_MESSAGE;
  (*msg)->dl_mac_config_msg = dl_mac_config_msg;

  return 0;

 error:
  return -1;
}

int flexran_agent_mac_destroy_dl_config(Protocol__FlexranMessage *msg) {
  int i,j, k;
  if(msg->msg_case != PROTOCOL__FLEXRAN_MESSAGE__MSG_DL_MAC_CONFIG_MSG)
    goto error;

  Protocol__FlexDlDci *dl_dci;

  free(msg->dl_mac_config_msg->header);
  for (i = 0; i < msg->dl_mac_config_msg->n_dl_ue_data; i++) {
    free(msg->dl_mac_config_msg->dl_ue_data[i]->ce_bitmap);
    for (j = 0; j < msg->dl_mac_config_msg->dl_ue_data[i]->n_rlc_pdu; j++) {
      for (k = 0; k <  msg->dl_mac_config_msg->dl_ue_data[i]->rlc_pdu[j]->n_rlc_pdu_tb; k++) {
	free(msg->dl_mac_config_msg->dl_ue_data[i]->rlc_pdu[j]->rlc_pdu_tb[k]);
      }
      free(msg->dl_mac_config_msg->dl_ue_data[i]->rlc_pdu[j]->rlc_pdu_tb);
      free(msg->dl_mac_config_msg->dl_ue_data[i]->rlc_pdu[j]);
    }
    free(msg->dl_mac_config_msg->dl_ue_data[i]->rlc_pdu);
    dl_dci = msg->dl_mac_config_msg->dl_ue_data[i]->dl_dci;
    free(dl_dci->tbs_size);
    free(dl_dci->mcs);
    free(dl_dci->ndi);
    free(dl_dci->rv);
    free(dl_dci);
    free(msg->dl_mac_config_msg->dl_ue_data[i]);
  }
  free(msg->dl_mac_config_msg->dl_ue_data);

  for (i = 0; i <  msg->dl_mac_config_msg->n_dl_rar; i++) {
    dl_dci = msg->dl_mac_config_msg->dl_rar[i]->rar_dci;
    free(dl_dci->tbs_size);
    free(dl_dci->mcs);
    free(dl_dci->ndi);
    free(dl_dci->rv);
    free(dl_dci);
    free(msg->dl_mac_config_msg->dl_rar[i]);
  }
  free(msg->dl_mac_config_msg->dl_rar);

  for (i = 0; i < msg->dl_mac_config_msg->n_dl_broadcast; i++) {
    dl_dci = msg->dl_mac_config_msg->dl_broadcast[i]->broad_dci;
    free(dl_dci->tbs_size);
    free(dl_dci->mcs);
    free(dl_dci->ndi);
    free(dl_dci->rv);
    free(dl_dci);
    free(msg->dl_mac_config_msg->dl_broadcast[i]);
  }
  free(msg->dl_mac_config_msg->dl_broadcast);

    for ( i = 0; i < msg->dl_mac_config_msg->n_ofdm_sym; i++) {
    free(msg->dl_mac_config_msg->ofdm_sym[i]);
  }
  free(msg->dl_mac_config_msg->ofdm_sym);
  free(msg->dl_mac_config_msg);
  free(msg);

  return 0;

 error:
  return -1;
}

int flexran_agent_mac_create_empty_ul_config(mid_t mod_id, Protocol__FlexranMessage **msg) {

  int xid = 0;
  Protocol__FlexHeader *header = NULL;
  if (flexran_create_header(xid, PROTOCOL__FLEX_TYPE__FLPT_UL_MAC_CONFIG, &header) != 0)
    goto error;

  Protocol__FlexUlMacConfig *ul_mac_config_msg;
  ul_mac_config_msg = malloc(sizeof(Protocol__FlexUlMacConfig));
  if (ul_mac_config_msg == NULL) {
    goto error;
  }
  protocol__flex_ul_mac_config__init(ul_mac_config_msg);

  ul_mac_config_msg->header = header;
  ul_mac_config_msg->has_sfn_sf = 1;
  ul_mac_config_msg->sfn_sf = flexran_get_sfn_sf(mod_id);

  *msg = malloc(sizeof(Protocol__FlexranMessage));
  if(*msg == NULL)
    goto error;
  protocol__flexran_message__init(*msg);
  (*msg)->msg_case = PROTOCOL__FLEXRAN_MESSAGE__MSG_UL_MAC_CONFIG_MSG;
  (*msg)->msg_dir =  PROTOCOL__FLEXRAN_DIRECTION__INITIATING_MESSAGE;
  (*msg)->ul_mac_config_msg = ul_mac_config_msg;

  return 0;

 error:
  return -1;
}


int flexran_agent_mac_destroy_ul_config(Protocol__FlexranMessage *msg) {
  int i; //,j, k;
  if(msg->msg_case != PROTOCOL__FLEXRAN_MESSAGE__MSG_UL_MAC_CONFIG_MSG)
    goto error;

  // Protocol__FlexUlDci *ul_dci;

  free(msg->ul_mac_config_msg->header);
  for (i = 0; i < msg->ul_mac_config_msg->n_ul_ue_data; i++) {
    // TODO  uplink rlc ...
    // free(msg->ul_mac_config_msg->dl_ue_data[i]->ce_bitmap);
  //   for (j = 0; j < msg->ul_mac_config_msg->ul_ue_data[i]->n_rlc_pdu; j++) {
  //     for (k = 0; k <  msg->ul_mac_config_msg->ul_ue_data[i]->rlc_pdu[j]->n_rlc_pdu_tb; k++) {
  // free(msg->ul_mac_config_msg->dl_ue_data[i]->rlc_pdu[j]->rlc_pdu_tb[k]);
  //     }
  //     free(msg->ul_mac_config_msg->ul_ue_data[i]->rlc_pdu[j]->rlc_pdu_tb);
  //     free(msg->ul_mac_config_msg->ul_ue_data[i]->rlc_pdu[j]);
  //   }
    // free(msg->ul_mac_config_msg->ul_ue_data[i]->rlc_pdu);
    // ul_dci = msg->ul_mac_config_msg->ul_ue_data[i]->ul_dci;
    // free(dl_dci->tbs_size);
    // free(ul_dci->mcs);
    // free(ul_dci->ndi);
    // free(ul_dci->rv);
    // free(ul_dci);
    // free(msg->ul_mac_config_msg->ul_ue_data[i]);
  }
  free(msg->ul_mac_config_msg->ul_ue_data);

  free(msg->ul_mac_config_msg);
  free(msg);

  return 0;

 error:
  return -1;
}


void flexran_agent_get_pending_dl_mac_config(mid_t mod_id, Protocol__FlexranMessage **msg) {

  struct lfds700_misc_prng_state ls;

  LFDS700_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
  lfds700_misc_prng_init(&ls);

  if (lfds700_ringbuffer_read(&ringbuffer_state[mod_id], NULL, (void **) msg, &ls) == 0) {
    *msg = NULL;
  }
}

int flexran_agent_mac_handle_dl_mac_config(mid_t mod_id, const void *params, Protocol__FlexranMessage **msg) {

  struct lfds700_misc_prng_state ls;
  enum lfds700_misc_flag overwrite_occurred_flag;
  Protocol__FlexranMessage *overwritten_dl_config;

  LFDS700_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
  lfds700_misc_prng_init(&ls);

  lfds700_ringbuffer_write( &ringbuffer_state[mod_id],
			    NULL,
			    (void *) params,
			    &overwrite_occurred_flag,
			    NULL,
			    (void **)&overwritten_dl_config,
			    &ls);

  if (overwrite_occurred_flag == LFDS700_MISC_FLAG_RAISED) {
    // Delete unmanaged dl_config
    flexran_agent_mac_destroy_dl_config(overwritten_dl_config);
  }
  *msg = NULL;
  return 2;

  // error:
  //*msg = NULL;
  //return -1;
}

void flexran_agent_init_mac_agent(mid_t mod_id) {
  int i, j;
  lfds700_misc_library_init_valid_on_current_logical_core();
  lfds700_misc_prng_init(&ps[mod_id]);
  int num_elements = RINGBUFFER_SIZE + 1;
  //Allow RINGBUFFER_SIZE messages to be stored in the ringbuffer at any time
  dl_mac_config_array[mod_id] = malloc( sizeof(struct lfds700_ringbuffer_element) *  num_elements);
  lfds700_ringbuffer_init_valid_on_current_logical_core( &ringbuffer_state[mod_id], dl_mac_config_array[mod_id], num_elements, &ps[mod_id], NULL );
  for (i = 0; i < MAX_MOBILES_PER_ENB; i++) {
    for (j = 0; j < 8; j++) {
      if (RC.mac && RC.mac[mod_id])
        RC.mac[mod_id]->UE_list.eNB_UE_stats[UE_PCCID(mod_id,i)][i].harq_pid = 0;
    }
  }
}

/***********************************************
 * FlexRAN agent - technology mac API implementation
 ***********************************************/

void flexran_agent_send_sr_info(mid_t mod_id) {
  int size;
  Protocol__FlexranMessage *msg;
  void *data;
  int priority = 0;
  err_code_t err_code;

  int xid = 0;

  /*TODO: Must use a proper xid*/
  err_code = flexran_agent_mac_sr_info(mod_id, (void *) &xid, &msg);
  if (err_code < 0) {
    goto error;
  }

  if (msg != NULL){
    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_MAC, data, size, priority)) {
      err_code = PROTOCOL__FLEXRAN_ERR__MSG_ENQUEUING;
      goto error;
    }

    LOG_D(FLEXRAN_AGENT,"sent message with size %d\n", size);
    return;
  }
 error:
  LOG_D(FLEXRAN_AGENT, "Could not send sr message\n");
}

void flexran_agent_send_sf_trigger(mid_t mod_id) {
  int size;
  Protocol__FlexranMessage *msg;
  void *data;
  int priority = 0;
  err_code_t err_code;

  int xid = 0;

  /*TODO: Must use a proper xid*/
  err_code = flexran_agent_mac_sf_trigger(mod_id, (void *) &xid, &msg);
  if (err_code < 0) {
    goto error;
  }

  if (msg != NULL){
    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_MAC, data, size, priority)) {
      err_code = PROTOCOL__FLEXRAN_ERR__MSG_ENQUEUING;
      goto error;
    }

    LOG_D(FLEXRAN_AGENT,"sent message with size %d\n", size);
    return;
  }
 error:
  LOG_D(FLEXRAN_AGENT, "Could not send sf trigger message\n");
}



int flexran_agent_register_mac_xface(mid_t mod_id, AGENT_MAC_xface *xface) {
  if (mac_agent_registered[mod_id]) {
    LOG_E(MAC, "MAC agent for eNB %d is already registered\n", mod_id);
    return -1;
  }

  //xface->agent_ctxt = &shared_ctxt[mod_id];
  xface->flexran_agent_send_sr_info = flexran_agent_send_sr_info;
  xface->flexran_agent_send_sf_trigger = flexran_agent_send_sf_trigger;
  //xface->flexran_agent_send_update_mac_stats = flexran_agent_send_update_mac_stats;
  xface->flexran_agent_get_pending_dl_mac_config = flexran_agent_get_pending_dl_mac_config;

  xface->dl_scheduler_loaded_lib = NULL;
  xface->ul_scheduler_loaded_lib = NULL;
  mac_agent_registered[mod_id] = 1;
  agent_mac_xface[mod_id] = xface;

  return 0;
}

int flexran_agent_unregister_mac_xface(mid_t mod_id, AGENT_MAC_xface *xface) {

  //xface->agent_ctxt = NULL;
  xface->flexran_agent_send_sr_info = NULL;
  xface->flexran_agent_send_sf_trigger = NULL;
  //xface->flexran_agent_send_update_mac_stats = NULL;
  xface->flexran_agent_get_pending_dl_mac_config = NULL;

  xface->dl_scheduler_loaded_lib = NULL;
  xface->ul_scheduler_loaded_lib = NULL;
  mac_agent_registered[mod_id] = 0;
  agent_mac_xface[mod_id] = NULL;

  return 0;
}






