#include "../SCHED/IF_Module_L1_primitives_nb_iot.h"
#include "../SCHED/defs.h"
#include "../SCHED/defs_nb_iot.h"
#include "common/utils/itti/assertions.h"
#include "PHY/defs.h"
#include "PHY/defs_nb_iot.h"
//#include "PHY/extern.h"
#include "PHY/extern_NB_IoT.h"
#include "PHY/vars.h"
#include "PHY/vars_NB_IoT.h"
#include "PHY/INIT/defs_nb_iot.h"


void handle_nfapi_dlsch_pdu_NB_IoT(PHY_VARS_eNB_NB_IoT *eNB,
						  		   eNB_rxtx_proc_t *proc,
		       	   	   	           nfapi_dl_config_request_pdu_t *dl_config_pdu,
						   		   uint8_t *sdu)
{

	NB_IoT_eNB_NDLSCH_t *ndlsch;
	NB_IoT_DL_eNB_HARQ_t *ndlsch_harq;
	nfapi_dl_config_ndlsch_pdu_rel13_t *rel13 = &dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13;
	int UE_id= -1;

  //Check for SI PDU since in NB-IoT there is no DCI for that
  //SIB1 (type 0), other DLSCH data (type 1) (include the SI messages) based on our ASSUMPTIONs

	//is SIB1-NB
  if(rel13->rnti_type == 0 && rel13->rnti == 65535)
  {

	  /*
	   * the configuration of the NDLSCH PDU for the SIB1-NB shoudl be the following:
	   * -RNTI type = 0; (BCCH)
	   * -RNTI = OxFFFF (65535)
	   * -Repetition number = 0-15 and should be mapped to 4,8,16 as reported in Table 16.4.1.3-3 TS 36.213 (is the schedulingInoSIB1 of the MIB)
	   * -Number of subframe for resource assignment = may is not neded to know since the scheduling is fixed
	   *  (Spec TS 36.331 "SIB1-NB transmission occur in subframe #4 of every other frame in 16 continuous frame"
	   *  meaning that from the starting point we should transmit the SIB1-NB in 8 subframes  among the 16 available (every other))
	   *
	   * From spec. TS 36.321 v14.2.o pag 31 --> there is an HARQ process for all the broadcast (so we consider it also for SIB1-NB)
	   *
	   */

	  	ndlsch= eNB->ndlsch_SIB1;
	  	ndlsch->ndlsch_type = SIB1;

		ndlsch->npdsch_start_symbol = rel13->start_symbol; //start symbol for the ndlsch transmission
		ndlsch_harq = ndlsch->harq_process;
		ndlsch_harq->pdu = sdu;
		//should be from 1 to 8
		ndlsch_harq->resource_assignment = rel13->number_of_subframes_for_resource_assignment;//maybe we don't care about it since a fixed schedule
		ndlsch_harq->repetition_number = rel13->repetition_number; //is the schedulingInfoSIB1 (value 1-15) of MIB that is mapped into value 4-8-16 (see NDLSCH fapi specs Table 4-47)
		ndlsch_harq->modulation = rel13->modulation;
		ndlsch_harq->status = ACTIVE;

		//SI information in reality have no feedback (so there is no retransmission from the HARQ view point since no ack and nack)
//        ndlsch_harq->frame = frame;
//        ndlsch_harq->subframe = subframe;

        ndlsch->nrs_antenna_ports = rel13->nrs_antenna_ports_assumed_by_the_ue;
        ndlsch->scrambling_sequence_intialization = rel13->scrambling_sequence_initialization_cinit;




  }
  //is SI message (this is an NDLSCH that will be transmitted very frequently)
  else if(rel13->rnti_type == 1 && rel13->rnti == 65535)
  {
	  /*
	   *
	   * the configuration of the NDLSCH PDU for the SIB1-NB should be the following:
	   * RNTI type = 1;
	   * RNTI = OxFFFF (65535)
	   * Repetition number = 0 and should be mapped to 1 through Table 16.4.1.3-2 TS 36.213
	   * Number of subframe for resource assignment = will be evaluated by the MAC based on the value of the "si-TB" field inside the SIB1-NB (value 2 or 8)
	   *
	   * From spec. TS 36.321 v14.2.o pag 31 --> there is an HARQ process for all the broadcast
	   *
	   * XXX for the moment we are not able to prevent the problem of Error: first transmission but sdu = NULL.
	   * anyway, the PHY layer if have finished the transmission it will not transmit anything and will generate the error
	   *
	   */

	  	ndlsch= eNB->ndlsch_SI;
	  	ndlsch_harq = ndlsch->harq_process;

		//new SI starting transmission (should enter here only the first time for a new transmission)
		if(sdu != NULL)
		{

		  	ndlsch->ndlsch_type = SI_Message;
			ndlsch->npdsch_start_symbol = rel13->start_symbol; //start OFDM symbol for the ndlsch transmission
			ndlsch_harq->pdu = sdu;
			ndlsch_harq->resource_assignment = rel13->number_of_subframes_for_resource_assignment;//value 2 or 8
			ndlsch_harq->repetition_number = rel13->repetition_number;//should be always fix to 0 to be mapped in 1
			ndlsch_harq->modulation = rel13->modulation;


			//SI information in reality have no feedback (so there is no retransmission from the HARQ view point since no sck and nack)
	//        ndlsch_harq->frame = frame;
	//        ndlsch_harq->subframe = subframe;

			ndlsch->nrs_antenna_ports = rel13->nrs_antenna_ports_assumed_by_the_ue;
			ndlsch->scrambling_sequence_intialization = rel13->scrambling_sequence_initialization_cinit;
		}
		else
		{
			//continue the remaining transmission of the previous SI at PHY if any (otherwise nothing)
			//there is no need of repeating the configuration on the ndlsch
			ndlsch_harq->pdu = NULL;
		}

		//Independently if we have the PDU or not (first transmission or repetition) the process is activated for triggering the ndlsch_procedure
	  	ndlsch_harq->status = ACTIVE;


  }
  //ue specific data or RAR (we already have received the DCI for this)
  else if(rel13->rnti != 65535 && rel13->rnti_type == 1)
  {

	  //check if the PDU is for RAR
	  if(eNB->ndlsch_ra != NULL && rel13->rnti == eNB->ndlsch_ra->rnti) //rnti for the RAR should have been set priviously by the DCI
	  {
		  eNB->ndlsch_ra->harq_process->pdu = sdu;
		  eNB->ndlsch_ra->npdsch_start_symbol = rel13->start_symbol;
		  eNB->ndlsch_ra->active = 1;
	  }
	  else
	  { //this for ue data
		  //TODO
		  //program addition DLSCH parameters not from DCI (for the moment we only pass the pdu)
		  //int UE_id = find_dlsch(rel13->rnti,eNB,SEARCH_EXIST);


		  UE_id =  find_ue_NB_IoT(rel13->rnti,eNB);
	  	  AssertFatal(UE_id==-1,"no existing ue specific dlsch_context\n");

	  	  ndlsch = eNB->ndlsch[(uint8_t)UE_id];
	  	  ndlsch_harq     = eNB->ndlsch[(uint8_t)UE_id]->harq_process;
	  	  AssertFatal(ndlsch_harq!=NULL,"dlsch_harq for ue specific is null\n");

	  	  ndlsch->npdsch_start_symbol = rel13->start_symbol;
	  	  ndlsch_harq->pdu  = sdu;
	  	  ndlsch->active = 1;

	  }

  }
  //I don't know which kind of data is
  else
  {
	  LOG_E(PHY, "handle_nfapi_dlsch_pdu_NB_IoT: Unknown type of data (rnti type %d, rnti %d)\n", rel13->rnti_type, rel13->rnti);
  }

}



