/*
 * 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 eNB_scheduler_mch.c
 * \brief procedures related to eNB for the MCH transport channel
 * \author  Navid Nikaein and Raymond Knopp
 * \date 2012 - 2014
 * \email: navid.nikaein@eurecom.fr
 * \version 1.0
 * @ingroup _mac

 */


#include "LAYER2/MAC/mac.h"
#include "LAYER2/MAC/mac_proto.h"
#include "LAYER2/MAC/mac_extern.h"
#include "common/utils/LOG/log.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "UTIL/OPT/opt.h"
#include "OCG.h"
#include "OCG_extern.h"
#include "PHY/LTE_TRANSPORT/transport_common_proto.h"

#include "RRC/LTE/rrc_extern.h"
#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"

//#include "LAYER2/MAC/pre_processor.c"
#include "pdcp.h"
#include "assertions.h"

#if defined(ENABLE_ITTI)
  #include "intertask_interface.h"
#endif

#include "SIMULATION/TOOLS/sim.h" // for taus

#define ENABLE_MAC_PAYLOAD_DEBUG
#define DEBUG_eNB_SCHEDULER 1
#include "common/ran_context.h"

extern RAN_CONTEXT_t RC;


int8_t
get_mbsfn_sf_alloction(module_id_t module_idP, uint8_t CC_id,
                       uint8_t mbsfn_sync_area) {
  // currently there is one-to-one mapping between sf allocation pattern and sync area
  if (mbsfn_sync_area >= MAX_MBSFN_AREA) {
    LOG_W(MAC,
          "[eNB %d] CC_id %d MBSFN synchronization area %d out of range\n ",
          module_idP, CC_id, mbsfn_sync_area);
    return -1;
  } else if (RC.mac[module_idP]->
             common_channels[CC_id].mbsfn_SubframeConfig[mbsfn_sync_area]
             != NULL) {
    return mbsfn_sync_area;
  } else {
    LOG_W(MAC,
          "[eNB %d] CC_id %d MBSFN Subframe Config pattern %d not found \n ",
          module_idP, CC_id, mbsfn_sync_area);
    return -1;
  }
}

