/*
 * 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.0  (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 PHY/LTE_TRANSPORT/dci_tools.c
 * \brief PHY Support routines (eNB/UE) for filling PDSCH/PUSCH/DLSCH/ULSCH data structures based on DCI PDUs generated by eNB MAC scheduler.
 * \author R. Knopp
 * \date 2011
 * \version 0.1
 * \company Eurecom
 * \email: knopp@eurecom.fr
 * \note
 * \warning
 */
#include "PHY/defs.h"
#include "PHY/extern.h"
#include "SCHED/defs.h"
#ifdef DEBUG_DCI_TOOLS
#include "PHY/vars.h"
#endif
#include "assertions.h"
//#include "dlsch_tbs_full.h"


//#define DEBUG_HARQ

#include "LAYER2/MAC/extern.h"
#include "LAYER2/MAC/defs.h"
#include "PHY/defs_nb_iot.h"

//#define DEBUG_DCI

void add_dci_NB_IoT(DCI_PDU_NB *DCI_pdu,void *pdu,rnti_t rnti,unsigned char dci_size_bytes,unsigned char aggregation,unsigned char dci_size_bits,unsigned char dci_fmt, uint8_t npdcch_start_symbol)
{
	//put the pdu
  memcpy(&DCI_pdu->dci_alloc[DCI_pdu->Num_dci].dci_pdu[0],pdu,dci_size_bytes);
  //configure the dci alloc
  DCI_pdu->dci_alloc[DCI_pdu->Num_dci].dci_length = dci_size_bits;
  DCI_pdu->dci_alloc[DCI_pdu->Num_dci].L          = aggregation;
  DCI_pdu->dci_alloc[DCI_pdu->Num_dci].rnti       = rnti;
  DCI_pdu->dci_alloc[DCI_pdu->Num_dci].format     = dci_fmt;
  DCI_pdu->npdcch_start_symbol = npdcch_start_symbol;

  DCI_pdu->Num_dci++;

  LOG_D(MAC,"add ue specific dci format %d for rnti %x \n",dci_fmt,rnti);
}


int generate_eNB_ulsch_params_from_dci_NB_IoT(PHY_VARS_eNB *eNB,
                                              eNB_rxtx_proc_t *proc,
                                              DCI_CONTENT *DCI_Content,
                                              uint16_t rnti,
                                              DCI_format_NB_t dci_format,
                                              uint8_t UE_id,
                                              uint8_t aggregation,
									                            uint8_t npdcch_start_symbol)
{

  void *ULSCH_DCI_NB = NULL;

  eNB->DCI_pdu = (DCI_PDU_NB*) malloc(sizeof(DCI_PDU_NB));

  /// type = 0 => DCI Format N0, type = 1 => DCI Format N1, 1 bits
  uint8_t type;
  /// Subcarrier indication, 6 bits
  uint8_t scind;
  /// Resourse Assignment (RU Assignment), 3 bits
  uint8_t ResAssign;
  /// Modulation and Coding Scheme, 4 bits
  uint8_t mcs;
  /// New Data Indicator, 1 bits
  uint8_t ndi;
  /// Scheduling Delay, 2 bits
  uint8_t Scheddly;
  /// Repetition Number, 3 bits
  uint8_t RepNum;
  /// Redundancy version for HARQ (only use 0 and 2), 1 bits
  uint8_t rv;
  /// DCI subframe repetition Number, 2 bits
  uint8_t DCIRep;
  
  if (dci_format == DCIFormatN0) 
    {
      
      type        = DCI_Content->DCIN0.type;
      scind       = DCI_Content->DCIN0.scind;
      ResAssign   = DCI_Content->DCIN0.ResAssign;
      mcs         = DCI_Content->DCIN0.mcs;
      ndi         = DCI_Content->DCIN0.ndi;
      Scheddly    = DCI_Content->DCIN0.Scheddly;
      RepNum      = DCI_Content->DCIN0.RepNum;
      rv          = DCI_Content->DCIN0.rv;
      DCIRep      = DCI_Content->DCIN0.DCIRep;

      /*Packed DCI here*/
      ((DCIN0_t *)ULSCH_DCI_NB)->type      =type;
      ((DCIN0_t *)ULSCH_DCI_NB)->scind     =scind;
      ((DCIN0_t *)ULSCH_DCI_NB)->ResAssign =ResAssign;
      ((DCIN0_t *)ULSCH_DCI_NB)->Scheddly  =Scheddly;
      ((DCIN0_t *)ULSCH_DCI_NB)->mcs       =mcs;
      ((DCIN0_t *)ULSCH_DCI_NB)->rv        =rv;
      ((DCIN0_t *)ULSCH_DCI_NB)->RepNum    =RepNum;
      ((DCIN0_t *)ULSCH_DCI_NB)->ndi       =ndi;
      ((DCIN0_t *)ULSCH_DCI_NB)->DCIRep    =DCIRep;


      add_dci_NB_IoT(eNB->DCI_pdu,ULSCH_DCI_NB,rnti,sizeof(DCIN0_t),aggregation,sizeof_DCIN0_t,DCIFormatN0, npdcch_start_symbol);

      // use this value to configure PHY both harq_processes and resource mapping.


      
      return(0);
    } 
  else 
    {
        LOG_E(PHY,"generate_eNB_ulsch_params_from_dci, Illegal dci_format %d\n",dci_format);
        return(-1);
    }
}