/////////////////////////////////////////////////////////////////////////////////////////////////
//Memo for initialization TODO: target/SIMU/USER/init_lte.c/init_lte_eNB --> new_eNB_dlsch(..) //
//this is where the allocation of PHy_vars_eNB_NB_IoT and all the ndlsch structures happen            //
/////////////////////////////////////////////////////////////////////////////////////////////////


// do the schedule response and trigger the TX
void schedule_response(Sched_Rsp_t *Sched_INFO)
{

  //XXX check if correct to take eNB like this
  PHY_VARS_eNB_NB_IoT *eNB = PHY_vars_eNB_NB_IoT_g[0][Sched_INFO->CC_id];
  eNB_rxtx_proc_t *proc = &eNB->proc.proc_rxtx[0];
  NB_IoT_eNB_NPBCH_t *npbch;

  int i;

  module_id_t                     Mod_id    = Sched_INFO->module_id;
  uint8_t                         CC_id     = Sched_INFO->CC_id;
  nfapi_dl_config_request_body_t *DL_req    = Sched_INFO->DL_req;
  nfapi_ul_config_request_t 	 *UL_req    = Sched_INFO->UL_req;
  nfapi_hi_dci0_request_body_t *HI_DCI0_req = Sched_INFO->HI_DCI0_req;

  frame_t                         frame     = Sched_INFO->frame;
  sub_frame_t                     subframe  = Sched_INFO->subframe;


  AsserFatal(proc->subframe_tx != subframe, "Current subframe %d != NFAPI subframe %d\n",proc->subframe_tx,subframe);
  AsserFatal(proc->frame_tx != frame, "Current sframe %d != NFAPI frame %d\n", proc->frame_tx,frame );

  uint8_t number_dl_pdu             = DL_req->number_pdu;
  uint8_t number_ul_pdu				= UL_req->ul_config_request_body.number_of_pdus;
  uint8_t number_ul_dci             = HI_DCI0_req->number_of_dci;
  uint8_t number_pdsch_rnti         = DL_req->number_pdsch_rnti; // for the moment not used

  // at most 2 pdus (DCI) in the case of NPDCCH
  nfapi_dl_config_request_pdu_t *dl_config_pdu;
  nfapi_ul_config_request_pdu_t *ul_config_pdu;
  nfapi_hi_dci0_request_pdu_t *hi_dci0_pdu;


  //clear previous possible allocation (maybe someone else should be added)
  for(int i = 0; i < NUMBER_OF_UE_MAX_NB_IoT; i++)
  {
	  if(eNB->ndlsch[i])
	  {
		  eNB->ndlsch[i]->harq_process->round=0; // may not needed
		  /*clear previous allocation information for all UEs*/
		  eNB->ndlsch[i]->subframe_tx[subframe] = 0;
	  }


	  /*clear the DCI allocation maps for new subframe*/
	  if(eNB->nulsch[i])
	  {
		  eNB->nulsch[i]->harq_process->dci_alloc = 0; //flag for indicating that a DCI has been allocated for UL
		  eNB->nulsch[i]->harq_process->rar_alloc = 0; //Flag indicating that this ULSCH has been allocated by a RAR (for Msg3)
		  //no phich for NB-IoT so no DMRS should be utilized
	  }

  }



  for (i=0;i<number_dl_pdu;i++) //in principle this should be at most 2 (in case of DCI)
  {
    dl_config_pdu = &DL_req->dl_config_pdu_list[i];
    switch (dl_config_pdu->pdu_type) 
    {
    	case NFAPI_DL_CONFIG_NPDCCH_PDU_TYPE:
    		//Remember: there is no DCI for SI information
    		//TODO: separate the ndlsch structure configuration from the DCI (here we will encode only the DCI)
      		generate_eNB_dlsch_params_NB_IoT(eNB,proc,dl_config_pdu);

      		break;
    	case NFAPI_DL_CONFIG_NBCH_PDU_TYPE:

    		// for the moment we don't care about the n-bch pdu content since we need only the sdu if tx.request
    		npbch = eNB->npbch; //in the main of the lte-softmodem they should allocate this memory of PHY_vars
    		npbch->h_sfn_lsb = dl_config_pdu->nbch_pdu.nbch_pdu_rel13.hyper_sfn_2_lsbs;

    		if(Sched_INFO->sdu[i] != NULL)
    			npbch->pdu = Sched_INFO->sdu[i];
    		else
    			LOG_E(PHY, "Received a schedule_response with N-BCH but no SDU!!\n");

      		break;
    	case NFAPI_DL_CONFIG_NDLSCH_PDU_TYPE:
    		//we can have three types of NDLSCH based on our assumptions: SIB1, SI, Data, RAR
    		//remember that SI messages have no DCI in NB-IoT therefore this is the only way to configure the ndlsch_SI/ndlsch_SIB1 structures ndlsch->active = 1;

    		/*
    		 * OBSERVATION:
    		 * Although 2 DCI may be received over a schedule_response the transmission of the NDLSCH data foresees only 1 NDLSCH PDU at time.
    		 * Therefore is the MAC scheduler that knowing the different timing delay will send the corresponding schedule_response containing the NDLSCH PDU and the MAC PDU
    		 * at the proper DL subframe
    		 * -for this reason the activation of the ndslch structure is done only when we receive the NDLSCH pdu (here) such the in the TX procedure only 1 ue-specific pdu
    		 * 	result active from the loop before calling the ndlsch_procedure
    		 */

    		handle_nfapi_dlsch_pdu_NB_IoT(eNB, proc,dl_config_pdu,Sched_INFO->sdu[i]);

    		break;
    	default:
    		LOG_E(PHY, "dl_config_pdu type not for NB_IoT\n");
    		break;
   }
  }
  
  for (i=0;i<number_ul_dci;i++) 
  {
    hi_dci0_pdu = &HI_DCI0_req->hi_dci0_pdu_list[i];
    switch (hi_dci0_pdu->pdu_type) 
    {
    	case NFAPI_HI_DCI0_NPDCCH_DCI_PDU_TYPE:

      		generate_eNB_ulsch_params_NB_IoT(eNB,proc,hi_dci0_pdu);

      		break;
    	default:
    		LOG_E(PHY, "dl_config_pdu type not for NB_IoT\n");
    		break;

  	}
  }


  for(i = 0; i< number_ul_pdu; i++)
  {
	  ul_config_pdu = &UL_req->ul_config_request_body.ul_config_pdu_list[i];
	  switch(ul_config_pdu->pdu_type)
	  {
	  case NFAPI_UL_CONFIG_NULSCH_PDU_TYPE:
		  //TODO should distinguish between data and between data (npusch format)
		  /*NB: for reception of Msg3 generally not exist a DCI (because scheduling information are implicitly given by the RAR)
		   * but in case of FAPI specs we should receive an UL_config (containing the NULSCH pdu) for configuring the PHY for Msg3 reception from the MAC
		   * (this UL_config most probably will be created by the MAC when fill the RAR)
		   * (most probably we don't have the DL_config (for the RAR transmission) and the UL_CONFIG (for the Msg3 reception) at the same time (same subrame)
		   * since we are working in HD-FDD mode so for sure the UE will transmit the Msg3 in another subframe so make sense to have a UL_CONFIG in subframe
		   * diferent from the DL_CONFIG one)
		   *
		   */
		  break;
	  case NFAPI_UL_CONFIG_NRACH_PDU_TYPE:
		  //TODO just for update the nprach  configuration (given at the beginning through phy_config_sib2)
		  break;
	  }
  }


  //XXX problem: although we may have nothing to transmit this function should be always triggered in order to allow the PHY layer to complete the repetitions
  //of previous Transport Blocks
  phy_procedures_eNB_TX_NB_IoT(eNB,proc,NULL);

}