int
schedule_MBMS(module_id_t module_idP, uint8_t CC_id, frame_t frameP,
              sub_frame_t subframeP) {
  int mcch_flag = 0, mtch_flag = 0, msi_flag = 0;
  int mbsfn_period = 0; // 1<<(RC.mac[module_idP]->mbsfn_SubframeConfig[0]->radioframeAllocationPeriod);
  int mcch_period = 0;  //32<<(RC.mac[module_idP]->mbsfn_AreaInfo[0]->mcch_Config_r9.mcch_RepetitionPeriod_r9);
  int mch_scheduling_period =
    8 << (RC.mac[module_idP]->common_channels[CC_id].
          pmch_Config[0]->mch_SchedulingPeriod_r9);
  unsigned char mcch_sdu_length;
  unsigned char header_len_mcch = 0, header_len_msi =
                                    0, header_len_mtch = 0, header_len_mtch_temp =
                                          0, header_len_mcch_temp = 0, header_len_msi_temp = 0;
  int ii = 0, msi_pos = 0;
  int mcch_mcs = -1;
  uint16_t TBS, j = -1, padding = 0, post_padding = 0;
  mac_rlc_status_resp_t rlc_status;
  int num_mtch;
  int msi_length, i, k;
  unsigned char sdu_lcids[11], num_sdus = 0, offset = 0;
  uint16_t sdu_lengths[11], sdu_length_total = 0;
  unsigned char mch_buffer[MAX_DLSCH_PAYLOAD_BYTES];  // check the max value, this is for dlsch only
  COMMON_channels_t *cc = &RC.mac[module_idP]->common_channels[CC_id];
  cc->MCH_pdu.Pdu_size = 0;

  for (i = 0; i < cc->num_active_mbsfn_area; i++) {
    // assume, that there is always a mapping
    if ((j = get_mbsfn_sf_alloction(module_idP, CC_id, i)) == -1) {
      return 0;
    }

    mbsfn_period =
      1 << (cc->mbsfn_SubframeConfig[j]->radioframeAllocationPeriod);
    mcch_period =
      32 << (cc->mbsfn_AreaInfo[i]->
             mcch_Config_r9.mcch_RepetitionPeriod_r9);
    msi_pos = 0;
    ii = 0;
    LOG_D(MAC,
          "[eNB %d] CC_id %d Frame %d subframeP %d : Checking MBSFN Sync Area %d/%d with SF allocation %d/%d for MCCH and MTCH (mbsfn period %d, mcch period %d)\n",
          module_idP, CC_id, frameP, subframeP, i,
          cc->num_active_mbsfn_area, j, cc->num_sf_allocation_pattern,
          mbsfn_period, mcch_period);

    switch (cc->mbsfn_AreaInfo[i]->mcch_Config_r9.signallingMCS_r9) {
      case 0:
        mcch_mcs = 2;
        break;

      case 1:
        mcch_mcs = 7;
        break;

      case 2:
        mcch_mcs = 13;
        break;

      case 3:
        mcch_mcs = 19;
        break;
    }

    // 1st: Check the MBSFN subframes from SIB2 info (SF allocation pattern i, max 8 non-overlapping patterns exist)
    if (frameP % mbsfn_period == cc->mbsfn_SubframeConfig[j]->radioframeAllocationOffset) { // MBSFN frameP
      if (cc->mbsfn_SubframeConfig[j]->subframeAllocation.present == LTE_MBSFN_SubframeConfig__subframeAllocation_PR_oneFrame) {  // one-frameP format

        //  Find the first subframeP in this MCH to transmit MSI
        if (frameP % mch_scheduling_period ==
            cc->mbsfn_SubframeConfig[j]->
            radioframeAllocationOffset) {
          while (ii == 0) {
            ii = cc->
                 mbsfn_SubframeConfig[j]->subframeAllocation.
                 choice.oneFrame.buf[0] & (0x80 >> msi_pos);
            msi_pos++;
          }

          LOG_D(MAC,
                "[eNB %d] CC_id %d Frame %d subframeP %d : sync area %d sf allocation pattern %d sf alloc %x msi pos is %d \n",
                module_idP, CC_id, frameP, subframeP, i, j,
                cc->mbsfn_SubframeConfig[j]->
                subframeAllocation.choice.oneFrame.buf[0],
                msi_pos);
        }

        // Check if the subframeP is for MSI, MCCH or MTCHs and Set the correspoding flag to 1
        switch (subframeP) {
          case 1:
            if (cc->tdd_Config == NULL) {
              if ((cc->
                   mbsfn_SubframeConfig[j]->subframeAllocation.
                   choice.oneFrame.buf[0] & MBSFN_FDD_SF1) ==
                  MBSFN_FDD_SF1) {
                if (msi_pos == 1) {
                  msi_flag = 1;
                }

                if ((frameP % mcch_period ==
                     cc->mbsfn_AreaInfo[i]->
                     mcch_Config_r9.mcch_Offset_r9)
                    &&
                    ((cc->mbsfn_AreaInfo[i]->
                      mcch_Config_r9.sf_AllocInfo_r9.
                      buf[0] & MBSFN_FDD_SF1) ==
                     MBSFN_FDD_SF1)) {
                  mcch_flag = 1;
                }

                mtch_flag = 1;
              }
            }

            break;

          case 2:
            if (cc->tdd_Config == NULL) {
              if ((cc->
                   mbsfn_SubframeConfig[j]->subframeAllocation.
                   choice.oneFrame.buf[0] & MBSFN_FDD_SF2) ==
                  MBSFN_FDD_SF2) {
                if (msi_pos == 2) {
                  msi_flag = 1;
                }

                if ((frameP % mcch_period ==
                     cc->mbsfn_AreaInfo[i]->
                     mcch_Config_r9.mcch_Offset_r9)
                    &&
                    ((cc->mbsfn_AreaInfo[i]->
                      mcch_Config_r9.sf_AllocInfo_r9.
                      buf[0] & MBSFN_FDD_SF2) ==
                     MBSFN_FDD_SF2)) {
                  mcch_flag = 1;
                }

                mtch_flag = 1;
              }
            }

            break;

          case 3:
            if (cc->tdd_Config != NULL) { // TDD
              if ((cc->
                   mbsfn_SubframeConfig[j]->subframeAllocation.
                   choice.oneFrame.buf[0] & MBSFN_TDD_SF3) ==
                  MBSFN_TDD_SF3) {
                if (msi_pos == 1) {
                  msi_flag = 1;
                }

                if ((frameP % mcch_period ==
                     cc->mbsfn_AreaInfo[i]->
                     mcch_Config_r9.mcch_Offset_r9)
                    &&
                    ((cc->mbsfn_AreaInfo[i]->
                      mcch_Config_r9.sf_AllocInfo_r9.
                      buf[0] & MBSFN_TDD_SF3) ==
                     MBSFN_TDD_SF3)) {
                  mcch_flag = 1;
                }

                mtch_flag = 1;
              }
            } else {  // FDD
              if ((cc->
                   mbsfn_SubframeConfig[j]->subframeAllocation.
                   choice.oneFrame.buf[0] & MBSFN_FDD_SF3) ==
                  MBSFN_FDD_SF3) {
                if (msi_pos == 3) {
                  msi_flag = 1;
                }

                if ((frameP % mcch_period ==
                     cc->mbsfn_AreaInfo[i]->
                     mcch_Config_r9.mcch_Offset_r9)
                    &&
                    ((cc->mbsfn_AreaInfo[i]->
                      mcch_Config_r9.sf_AllocInfo_r9.
                      buf[0] & MBSFN_FDD_SF3) ==
                     MBSFN_FDD_SF3)) {
                  mcch_flag = 1;
                }

                mtch_flag = 1;
              }
            }

            break;

          case 4:
            if (cc->tdd_Config != NULL) {
              if ((cc->
                   mbsfn_SubframeConfig[j]->subframeAllocation.
                   choice.oneFrame.buf[0] & MBSFN_TDD_SF4) ==
                  MBSFN_TDD_SF4) {
                if (msi_pos == 2) {
                  msi_flag = 1;
                }

                if ((frameP % mcch_period ==
                     cc->mbsfn_AreaInfo[i]->
                     mcch_Config_r9.mcch_Offset_r9)
                    &&
                    ((cc->mbsfn_AreaInfo[i]->
                      mcch_Config_r9.sf_AllocInfo_r9.
                      buf[0] & MBSFN_TDD_SF4) ==
                     MBSFN_TDD_SF4)) {
                  mcch_flag = 1;
                }

                mtch_flag = 1;
              }
            }

            break;

          case 6:
            if (cc->tdd_Config == NULL) {
              if ((cc->
                   mbsfn_SubframeConfig[j]->subframeAllocation.
                   choice.oneFrame.buf[0] & MBSFN_FDD_SF6) ==
                  MBSFN_FDD_SF6) {
                if (msi_pos == 4) {
                  msi_flag = 1;
                }

                if ((frameP % mcch_period ==
                     cc->mbsfn_AreaInfo[i]->
                     mcch_Config_r9.mcch_Offset_r9)
                    &&
                    ((cc->mbsfn_AreaInfo[i]->
                      mcch_Config_r9.sf_AllocInfo_r9.
                      buf[0] & MBSFN_FDD_SF6) ==
                     MBSFN_FDD_SF6)) {
                  mcch_flag = 1;
                }

                mtch_flag = 1;
              }
            }

            break;

          case 7:
            if (cc->tdd_Config != NULL) { // TDD
              if ((cc->
                   mbsfn_SubframeConfig[j]->subframeAllocation.
                   choice.oneFrame.buf[0] & MBSFN_TDD_SF7) ==
                  MBSFN_TDD_SF7) {
                if (msi_pos == 3) {
                  msi_flag = 1;
                }

                if ((frameP % mcch_period ==
                     cc->mbsfn_AreaInfo[i]->
                     mcch_Config_r9.mcch_Offset_r9)
                    &&
                    ((cc->mbsfn_AreaInfo[i]->
                      mcch_Config_r9.sf_AllocInfo_r9.
                      buf[0] & MBSFN_TDD_SF7) ==
                     MBSFN_TDD_SF7)) {
                  mcch_flag = 1;
                }

                mtch_flag = 1;
              }
            } else {  // FDD
              if ((cc->
                   mbsfn_SubframeConfig[j]->subframeAllocation.
                   choice.oneFrame.buf[0] & MBSFN_FDD_SF7) ==
                  MBSFN_FDD_SF7) {
                if (msi_pos == 5) {
                  msi_flag = 1;
                }

                if ((frameP % mcch_period ==
                     cc->mbsfn_AreaInfo[i]->
                     mcch_Config_r9.mcch_Offset_r9)
                    &&
                    ((cc->mbsfn_AreaInfo[i]->
                      mcch_Config_r9.sf_AllocInfo_r9.
                      buf[0] & MBSFN_FDD_SF7) ==
                     MBSFN_FDD_SF7)) {
                  mcch_flag = 1;
                }

                mtch_flag = 1;
              }
            }

            break;

          case 8:
            if (cc->tdd_Config != NULL) { //TDD
              if ((cc->
                   mbsfn_SubframeConfig[j]->subframeAllocation.
                   choice.oneFrame.buf[0] & MBSFN_TDD_SF8) ==
                  MBSFN_TDD_SF8) {
                if (msi_pos == 4) {
                  msi_flag = 1;
                }

                if ((frameP % mcch_period ==
                     cc->mbsfn_AreaInfo[i]->
                     mcch_Config_r9.mcch_Offset_r9)
                    &&
                    ((cc->mbsfn_AreaInfo[i]->
                      mcch_Config_r9.sf_AllocInfo_r9.
                      buf[0] & MBSFN_TDD_SF8) ==
                     MBSFN_TDD_SF8)) {
                  mcch_flag = 1;
                }

                mtch_flag = 1;
              }
            } else {  // FDD
              if ((cc->
                   mbsfn_SubframeConfig[j]->subframeAllocation.
                   choice.oneFrame.buf[0] & MBSFN_FDD_SF8) ==
                  MBSFN_FDD_SF8) {
                if (msi_pos == 6) {
                  msi_flag = 1;
                }

                if ((frameP % mcch_period ==
                     cc->mbsfn_AreaInfo[i]->
                     mcch_Config_r9.mcch_Offset_r9)
                    &&
                    ((cc->mbsfn_AreaInfo[i]->
                      mcch_Config_r9.sf_AllocInfo_r9.
                      buf[0] & MBSFN_FDD_SF8) ==
                     MBSFN_FDD_SF8)) {
                  mcch_flag = 1;
                }

                mtch_flag = 1;
              }
            }

            break;

          case 9:
            if (cc->tdd_Config != NULL) {
              if ((cc->
                   mbsfn_SubframeConfig[j]->subframeAllocation.
                   choice.oneFrame.buf[0] & MBSFN_TDD_SF9) ==
                  MBSFN_TDD_SF9) {
                if (msi_pos == 5) {
                  msi_flag = 1;
                }

                if ((frameP % mcch_period ==
                     cc->mbsfn_AreaInfo[i]->
                     mcch_Config_r9.mcch_Offset_r9)
                    &&
                    ((cc->mbsfn_AreaInfo[i]->
                      mcch_Config_r9.sf_AllocInfo_r9.
                      buf[0] & MBSFN_TDD_SF9) ==
                     MBSFN_TDD_SF9)) {
                  mcch_flag = 1;
                }

                mtch_flag = 1;
              }
            }

            break;
        }   // end switch

        // sf allocation is non-overlapping
        if ((msi_flag == 1) || (mcch_flag == 1)
            || (mtch_flag == 1)) {
          LOG_D(MAC,
                "[eNB %d] CC_id %d Frame %d Subframe %d: sync area %d SF alloc %d: msi flag %d, mcch flag %d, mtch flag %d\n",
                module_idP, CC_id, frameP, subframeP, i, j,
                msi_flag, mcch_flag, mtch_flag);
          break;
        }
      } else {    // four-frameP format
      }
    }
  }       // end of for loop

  cc->msi_active = 0;
  cc->mcch_active = 0;
  cc->mtch_active = 0;

  // Calculate the mcs
  if ((msi_flag == 1) || (mcch_flag == 1)) {
    cc->MCH_pdu.mcs = mcch_mcs;
  } else if (mtch_flag == 1) {  // only MTCH in this subframeP
    cc->MCH_pdu.mcs = cc->pmch_Config[0]->dataMCS_r9;
  }

  // 2nd: Create MSI, get MCCH from RRC and MTCHs from RLC

  // there is MSI (MCH Scheduling Info)
  if (msi_flag == 1) {
    // Create MSI here
    uint16_t msi_control_element[29], *msi_ptr;
    msi_ptr = &msi_control_element[0];
    ((MSI_ELEMENT *) msi_ptr)->lcid = MCCH_LCHANID; //MCCH

    if (mcch_flag == 1) {
      ((MSI_ELEMENT *) msi_ptr)->stop_sf_MSB = 0;
      ((MSI_ELEMENT *) msi_ptr)->stop_sf_LSB = 0;
    } else {    // no mcch for this MSP
      ((MSI_ELEMENT *) msi_ptr)->stop_sf_MSB = 0x7; // stop value is 2047
      ((MSI_ELEMENT *) msi_ptr)->stop_sf_LSB = 0xff;
    }

    msi_ptr += sizeof(MSI_ELEMENT);
    //Header for MTCHs
    num_mtch = cc->mbms_SessionList[0]->list.count;

    for (k = 0; k < num_mtch; k++) {  // loop for all session in this MCH (MCH[0]) at this moment
      ((MSI_ELEMENT *) msi_ptr)->lcid = cc->mbms_SessionList[0]->list.array[k]->logicalChannelIdentity_r9;  //mtch_lcid;
      ((MSI_ELEMENT *) msi_ptr)->stop_sf_MSB = 0; // last subframeP of this mtch (only one mtch now)
      ((MSI_ELEMENT *) msi_ptr)->stop_sf_LSB = 0xB;
      msi_ptr += sizeof(MSI_ELEMENT);
    }

    msi_length = msi_ptr - msi_control_element;

    if (msi_length < 128) {
      header_len_msi = 2;
    } else {
      header_len_msi = 3;
    }

    LOG_D(MAC,
          "[eNB %d] CC_id %d Frame %d : MSI->MCH, length of MSI is %d bytes \n",
          module_idP, CC_id, frameP, msi_length);
    //LOG_D(MAC,"Scheduler: MSI is transmitted in this subframeP \n" );
    //   LOG_D(MAC,"Scheduler: MSI length is %d bytes\n",msi_length);
    // Store MSI data to mch_buffer[0]
    memcpy((char *) &mch_buffer[sdu_length_total],
           msi_control_element, msi_length);
    sdu_lcids[num_sdus] = MCH_SCHDL_INFO;
    sdu_lengths[num_sdus] = msi_length;
    sdu_length_total += sdu_lengths[num_sdus];
    LOG_I(MAC, "[eNB %d] CC_id %d Create %d bytes for MSI\n",
          module_idP, CC_id, sdu_lengths[num_sdus]);
    num_sdus++;
    cc->msi_active = 1;
  }

  // there is MCCH
  if (mcch_flag == 1) {
    LOG_D(MAC,
          "[eNB %d] CC_id %d Frame %d Subframe %d: Schedule MCCH MESSAGE (area %d, sfAlloc %d)\n",
          module_idP, CC_id, frameP, subframeP, i, j);
    mcch_sdu_length = mac_rrc_data_req(module_idP, CC_id, frameP, MCCH, 0xFFFC, 1, &cc->MCCH_pdu.payload[0],
                                       i);  // this is the mbsfn sync area index

    if (mcch_sdu_length > 0) {
      LOG_D(MAC,
            "[eNB %d] CC_id %d Frame %d subframeP %d : MCCH->MCH, Received %d bytes from RRC \n",
            module_idP, CC_id, frameP, subframeP, mcch_sdu_length);
      header_len_mcch = 2;

      if (cc->tdd_Config != NULL) {
        LOG_D(MAC,
              "[eNB %d] CC_id %d Frame %d subframeP %d: Scheduling MCCH->MCH (TDD) for MCCH message %d bytes (mcs %d )\n",
              module_idP, CC_id, frameP, subframeP,
              mcch_sdu_length, mcch_mcs);
      } else {
        LOG_I(MAC,
              "[eNB %d] CC_id %d Frame %d subframeP %d: Scheduling MCCH->MCH (FDD) for MCCH message %d bytes (mcs %d)\n",
              module_idP, CC_id, frameP, subframeP,
              mcch_sdu_length, mcch_mcs);
      }

      cc->mcch_active = 1;
      memcpy((char *) &mch_buffer[sdu_length_total],
             &cc->MCCH_pdu.payload[0], mcch_sdu_length);
      sdu_lcids[num_sdus] = MCCH_LCHANID;
      sdu_lengths[num_sdus] = mcch_sdu_length;

      if (sdu_lengths[num_sdus] > 128) {
        header_len_mcch = 3;
      }

      sdu_length_total += sdu_lengths[num_sdus];
      LOG_D(MAC,
            "[eNB %d] CC_id %d Got %d bytes for MCCH from RRC \n",
            module_idP, CC_id, sdu_lengths[num_sdus]);
      num_sdus++;
    }
  }

  TBS =
    get_TBS_DL(cc->MCH_pdu.mcs, to_prb(cc->mib->message.dl_Bandwidth));

  // do not let mcch and mtch multiplexing when relaying is active
  // for sync area 1, so not transmit data
  //if ((i == 0) && ((RC.mac[module_idP]->MBMS_flag != multicast_relay) || (RC.mac[module_idP]->mcch_active==0))) {

  // there is MTCHs, loop if there are more than 1
  if (mtch_flag == 1) {
    // Calculate TBS
    /* if ((msi_flag==1) || (mcch_flag==1)) {
       TBS = mac_xface->get_TBS(mcch_mcs, mac_xface->frame_parms->N_RB_DL);
       }
       else { // only MTCH in this subframeP
       TBS = mac_xface->get_TBS(RC.mac[module_idP]->pmch_Config[0]->dataMCS_r9, mac_xface->frame_parms->N_RB_DL);
       }

       // get MTCH data from RLC (like for DTCH)
       LOG_D(MAC,"[eNB %d] CC_id %d Frame %d subframe %d: Schedule MTCH (area %d, sfAlloc %d)\n",Mod_id,CC_id,frame,subframe,i,j);

       header_len_mtch = 3;
       LOG_D(MAC,"[eNB %d], CC_id %d, Frame %d, MTCH->MCH, Checking RLC status (rab %d, tbs %d, len %d)\n",
       Mod_id,CC_id,frame,MTCH,TBS,
       TBS-header_len_mcch-header_len_msi-sdu_length_total-header_len_mtch);

       rlc_status = mac_rlc_status_ind(Mod_id,frame,1,RLC_MBMS_YES,MTCH+ (maxDRB + 3) * MAX_MOBILES_PER_RG,
       TBS-header_len_mcch-header_len_msi-sdu_length_total-header_len_mtch);
       printf("frame %d, subframe %d,  rlc_status.bytes_in_buffer is %d\n",frame,subframe, rlc_status.bytes_in_buffer);

     */
    // get MTCH data from RLC (like for DTCH)
    LOG_D(MAC,
          "[eNB %d] CC_id %d Frame %d subframeP %d: Schedule MTCH (area %d, sfAlloc %d)\n",
          module_idP, CC_id, frameP, subframeP, i, j);
    header_len_mtch = 3;
    LOG_D(MAC,
          "[eNB %d], CC_id %d, Frame %d, MTCH->MCH, Checking RLC status (rab %d, tbs %d, len %d)\n",
          module_idP, CC_id, frameP, MTCH, TBS,
          TBS - header_len_mcch - header_len_msi - sdu_length_total -
          header_len_mtch);
    rlc_status =
      mac_rlc_status_ind(module_idP, 0, frameP, subframeP,
                         module_idP, ENB_FLAG_YES, MBMS_FLAG_YES,
                         MTCH,
                         TBS - header_len_mcch - header_len_msi -
                         sdu_length_total - header_len_mtch,0, 0);
    LOG_D(MAC,
          "e-MBMS log channel %u frameP %d, subframeP %d,  rlc_status.bytes_in_buffer is %d\n",
          MTCH, frameP, subframeP, rlc_status.bytes_in_buffer);

    if (rlc_status.bytes_in_buffer > 0) {
      LOG_I(MAC,
            "[eNB %d][MBMS USER-PLANE], CC_id %d, Frame %d, MTCH->MCH, Requesting %d bytes from RLC (header len mtch %d)\n",
            module_idP, CC_id, frameP,
            TBS - header_len_mcch - header_len_msi -
            sdu_length_total - header_len_mtch, header_len_mtch);
      sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP, 0, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_YES, MTCH, 0, //not used
                              (char *) &mch_buffer[sdu_length_total],0, 0
                                              );
      //sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP,frameP, MBMS_FLAG_NO,  MTCH+(MAX_NUM_RB*(MAX_MOBILES_PER_ENB+1)), (char*)&mch_buffer[sdu_length_total]);
      LOG_I(MAC,
            "[eNB %d][MBMS USER-PLANE] CC_id %d Got %d bytes for MTCH %d\n",
            module_idP, CC_id, sdu_lengths[num_sdus], MTCH);
      cc->mtch_active = 1;
      sdu_lcids[num_sdus] = MTCH;
      sdu_length_total += sdu_lengths[num_sdus];

      if (sdu_lengths[num_sdus] < 128) {
        header_len_mtch = 2;
      }

      num_sdus++;
    } else {
      header_len_mtch = 0;
    }
  }

  // FINAL STEP: Prepare and multiplexe MSI, MCCH and MTCHs
  if ((sdu_length_total + header_len_msi + header_len_mcch +
       header_len_mtch) > 0) {
    // Adjust the last subheader
    /*                                 if ((msi_flag==1) || (mcch_flag==1)) {
       RC.mac[module_idP]->MCH_pdu.mcs = mcch_mcs;
       }
       else if (mtch_flag == 1) { // only MTCH in this subframeP
       RC.mac[module_idP]->MCH_pdu.mcs = RC.mac[module_idP]->pmch_Config[0]->dataMCS_r9;
       }
     */
    header_len_mtch_temp = header_len_mtch;
    header_len_mcch_temp = header_len_mcch;
    header_len_msi_temp = header_len_msi;

    if (header_len_mtch > 0) {
      header_len_mtch = 1;  // remove Length field in the  subheader for the last PDU
    } else if (header_len_mcch > 0) {
      header_len_mcch = 1;
    } else {
      header_len_msi = 1;
    }

    // Calculate the padding
    if ((TBS - header_len_mtch - header_len_mcch - header_len_msi -
         sdu_length_total) < 0) {
      LOG_E(MAC, "Error in building MAC PDU, TBS %d < PDU %d \n",
            TBS,
            header_len_mtch + header_len_mcch + header_len_msi +
            sdu_length_total);
      return 0;
    } else if ((TBS - header_len_mtch - header_len_mcch - header_len_msi -
                sdu_length_total) <= 2) {
      padding =
        (TBS - header_len_mtch - header_len_mcch - header_len_msi -
         sdu_length_total);
      post_padding = 0;
    } else {    // using post_padding, give back the Length field of subheader  for the last PDU
      padding = 0;

      if (header_len_mtch > 0) {
        header_len_mtch = header_len_mtch_temp;
      } else if (header_len_mcch > 0) {
        header_len_mcch = header_len_mcch_temp;
      } else {
        header_len_msi = header_len_msi_temp;
      }

      post_padding =
        TBS - sdu_length_total - header_len_msi - header_len_mcch -
        header_len_mtch;
    }

    // Generate the MAC Header for MCH
    // here we use the function for DLSCH because DLSCH & MCH have the same Header structure
    offset = generate_dlsch_header((unsigned char *) cc->MCH_pdu.payload, num_sdus, sdu_lengths, sdu_lcids, 255,  // no drx
                                   31,  // no timing advance
                                   NULL,  // no contention res id
                                   padding, post_padding);
    cc->MCH_pdu.Pdu_size = TBS;
    cc->MCH_pdu.sync_area = i;
    cc->MCH_pdu.msi_active = cc->msi_active;
    cc->MCH_pdu.mcch_active = cc->mcch_active;
    cc->MCH_pdu.mtch_active = cc->mtch_active;
    LOG_D(MAC,
          " MCS for this sf is %d (mcch active %d, mtch active %d)\n",
          cc->MCH_pdu.mcs, cc->MCH_pdu.mcch_active,
          cc->MCH_pdu.mtch_active);
    LOG_I(MAC,
          "[eNB %d][MBMS USER-PLANE ] CC_id %d Generate header : sdu_length_total %d, num_sdus %d, sdu_lengths[0] %d, sdu_lcids[0] %d => payload offset %d,padding %d,post_padding %d (mcs %d, TBS %d), header MTCH %d, header MCCH %d, header MSI %d\n",
          module_idP, CC_id, sdu_length_total, num_sdus,
          sdu_lengths[0], sdu_lcids[0], offset, padding, post_padding,
          cc->MCH_pdu.mcs, TBS, header_len_mtch, header_len_mcch,
          header_len_msi);
    // copy SDU to mch_pdu after the MAC Header
    memcpy(&cc->MCH_pdu.payload[offset], mch_buffer, sdu_length_total);

    // filling remainder of MCH with random data if necessery
    for (j = 0; j < (TBS - sdu_length_total - offset); j++) {
      cc->MCH_pdu.payload[offset + sdu_length_total + j] =
        (char) (taus() & 0xff);
    }

    /* Tracing of PDU is done on UE side */
    if (opt_enabled == 1) {
      trace_pdu(DIRECTION_DOWNLINK, (uint8_t *) cc->MCH_pdu.payload, TBS, module_idP, WS_M_RNTI, 0xffff,  // M_RNTI = 6 in wirehsark
                RC.mac[module_idP]->frame,
                RC.mac[module_idP]->subframe, 0, 0);
      LOG_D(OPT,
            "[eNB %d][MCH] CC_id %d Frame %d : MAC PDU with size %d\n",
            module_idP, CC_id, frameP, TBS);
    }

    /*
       for (j=0;j<sdu_length_total;j++)
       printf("%2x.",RC.mac[module_idP]->MCH_pdu.payload[j+offset]);
       printf(" \n"); */
    return 1;
  } else {
    cc->MCH_pdu.Pdu_size = 0;
    cc->MCH_pdu.sync_area = 0;
    cc->MCH_pdu.msi_active = 0;
    cc->MCH_pdu.mcch_active = 0;
    cc->MCH_pdu.mtch_active = 0;
    // for testing purpose, fill with random data
    //for (j=0;j<(TBS-sdu_length_total-offset);j++)
    //  RC.mac[module_idP]->MCH_pdu.payload[offset+sdu_length_total+j] = (char)(taus()&0xff);
    return 0;
  }

  //this is for testing
  /*
     if (mtch_flag == 1) {
     //  LOG_D(MAC,"DUY: mch_buffer length so far is : %ld\n", &mch_buffer[sdu_length_total]-&mch_buffer[0]);
     return 1;
     }
     else
     return 0;
   */
}

MCH_PDU *get_mch_sdu(module_id_t module_idP, int CC_id, frame_t frameP,
                     sub_frame_t subframeP) {
  //  RC.mac[module_idP]->MCH_pdu.mcs=0;
  //LOG_D(MAC," MCH_pdu.mcs is %d\n", RC.mac[module_idP]->MCH_pdu.mcs);
  //#warning "MCH pdu should take the CC_id index"
  return (&RC.mac[module_idP]->common_channels[CC_id].MCH_pdu);
}