//map the Isf (DCI param) to the number of subframes (Nsf)
int resource_to_subframe[8] = {1,2,3,4,5,6,8,10}; 

int generate_eNB_dlsch_params_from_dci_NB_IoT(PHY_VARS_eNB *eNB,
                                              int frame,
                                              uint8_t subframe,
                                              DCI_CONTENT *DCI_Content,
                                              uint16_t rnti,
                                              DCI_format_NB_t dci_format,
                                              NB_IoT_eNB_NDLSCH_t *ndlsch,
                                              NB_DL_FRAME_PARMS *frame_parms,
                                              uint8_t aggregation,
									                            uint8_t npdcch_start_symbol)
{

  NB_IoT_DL_eNB_HARQ_t* ndlsch_harq = ndlsch->harq_process;
  void *DLSCH_DCI_NB = NULL;
  eNB->DCI_pdu = (DCI_PDU_NB*) malloc(sizeof(DCI_PDU_NB));



  //N1 parameters

  /// type = 0 => DCI Format N0, type = 1 => DCI Format N1, 1 bits
  uint8_t type = 0;
  //NPDCCH order indicator (set to 0),1 bits
  uint8_t orderIndicator = 0;
  // Scheduling Delay, 3 bits
  uint8_t Sched_delay = 0;
  // Resourse Assignment (RU Assignment), 3 bits
  uint8_t ResAssign = 0;//Isf
  // Modulation and Coding Scheme, 4 bits
  uint8_t mcs = 0;
  // Repetition Number, 4 bits
  uint8_t RepNum = 0;
  // DCI subframe repetition Number, 2 bits
  uint8_t DCIRep = 0;
  // New Data Indicator,1 bits
  uint8_t ndi = 0;
  // HARQ-ACK resource,4 bits
  uint8_t HARQackRes = 0;

  //N2 parameters

  //Direct indication information, 8 bits
  uint8_t directIndInf= 0;
  // Reserved information bits, 6 bits
  uint8_t resInfoBits =0;

  //   printf("Generate eNB DCI, format %d, rnti %x (pdu %p)\n",dci_format,rnti,dci_pdu);

  switch (dci_format) {

  // Impossible to have a DCI N0, we have condition before
  case DCIFormatN0:
    return(-1);
    break;

  case DCIFormatN1_RAR:  // This is DLSCH allocation for control traffic (no NDI and no ACK/NACK resource for RAR DCI)


	/*Packed DCI here-------------------------------------------*/
    type               = DCI_Content->DCIN1_RAR.type;
    orderIndicator     = DCI_Content->DCIN1_RAR.orderIndicator; 
    Sched_delay        = DCI_Content->DCIN1_RAR.Scheddly;
    ResAssign          = DCI_Content->DCIN1_RAR.ResAssign; 
    mcs                = DCI_Content->DCIN1_RAR.mcs; 
    RepNum             = DCI_Content->DCIN1_RAR.RepNum; 
    ndi                = DCI_Content->DCIN1_RAR.ndi;
    HARQackRes         = DCI_Content->DCIN1_RAR.HARQackRes; 
    DCIRep             = DCI_Content->DCIN1_RAR.DCIRep;
    
    //DCI pdu content
    ((DCIN1_RAR_t *)DLSCH_DCI_NB)->type           =type;
    ((DCIN1_RAR_t *)DLSCH_DCI_NB)->orderIndicator =orderIndicator;
    ((DCIN1_RAR_t *)DLSCH_DCI_NB)->Scheddly       =Sched_delay;
    ((DCIN1_RAR_t *)DLSCH_DCI_NB)->ResAssign      =ResAssign;
    ((DCIN1_RAR_t *)DLSCH_DCI_NB)->mcs            =mcs;
    ((DCIN1_RAR_t *)DLSCH_DCI_NB)->RepNum         =RepNum;
    ((DCIN1_RAR_t *)DLSCH_DCI_NB)->ndi            =ndi;
    ((DCIN1_RAR_t *)DLSCH_DCI_NB)->HARQackRes     =HARQackRes;
    ((DCIN1_RAR_t *)DLSCH_DCI_NB)->DCIRep         =DCIRep;


    add_dci_NB_IoT(eNB->DCI_pdu,DLSCH_DCI_NB,rnti,sizeof(DCIN1_RAR_t),aggregation,sizeof_DCIN1_RAR_t,DCIFormatN1_RAR, npdcch_start_symbol);


    /*Now configure the ndlsch structure*/

    ndlsch->subframe_tx[subframe] = 1; // check if it's OK
    ndlsch->rnti = rnti; //we store the RNTI (e.g. for RNTI will be used later)
    ndlsch->active = 0; //will be activated by the corresponding NDSLCH pdu

    // use this value to configure PHY both harq_processes and resource mapping.
    ndlsch_harq->scheduling_delay = Sched_delay;
    ndlsch_harq->resource_assignment = resource_to_subframe[ResAssign];  //from Isf of DCI to the number of subframe
    ndlsch_harq->repetition_number = RepNum;
    ndlsch_harq->dci_subframe_repetitions = DCIRep;
    ndlsch_harq->modulation = 2; //QPSK
    if(ndlsch_harq->round == 0) //this should be set from initialization (init-lte)
    	ndlsch_harq->status = ACTIVE;
    ndlsch_harq->mcs = mcs;

    /*
     * TS 36.213 ch 16.4.1.5
     * ITBS is always set equivalent to IMCS for data
     * ISF = ResAssign
     */

    ndlsch_harq->TBS = TBStable_NB_IoT[mcs][ResAssign];
    ndlsch_harq->subframe = subframe;

    //ndlsch_harq->B; we don-t have now my is given when we receive the dlsch data
    //ndlsch->error_treshold
    //ndlsch->G??
    //ndlsc->nsoft?? //set in new_eNB_dlsch (initialization)
    //ndlsch-> sqrt_rho_a?? set in dlsch_modulation
    //ndlsch-> sqrt_rho_b??? set in dlsch_modulation

    //set in new_eNB_dlsch (initialization)
    /*
     * Mlimit
     * nsoft
     * round
     */


    break;

  case DCIFormatN1: // for user data
    

    type               = DCI_Content->DCIN1.type;
    orderIndicator     = DCI_Content->DCIN1.orderIndicator; 
    Sched_delay           = DCI_Content->DCIN1.Scheddly;
    ResAssign          = DCI_Content->DCIN1.ResAssign; 
    mcs                = DCI_Content->DCIN1.mcs; 
    RepNum             = DCI_Content->DCIN1.RepNum; 
    ndi                = DCI_Content->DCIN1.ndi;
    HARQackRes         = DCI_Content->DCIN1.HARQackRes; 
    DCIRep             = DCI_Content->DCIN1.DCIRep;
    
    /*Packed DCI here*/
    ((DCIN1_t *)DLSCH_DCI_NB)->type           =type;
    ((DCIN1_t *)DLSCH_DCI_NB)->orderIndicator =orderIndicator;
    ((DCIN1_t *)DLSCH_DCI_NB)->Scheddly       =Sched_delay;
    ((DCIN1_t *)DLSCH_DCI_NB)->ResAssign      =ResAssign;
    ((DCIN1_t *)DLSCH_DCI_NB)->mcs            =mcs;
    ((DCIN1_t *)DLSCH_DCI_NB)->RepNum         =RepNum;
    ((DCIN1_t *)DLSCH_DCI_NB)->ndi            =ndi;
    ((DCIN1_t *)DLSCH_DCI_NB)->HARQackRes     =HARQackRes;
    ((DCIN1_t *)DLSCH_DCI_NB)->DCIRep         =DCIRep;


    add_dci_NB_IoT(eNB->DCI_pdu,DLSCH_DCI_NB,rnti,sizeof(DCIN1_t),aggregation,sizeof_DCIN1_t,DCIFormatN1,npdcch_start_symbol);

    /*Now configure the ndlsch structure*/

    ndlsch->subframe_tx[subframe] = 1; // check if it's OK
    ndlsch->rnti = rnti; //we store the RNTI (e.g. for RNTI will be used later)
    ndlsch->active = 0;//will be activated by the corresponding NDSLCH pdu

    // use this value to configure PHY both harq_processes and resource mapping.
        ndlsch_harq->scheduling_delay = Sched_delay;
        ndlsch_harq->resource_assignment = resource_to_subframe[ResAssign]; //from Isf of DCI to the number of subframe
        ndlsch_harq->repetition_number = RepNum;
        ndlsch_harq->dci_subframe_repetitions = DCIRep;
        ndlsch_harq->modulation = 2; //QPSK
        if(ndlsch_harq->round == 0){ //this should be set from initialization (init-lte)
        	ndlsch_harq->status = ACTIVE;
        	ndlsch_harq->mcs = mcs;
        	ndlsch_harq->TBS = TBStable_NB_IoT[mcs][ResAssign]; // this table should be rewritten for nb-iot
        }
        ndlsch_harq->frame = frame;
        ndlsch_harq->subframe = subframe;



    break;

  case DCIFormatN2_Ind: //MP: for the moment is not implemented

    type               = DCI_Content->DCIN2_Ind.type;
    directIndInf       = DCI_Content->DCIN2_Ind.directIndInf; 
    resInfoBits        = DCI_Content->DCIN2_Ind.resInfoBits; 

    /*Packed DCI here*/
    ((DCIN2_Ind_t *)DLSCH_DCI_NB)->type           =type;
    ((DCIN2_Ind_t *)DLSCH_DCI_NB)->directIndInf   =directIndInf;
    ((DCIN2_Ind_t *)DLSCH_DCI_NB)->resInfoBits    =resInfoBits;


    add_dci_NB_IoT(eNB->DCI_pdu,DLSCH_DCI_NB,rnti,sizeof(DCIN2_Ind_t),aggregation,sizeof_DCIN2_Ind_t,DCIFormatN2_Ind,npdcch_start_symbol);

    // use this value to configure PHY both harq_processes and resource mapping.
    break;

    
  case DCIFormatN2_Pag:  //MP: for the moment is not implemented

    type               = DCI_Content->DCIN2_Pag.type;
    ResAssign          = DCI_Content->DCIN2_Pag.ResAssign; 
    mcs                = DCI_Content->DCIN2_Pag.mcs; 
    RepNum             = DCI_Content->DCIN2_Pag.RepNum; 
    DCIRep             = DCI_Content->DCIN2_Pag.DCIRep; 

    /*Packed DCI here*/
    ((DCIN2_Pag_t *)DLSCH_DCI_NB)->type      =type;
    ((DCIN2_Pag_t *)DLSCH_DCI_NB)->ResAssign =ResAssign;
    ((DCIN2_Pag_t *)DLSCH_DCI_NB)->mcs       =mcs;
    ((DCIN2_Pag_t *)DLSCH_DCI_NB)->RepNum    =RepNum;
    ((DCIN2_Pag_t *)DLSCH_DCI_NB)->DCIRep    =DCIRep;


    add_dci_NB_IoT(eNB->DCI_pdu,DLSCH_DCI_NB,rnti,sizeof(DCIN2_Pag_t),aggregation,sizeof_DCIN2_Pag_t,DCIFormatN2_Pag,npdcch_start_symbol);

    // use this value to configure PHY both harq_processes and resource mapping.
    break;


  default:
    LOG_E(PHY,"Unknown DCI format\n");
    return(-1);
    break;
  }

  // compute DL power control parameters



  return(0);
}