void PHY_config_req(PHY_Config_t* config_INFO){


	if(config_INFO->get_MIB != 0){

		//MIB-NB configuration
		NB_phy_config_mib_eNB(config_INFO->mod_id,
							  config_INFO->CC_id,
							  config_INFO->cfg->nfapi_config.rf_bands.rf_band[0],//eutraband
							  config_INFO->cfg->sch_config.physical_cell_id.value,
							  config_INFO->cfg->subframe_config.dl_cyclic_prefix_type.value,
							  config_INFO->cfg->subframe_config.ul_cyclic_prefix_type.value,
							  config_INFO->cfg->rf_config.tx_antenna_ports.value,
							  config_INFO->cfg->nfapi_config.earfcn.value,
							  config_INFO->cfg->nb_iot_config.prb_index.value,
							  config_INFO->cfg->nb_iot_config.operating_mode.value,
							  config_INFO->cfg->nb_iot_config.control_region_size.value,
							  config_INFO->cfg->nb_iot_config.assumed_crs_aps.value); //defined only in in-band different PCI

	}

	if(config_INFO->get_COMMON != 0)
	{
		//Common Configuration included in SIB2-NB
		NB_phy_config_sib2_eNB(config_INFO->mod_id,
							   config_INFO->CC_id,
						       &config_INFO->cfg->nb_iot_config, // FIXME to be evaluated is should be passed a pointer
						       &config_INFO->cfg->rf_config,
							   &config_INFO->cfg->uplink_reference_signal_config,
							   &config_INFO->extra_phy_parms
							   );
	}

	///FOR FAPI is not specified
	if(config_INFO->get_DEDICATED!= 0)
	{
	//Dedicated Configuration

			NB_phy_config_dedicated_eNB(config_INFO->mod_id,
										config_INFO->CC_id,
										config_INFO->rnti,
										&config_INFO->extra_phy_parms
										);

	}
}