/*
 * 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 eNB_scheduler_dlsch_NB_IoT.c
 * \brief handle DL UE-specific scheduling
 * \author  NTUST BMW Lab./Xavier LIU
 * \date 2017 - 2018
 * \email: sephiroth7277@gmail.com 
 * \version 1.0
 *
 */

#include "defs_NB_IoT.h"
#include "proto_NB_IoT.h"
#include "extern_NB_IoT.h"   


/*DL scheduler*/
int schedule_DL_NB_IoT(module_id_t module_id, eNB_MAC_INST_NB_IoT *mac_inst, UE_TEMPLATE_NB_IoT *UE_info, uint32_t hyperSF_start, uint32_t frame_start, uint32_t subframe_start, UE_SCHED_CTRL_NB_IoT_t *UE_sched_ctrl_info)
{
	//number of candidate
	int cdd_num;
	//Transport block size
	int TBS;
	//Scheduling result buffer
	sched_temp_DL_NB_IoT_t *NPDCCH_info = (sched_temp_DL_NB_IoT_t*)malloc(sizeof(sched_temp_DL_NB_IoT_t));
	sched_temp_DL_NB_IoT_t *NPDSCH_info = (sched_temp_DL_NB_IoT_t*)malloc(sizeof(sched_temp_DL_NB_IoT_t));
	sched_temp_UL_NB_IoT_t *HARQ_info = (sched_temp_UL_NB_IoT_t*)malloc(sizeof(sched_temp_UL_NB_IoT_t));
	
	//Eric current remove it
	//DCI N1
	//DCIFormatN1_t *DCI_N1 = (DCIFormatN1_t*)malloc(sizeof(DCIFormatN1_t));
	
	//RLC Status
	
	//Eric
	//mac_rlc_status_resp_NB_IoT_t rlc_status;
	
	/*Index in DCI_N1*/
	uint32_t I_mcs, I_tbs, I_delay, I_sf;
	/*value for corresponding index*/
	/*Number of subframe per repetition*/
	int n_sf;
	/*flag*/
	int end_flagCCH=0;
	int end_flagSCH=0;
	int end_flagHARQ=0;
	int flag_retransmission=0;

	int HARQ_delay=0;
	uint32_t data_size;
	//uint32_t mac_sdu_size;

	//uint8_t sdu_temp[SCH_PAYLOAD_SIZE_MAX_NB_IoT];
	
	//Eric
	//logical_chan_id_t logical_channel;
	//Eric

	uint32_t subheader_length=2;
	
	//uint32_t payload_offset;
	

	uint32_t search_space_end_sf, h_temp, f_temp, sf_temp;

	I_mcs = get_I_mcs(UE_info->CE_level);
	I_tbs = I_mcs;
	//get max TBS
	TBS = get_max_tbs(I_tbs);

	//Eric
	if(UE_info->HARQ_round==0)
	{
		flag_retransmission=0;
		data_size=UE_sched_ctrl_info->total_sdu_size;
	}
	else
	{
		flag_retransmission=1;
		data_size=UE_info->DLSCH_pdu_size;
	}
	//Eric

	LOG_D(MAC,"[%04d][DLSchedulerUSS] Max TBS %d MCS index %d TBS index %d\n", mac_inst->current_subframe, TBS, I_mcs, I_tbs);
	/*set UE data information*/
	/*New transmission*/
#if 0
	if(UE_info->HARQ_round==0)
	{
		//Get RLC status
		/*
		rlc_status = mac_rlc_status_ind_NB_IoT(
										module_id,
										UE_info->rnti,
										module_id,
										frame_start,
										subframe_start,
										1,
										0,
										DCCH0_NB_IoT,
										0);
		data_size = rlc_status.bytes_in_buffer;
		*/
		//data_size = 200;
		data_size=0;
		int ue_index;
		for(ue_index=0;ue_index<UE_NUM_SIM;++ue_index)
		{
			if(UE_info_sim[ue_index].tc_rnti==UE_info->rnti)
				data_size = UE_info_sim[ue_index].data_size;
		}
	}
	/*Retransmission*/
	else
	{
		data_size = UE_info->DLSCH_pdu_size;
		flag_retransmission = 1;
		if((UE_info->HARQ_round>0)&&(TBS<data_size))
		{
			LOG_D(MAC,"[%04d][DLSchedulerUSS][Fail] TBS is not enough for retransmission\n", mac_inst->current_subframe);
			return;
		}
	}
#endif

	data_size=200;	//for testing

	LOG_D(MAC,"[%04d][DLSchedulerUSS] UE data size %d\n", mac_inst->current_subframe, data_size);
	//Have DCCH data
	if(data_size == 0)
	{
		LOG_D(MAC,"[%04d][DLSchedulerUSS][Fail] No data in DCCH0_NB_IoT\n", mac_inst->current_subframe);
		return;
	}
	if(data_size>127)
	{
		subheader_length=3;
	}
	if(TBS > data_size+subheader_length)
	{
		TBS = get_tbs(data_size, I_tbs, &I_sf);
		LOG_D(MAC,"[%04d][DLSchedulerUSS] TBS change to %d because data size is smaller than previous TBS\n", mac_inst->current_subframe, TBS);
	}

  search_space_end_sf=cal_num_dlsf(mac_inst, hyperSF_start, frame_start, subframe_start, &h_temp, &f_temp, &sf_temp, UE_info->R_max);
  LOG_D(MAC,"[%04d][DLSchedulerUSS] Search_space_start_sf %d Search_space_end_sf %d\n", convert_system_number_sf(hyperSF_start, frame_start, subframe_start), mac_inst->current_subframe, search_space_end_sf);
  //LOG_D(MAC,"[%04d][DLSchedulerUSS][%d] Search_space_start_sf %d Search_space_end_sf %d\n", mac_inst->current_subframe, UE_info->rnti, mac_inst->current_subframe, convert_system_number_sf(hyperSF_start, frame_start, subframe_start), search_space_end_sf);
	/*Loop all NPDCCH candidate position*/
	//Eric
	for(cdd_num=0;cdd_num<UE_info->R_max/UE_sched_ctrl_info->R_dci;++cdd_num)
	//Eric
	{
		//LOG_D(MAC,"[%04d][DLSchedulerUSS] Candidate num %d DCI Rep %d\n",mac_inst->current_subframe, cdd_num, UE_sched_ctrl_info->R_dci);
		/*Check NPDCCH Resource*/
		end_flagCCH = check_resource_NPDCCH_NB_IoT(mac_inst, hyperSF_start, frame_start, subframe_start, NPDCCH_info, cdd_num, UE_info->R_dci);

		//This candidate position is available
		/*Check NPDSCH Resource*/
		if(end_flagCCH!=-1)
		{
		  //LOG_D(MAC,"[%04d][DLSchedulerUSS] Candidate num %d allocate success\n",mac_inst->current_subframe, cdd_num);
			//LOG_D(MAC,"[%04d][DLSchedulerUSS] Allocate NPDCCH subframe %d to subframe %d cdd index %d\n", mac_inst->current_subframe, NPDCCH_info->sf_start, NPDCCH_info->sf_end, cdd_num);
			
			//Max DL TBS
			if(TBS > data_size+subheader_length)
			{
				TBS = get_tbs(data_size, I_tbs, &I_sf);
				//Eric
				LOG_D(MAC,"[%04d][DLSchedulerUSS] [%d] data_size %d TBS change to %d \n", mac_inst->current_subframe,UE_info->rnti, data_size, TBS);
				//Eric
			}
			

			//Get number of subframe this UE need per repetition
			n_sf = get_num_sf(I_sf);
			//LOG_D(MAC,"[%04d][DLSchedulerUSS] Number SF %d index SF %d\n",mac_inst->current_subframe, n_sf, I_sf);
			//LOG_D(MAC,"[%04d][DLSchedulerUSS] Require total %d DL SF Rep %d\n", n_sf*UE_sched_ctrl_info->R_dl, UE_sched_ctrl_info->R_dl);
			//Check have enough NPDSCH resource or not
			//loop 8 scheduling delay index
			for(I_delay=0;I_delay<8;++I_delay)
			{
		        if(search_space_end_sf<NPDCCH_info->sf_end+get_scheduling_delay(I_delay, UE_info->R_max)+5)
		        {
		          end_flagSCH = check_resource_NPDSCH_NB_IoT(mac_inst, NPDSCH_info, NPDCCH_info->sf_end, I_delay, UE_info->R_max, UE_sched_ctrl_info->R_dl_data, n_sf);
		          //Have available resource
		          /*Check HARQ resource*/
		          if(end_flagSCH!=-1)
		          {
		            //LOG_D(MAC,"[%04d][DLSchedulerUSS] Scheduling delay index: %d value: %d + 4 allocate success\n", mac_inst->current_subframe, I_delay, get_scheduling_delay(I_delay, UE_info->R_max));
		            //LOG_D(MAC,"[%04d][DLSchedulerUSS] Allocate NPDSCH subframe %d to subframe %d\n", mac_inst->current_subframe, NPDSCH_info->sf_start, NPDSCH_info->sf_end);
		            for(HARQ_delay=0;HARQ_delay<4;++HARQ_delay)
		            {
		              //LOG_D(MAC,"[%04d][DLSchedulerUSS] HARQ delay %d\n", mac_inst->current_subframe,get_HARQ_delay(1, HARQ_delay) );
		              end_flagHARQ=Check_UL_resource(NPDSCH_info->sf_end+get_HARQ_delay(1, HARQ_delay), UE_sched_ctrl_info->R_dl_harq, HARQ_info, 0, 1);
		              if(end_flagHARQ!=-1)
		              {
		                //LOG_D(MAC,"[%04d][DLSchedulerUSS] Allocate HARQ feedback subframe %d to subframe %d\n", mac_inst->current_subframe, HARQ_info->sf_start, HARQ_info->sf_end);
		                HARQ_info->ACK_NACK_resource_field=get_resource_field_value(HARQ_info->subcarrier_indication, get_scheduling_delay(HARQ_delay, UE_info->R_max));
		                //toggle NDI
		                if(flag_retransmission==0)
		                {
		                  UE_info->oldNDI_DL=(UE_info->oldNDI_DL+1)%2;
		                  //New transmission need to request data from RLC and generate new MAC PDU
		                  UE_info->I_mcs_dl = I_mcs;
		                  /*.......
		                  //Request data from RLC layer
		                  rlc_status = mac_rlc_status_ind_NB_IoT(
		                        module_id,
		                        UE_info->rnti,
		                        module_id,
		                        frame_start,
		                        subframe_start,
		                        1,
		                        0,
		                        DCCH0_NB_IoT,
		                        TBS-subheader_length);
		                  */
		                  //mac_sdu_size = mac_rlc_data_req_eNB_NB_IoT(module_id, UE_info->rnti, 0, frame_start, 0, DCCH0_NB_IoT, sdu_temp);
		                  
		                  //Eric
		                  //channel=DCCH0_NB_IoT;
						  //Eric

		                  //Generate header
		                  //payload_offset = generate_dlsch_header_NB_IoT(UE_info->DLSCH_pdu.payload, 1, &logical_channel, &mac_sdu_size, 0, 0, TBS);
		                  //Complete MAC PDU
		                  //memcpy(UE_info->DLSCH_pdu.payload+payload_offset, sdu_temp, mac_sdu_size);
		                  
		                  //Eric
		                  //UE_info->DLSCH_pdu.pdu_size=TBS;
		                  UE_sched_ctrl_info->NPDCCH_sf_end=NPDCCH_info->sf_end;
							UE_sched_ctrl_info->NPDCCH_sf_start=NPDCCH_info->sf_start;
							UE_sched_ctrl_info->NPDSCH_sf_end=NPDSCH_info->sf_end;
							UE_sched_ctrl_info->NPDSCH_sf_start=NPDSCH_info->sf_start;
							UE_sched_ctrl_info->HARQ_sf_end=HARQ_info->sf_end;
							UE_sched_ctrl_info->HARQ_sf_start=HARQ_info->sf_start;
							UE_sched_ctrl_info->TBS=TBS;
						  	UE_sched_ctrl_info->dci_n1_index_mcs=I_mcs;
						  	UE_sched_ctrl_info->index_tbs=I_tbs;
						  	UE_sched_ctrl_info->dci_n1_index_sf=I_sf;
							UE_sched_ctrl_info->dci_n1_n_sf=n_sf;
							UE_sched_ctrl_info->dci_n1_index_delay=I_delay;
							UE_sched_ctrl_info->dci_n1_index_ack_nack=HARQ_info->ACK_NACK_resource_field;
							UE_sched_ctrl_info->total_data_size_dl=data_size;
						  //Eric
		                }
		                LOG_D(MAC,"[%04d][DLSchedulerUSS][%d][Success] Complete scheduling with data size %d\n", mac_inst->current_subframe, UE_info->rnti, data_size);
		                //LOG_D(MAC,"[%04d][DLSchedulerUSS] RNTI %d\n", mac_inst->current_subframe, UE_info->rnti);
		                LOG_D(MAC,"[%04d][DLSchedulerUSS][%d][Success] Allocate NPDCCH subframe %d to subframe %d candidate index %d\n", mac_inst->current_subframe, UE_info->rnti, NPDCCH_info->sf_start, NPDCCH_info->sf_end, cdd_num);
		                LOG_D(MAC,"[%04d][DLSchedulerUSS][%d][Success] Scheduling delay index: %d value: %d + 4\n", mac_inst->current_subframe, UE_info->rnti, I_delay, get_scheduling_delay(I_delay, UE_info->R_max));
		                LOG_D(MAC,"[%04d][DLSchedulerUSS][%d][Success] Allocate NPDSCH subframe %d to subframe %d\n", mac_inst->current_subframe, UE_info->rnti, NPDSCH_info->sf_start, NPDSCH_info->sf_end);
		                LOG_D(MAC,"[%04d][DLSchedulerUSS][%d][Success] Allocate HARQ feedback subframe %d to subframe %d\n", mac_inst->current_subframe, UE_info->rnti, HARQ_info->sf_start, HARQ_info->sf_end);
		                LOG_D(MAC,"[%04d][DLSchedulerUSS][%d] Allocate NPDCCH subframe %d to subframe %d candidate index %d\n", mac_inst->current_subframe, UE_info->rnti, NPDCCH_info->sf_start, NPDCCH_info->sf_end, cdd_num);
		                LOG_D(MAC,"[%04d][DLSchedulerUSS][%d] Scheduling delay index: %d value: %d + 4\n", mac_inst->current_subframe, UE_info->rnti, I_delay, get_scheduling_delay(I_delay, UE_info->R_max));
		                LOG_D(MAC,"[%04d][DLSchedulerUSS][%d] Allocate NPDSCH subframe %d to subframe %d\n", mac_inst->current_subframe, UE_info->rnti, NPDSCH_info->sf_start, NPDSCH_info->sf_end);
		                LOG_D(MAC,"[%04d][DLSchedulerUSS][%d] Allocate HARQ feedback subframe %d to subframe %d\n", mac_inst->current_subframe, UE_info->rnti, HARQ_info->sf_start, HARQ_info->sf_end);
		                //Store PDU in UE template for retransmission
		                //fill_DCI_N1(DCI_N1, UE_info, I_delay, I_sf, HARQ_info->ACK_NACK_resource_field);
		                //LOG_D(MAC,"[%04d][DLSchedulerUSS] HARQ index %d\n", HARQ_info->ACK_NACK_resource_field);
		                
		                //Eric
		                //LOG_D(MAC,"[%04d][DLSchedulerUSS][%d] DCI N1 type:%d order:%d MCS:%d HARQ index:%d R:%d RscAssign:%d scheddly:%d DCI_R:%d\n", mac_inst->current_subframe, UE_info->rnti, DCI_N1->type, DCI_N1->orderIndicator, DCI_N1->mcs, DCI_N1->HARQackRes, DCI_N1->RepNum, DCI_N1->ResAssign, DCI_N1->Scheddly, DCI_N1->DCIRep);
		                //Eric


		                //Generate Scheduling result for this UE
		                //generate_scheduling_result_DL(NPDCCH_info->sf_start, NPDSCH_info->sf_start, HARQ_info->sf_start, DCI_N1, UE_info->rnti, TBS, UE_info->DLSCH_pdu.payload);
		                //generate_scheduling_result_DL(NPDCCH_info, NPDSCH_info, HARQ_info, DCI_N1, UE_info->rnti, TBS, UE_info->DLSCH_pdu.payload);
		                //LOG_D(MAC,"[%04d][DLSchedulerUSS] finish generate scheduling result\n");
		                //matain DL avialable resource
		                maintain_resource_DL(mac_inst, NPDCCH_info, NPDSCH_info);
		                //available_resource_DL_t *temp = available_resource_DL;
		                /*
		                while(temp!=NULL)
		                {
		                  LOG_D(MAC,"[%04d][DLSchedulerUSS] Available resource node subframe start %d end %d\n", mac_inst->current_subframe, temp->start_subframe, temp->end_subframe);
		                  temp=temp->next;
		                }
		                */
		                //Do maintain UL resource
		                adjust_UL_resource_list(HARQ_info);
		                LOG_D(MAC,"[%04d][DLSchedulerUSS] Complete DL scheduling\n", mac_inst->current_subframe);
		                //Change the UE state to idle
		                UE_info->direction = -1;

		                //LOG_D(MAC,"[%04d][DLSchedulerUSS] RNTI %d complete scheduling\n", mac_inst->current_subframe, UE_info->rnti);

		                return;
		              }
		            }
		            /*harq resource fail*/
		            if(end_flagHARQ==-1)
		            {
		              //LOG_D(MAC,"[%04d][DLSchedulerUSS] [Fail]HARQ_delay %d HARQ Resource fail\n", mac_inst->current_subframe, HARQ_delay);
		            }
		          }
		          //LOG_D(MAC,"[%04d][DLSchedulerUSS] Scheduling delay index %d allocate fail\n", mac_inst->current_subframe, I_delay);
		        }
			}
			/*NPDSCH resource fail*/
			if(end_flagSCH==-1)
			{
				//LOG_D(MAC,"[%04d][DLSchedulerUSS] [Fail]I_delay %d NPDSCH Resource fail\n", mac_inst->current_subframe, I_delay);
			}
		}
		//LOG_D(MAC,"[%04d][DLSchedulerUSS] Candidate %d no resource\n", mac_inst->current_subframe, cdd_num);

	}
	//Eric
	UE_sched_ctrl_info->flag_schedule_success=0;
	//Eric

	/*Resource allocate fail*/
	if((end_flagCCH==-1)||(end_flagSCH==-1)||(end_flagHARQ==-1))
	{
		LOG_D(MAC,"[%04d][DLSchedulerUSS][%d][Fail] Resource allocate fail\n", mac_inst->current_subframe, UE_info->rnti);
		LOG_D(MAC,"[%04d][DLSchedulerUSS][%d][Fail] Resource allocate fail\n", mac_inst->current_subframe, UE_info->rnti);
	}
	//Eric
	return -1;
	//Eric
}

int check_resource_NPDCCH_NB_IoT(eNB_MAC_INST_NB_IoT *mac_inst, uint32_t hyperSF_start, uint32_t frame_start, uint32_t subframe_start, sched_temp_DL_NB_IoT_t *NPDCCH_info, uint32_t cdd_num, uint32_t dci_rep)
{
	NPDCCH_info->sf_start = cal_num_dlsf(mac_inst, hyperSF_start, frame_start, subframe_start, &(NPDCCH_info->start_h), &(NPDCCH_info->start_f), &(NPDCCH_info->start_sf), dci_rep*cdd_num+1);
	//LOG_D(MAC,"[%04d][check_resource_NPDCCH_NB_IoT]  NPDCCH sf start %d\n", mac_inst->current_subframe, NPDCCH_info->sf_start);
	//LOG_D(MAC,"[check_resource_NPDCCH_NB_IoT]abs start : %d\n", NPDCCH_info->sf_start);
	return check_resource_DL_NB_IoT(mac_inst, NPDCCH_info->start_h, NPDCCH_info->start_f, NPDCCH_info->start_sf, dci_rep, NPDCCH_info);
}

int check_resource_NPDSCH_NB_IoT(eNB_MAC_INST_NB_IoT *mac_inst, sched_temp_DL_NB_IoT_t *NPDSCH_info, uint32_t sf_end, uint32_t I_delay, uint32_t R_max, uint32_t R_dl, uint32_t n_sf)
{
	int sf_temp = sf_end+get_scheduling_delay(I_delay, R_max)+5;
	while(is_dlsf(mac_inst, sf_temp)!=1)
  {
    ++sf_temp;
  }
	NPDSCH_info->sf_start = sf_temp;
	//transform sf into Hyper SF, Frame and subframe
	convert_system_number(NPDSCH_info->sf_start,&(NPDSCH_info->start_h), &(NPDSCH_info->start_f), &(NPDSCH_info->start_sf));
	//check this position available or not
	return check_resource_DL_NB_IoT(mac_inst, NPDSCH_info->start_h, NPDSCH_info->start_f, NPDSCH_info->start_sf, R_dl*n_sf, NPDSCH_info);
}

/*Check the available resource is enough or not from input starting position*/
/*return 0:success\1:fail*/
int check_resource_DL_NB_IoT(eNB_MAC_INST_NB_IoT *mac_inst, uint32_t hyperSF_start, uint32_t frame_start, uint32_t subframe_start, uint32_t dlsf_require, sched_temp_DL_NB_IoT_t *schedule_info)
{

	uint32_t node_start_sf, node_end_sf;
	uint32_t rsc_start_sf, rsc_end_sf;

	/*calculate the last subframe number for this transmission*/
	schedule_info->sf_end= cal_num_dlsf(mac_inst, hyperSF_start, frame_start, subframe_start, &(schedule_info->end_h), &(schedule_info->end_f), &(schedule_info->end_sf), dlsf_require);
	//LOG_D(MAC,"abs_end = %d\n", schedule_info->sf_end);
	rsc_start_sf = schedule_info->sf_start;
	if(schedule_info->sf_start<=schedule_info->sf_end)
	{
		rsc_end_sf = schedule_info->sf_end;
	}
	else
	{
		/*input position + Upper bound of subframe*/
		rsc_end_sf = schedule_info->sf_end+(1024*1024*10);
	}
	//LOG_D(MAC,"check_resource_DL_NB_IoT flag 1\n");
	/*initialize*/
	schedule_info->node = available_resource_DL;
	//LOG_D(MAC,"rsc need start subframe %d end subframe %d\n", rsc_start_sf, rsc_end_sf);
	/*Check available resource nodes to find the appropriate resource position*/
	while(schedule_info->node!=NULL)
	{
		//schedule_info->node->start_subframe <= schedule_info->sf_end
		//LOG_D(MAC,"check_resource_DL_NB_IoT flag 2\n");
		node_start_sf = schedule_info->node->start_subframe;
		if(schedule_info->node->start_subframe<=schedule_info->node->end_subframe)
		{
			node_end_sf = schedule_info->node->end_subframe;
		}
		else
		{
			/*input position + Upper bound of subframe*/
			node_end_sf = schedule_info->node->end_subframe+(1024*1024*10);
		}
		//LOG_D(MAC,"node start %d node end %d\n", node_start_sf, node_end_sf);
        if((node_start_sf<=rsc_start_sf)&&(node_end_sf>=rsc_end_sf))
        {
            return 0;
        }
        schedule_info->node = schedule_info->node->next;
	}
	//LOG_D(MAC,"check_resource_DL_NB_IoT flag 3\n");
	return -1;
}

uint32_t generate_dlsch_header_NB_IoT(uint8_t *pdu, uint32_t num_sdu, logical_chan_id_t *logical_channel, uint32_t *sdu_length, uint8_t flag_drx, uint8_t flag_ta, uint32_t TBS)
{
	int i;
	uint32_t total_sdu_size=0;
	//number of control element
	uint32_t num_ce=0;
	uint32_t num_subheader=0;
	uint32_t num_sdu_L_15;
	int32_t padding_size;
	uint8_t flag_end_padding=0;
	//Eric
	SCH_SUBHEADER_FIXED_NB_IoT *mac_header=(SCH_SUBHEADER_FIXED_NB_IoT*)pdu;
	//Eric
	uint32_t offset=0;

	for(i=0;i<num_sdu;++i)
	{
		LOG_D(MAC,"index %d sdu size %d\n", i, sdu_length[i]);
		if(sdu_length[i]>127)
		{
			num_sdu_L_15++;
		}
		total_sdu_size+=sdu_length[i];
	}
	if(flag_drx==1)
		num_ce++;
	if(flag_ta==1)
		num_ce++;
	num_subheader=num_ce+num_sdu;
	padding_size = TBS-total_sdu_size-num_ce;
	if(padding_size<0)
	{
		LOG_D(MAC,"[ERROR]TBS less than require subheader and control element\n");
		return -1;
	}
	LOG_D(MAC,"total SDU size %d\n", total_sdu_size);
	LOG_D(MAC,"padding size %d\n", padding_size);
	if(padding_size>2)
	{
		flag_end_padding=1;
	}
	if((padding_size<=2)&&(padding_size>0))
	{
		mac_header->LCID=PADDING;
		mac_header->E=1;
		mac_header->F2=0;
		mac_header->R=0;
		mac_header++;
		offset++;
	}
	if(padding_size==2)
	{
		mac_header->LCID=PADDING;
		mac_header->E=1;
		mac_header->F2=0;
		mac_header->R=0;
		mac_header++;
		offset++;
	}
	if(flag_drx==1)
	{
		mac_header->LCID=DRX_COMMAND;
		mac_header->E=1;
		mac_header->F2=0;
		mac_header->R=0;
		mac_header++;
		num_subheader--;
		offset++;
	}
	for(i=0;i<num_sdu;++i)
	{
		if((num_subheader==1)&&(flag_end_padding!=1))
        {
            mac_header->E=0;
            mac_header->LCID = logical_channel[i];
            mac_header->F2=0;
            mac_header->R=0;
            offset++;
            LOG_D(MAC,"last sdu\n");
        }
        else
        {		
        	//Eric
            if(sdu_length[i]<128)
            {
                ((SCH_SUBHEADER_SHORT_NB_IoT*)mac_header)->LCID = logical_channel[i];
                ((SCH_SUBHEADER_SHORT_NB_IoT*)mac_header)->F2=0;
                ((SCH_SUBHEADER_SHORT_NB_IoT*)mac_header)->R=0;
                ((SCH_SUBHEADER_SHORT_NB_IoT*)mac_header)->E=1;
                ((SCH_SUBHEADER_SHORT_NB_IoT*)mac_header)->F=0;
                ((SCH_SUBHEADER_SHORT_NB_IoT*)mac_header)->L=(uint8_t)sdu_length[i];
                num_subheader--;
                mac_header+=2;
                offset+=2;
            }
            else
            {
                ((SCH_SUBHEADER_LONG_NB_IoT*)mac_header)->LCID = logical_channel[i];
                ((SCH_SUBHEADER_LONG_NB_IoT*)mac_header)->F2=0;
                ((SCH_SUBHEADER_LONG_NB_IoT*)mac_header)->R=0;
                ((SCH_SUBHEADER_LONG_NB_IoT*)mac_header)->F=1;
                ((SCH_SUBHEADER_LONG_NB_IoT*)mac_header)->E=1;
                ((SCH_SUBHEADER_LONG_NB_IoT*)mac_header)->L_MSB=(uint8_t)(sdu_length[i]/256);
                ((SCH_SUBHEADER_LONG_NB_IoT*)mac_header)->L_LSB=(uint8_t)(sdu_length[i]%256);
                mac_header+=3;
                num_subheader--;
                offset+=3;
            }
            //Eric
        }
	}
	if(flag_end_padding==1)
	{
		mac_header->LCID=PADDING;
		mac_header->E=0;
		mac_header->F2=0;
		mac_header->R=0;
		mac_header++;
		offset++;
	}
	return offset;
}
void fill_DCI_N1(DCIFormatN1_t *DCI_N1, UE_TEMPLATE_NB_IoT *UE_info, UE_SCHED_CTRL_NB_IoT_t *UE_sched_ctrl_info)
{
	DCI_N1->type=1;
	DCI_N1->orderIndicator = 0;
	DCI_N1->Scheddly = UE_sched_ctrl_info->dci_n1_index_delay;
	DCI_N1->ResAssign =UE_sched_ctrl_info->dci_n1_index_sf;
	DCI_N1->mcs = UE_sched_ctrl_info->dci_n1_index_mcs;
	DCI_N1->RepNum = UE_sched_ctrl_info->dci_n1_index_sf;
	DCI_N1->HARQackRes = UE_sched_ctrl_info->dci_n1_index_ack_nack;
	//DCI_N1->DCIRep = 3-UE_info->R_max/UE_info->R_dci/2;
	DCI_N1->DCIRep=get_DCI_REP(UE_sched_ctrl_info->R_dci, UE_info->R_max);
	// Eric 
	LOG_D(MAC,"[fill_DCI_N1] Type %d order %d I_delay %d I_SF %d I_mcs %d I_rep %d I_harq %d I_dci %d\n", DCI_N1->type, DCI_N1->orderIndicator, DCI_N1->Scheddly, DCI_N1->ResAssign, DCI_N1->mcs, DCI_N1->RepNum, DCI_N1->HARQackRes, DCI_N1->DCIRep);
	// Eric
}
	

void generate_scheduling_result_DL(uint32_t NPDCCH_sf_end, uint32_t NPDCCH_sf_start, uint32_t NPDSCH_sf_end, uint32_t NPDSCH_sf_start, uint32_t HARQ_sf_end, uint32_t HARQ_sf_start, DCIFormatN1_t *DCI_pdu, rnti_t rnti, uint32_t TBS, uint8_t *DLSCH_pdu)
{
	// create the schedule result node for this time transmission
	schedule_result_t *NPDCCH_result = (schedule_result_t*)malloc(sizeof(schedule_result_t));
	schedule_result_t *NPDSCH_result = (schedule_result_t*)malloc(sizeof(schedule_result_t));
	schedule_result_t *HARQ_result = (schedule_result_t*)malloc(sizeof(schedule_result_t));

	schedule_result_t *tmp, *tmp1;
	/*fill NPDCCH result*/
	NPDCCH_result->rnti=rnti;
	NPDCCH_result->output_subframe = NPDCCH_sf_start;
	NPDCCH_result->end_subframe = NPDCCH_sf_end;
	NPDCCH_result->sdu_length = TBS;
	NPDCCH_result->direction = 1;
	NPDCCH_result->rnti_type = 3;
	NPDCCH_result->DLSCH_pdu = NULL;
	NPDCCH_result->DCI_pdu = (void*)DCI_pdu;
	NPDCCH_result->DCI_release = 0;
	NPDCCH_result->channel = NPDCCH;
	//NPDCCH_result->debug_str = (uint8_t*)malloc(6*sizeof(uint8_t));
	//NPDCCH_result->debug_str = dl_str1;
	NPDCCH_result->next = NULL;
	/*fill NPDSCH result*/
	NPDSCH_result->rnti=rnti;
	//NPDSCH_result->output_subframe = NPDSCH_subframe;
	NPDSCH_result->output_subframe = NPDSCH_sf_start;
	NPDSCH_result->end_subframe = NPDSCH_sf_end;
	NPDSCH_result->sdu_length = TBS;
	//NPDSCH_result->DLSCH_pdu = DLSCH_pdu;
	NPDSCH_result->DLSCH_pdu = NULL;
	NPDSCH_result->direction = 1;
	NPDSCH_result->rnti_type = 3;
	NPDSCH_result->DCI_pdu = (void*)DCI_pdu;
	NPDSCH_result->DCI_release = 0;
	NPDSCH_result->channel = NPDSCH;
	//NPDSCH_result->debug_str = (uint8_t*)malloc(7*sizeof(uint8_t));
	//NPDSCH_result->debug_str = dl_str2;
	NPDSCH_result->next = NULL;
	/*fill HARQ result*/
	HARQ_result->rnti=rnti;
	//HARQ_result->output_subframe = HARQ_subframe;
	HARQ_result->output_subframe = HARQ_sf_start;
	HARQ_result->end_subframe = HARQ_sf_end;
	HARQ_result->sdu_length = 0;
	HARQ_result->direction = 0;
	HARQ_result->rnti_type = 3;
	HARQ_result->DLSCH_pdu = NULL;
	HARQ_result->DCI_pdu = (void*)DCI_pdu;
	HARQ_result->DCI_release = 1;
	HARQ_result->channel = NPUSCH;
	HARQ_result->npusch_format = 1;
	//HARQ_result->debug_str = (uint8_t*)malloc(7*sizeof(uint8_t));
	//HARQ_result->debug_str = dl_str3;
	HARQ_result->next = NULL;
	//DEBUG("[generate_scheduling_result_DL] Generate NPDCCH node\n");
	/*NPDCCH scheduling result*/
	// be the first node of the DL scheduling result
	
	tmp = NULL;
	tmp1 = NULL;

	if(schedule_result_list_DL == NULL)
	{
		//schedule_result_list_DL = (schedule_result_t*)malloc(sizeof(schedule_result_t));
		schedule_result_list_DL = NPDCCH_result;
		//DEBUG("[generate_scheduling_result_DL] Generate NPDCCH node at head\n");
	}
	else
	{
		tmp = schedule_result_list_DL;
		while(tmp!=NULL)
		{
			if(NPDCCH_sf_start < tmp->output_subframe)
			{
				break;
			}
			tmp1 = tmp;
			tmp = tmp->next;
			//DEBUG("[generate_scheduling_result_DL] node output subframe %d at NPDCCH part\n", tmp->output_subframe);
		}
		/*tail*/
		if(tmp==NULL)
		{
			tmp1->next = NPDCCH_result;
		}
		else
		{
			NPDCCH_result->next = tmp;
			if(tmp1)
			{
				tmp1->next = NPDCCH_result;
			}
			else
			{
				schedule_result_list_DL = NPDCCH_result;
			}
		}
	}
	
	//DEBUG("[generate_scheduling_result_DL] Generate NPDCSH node\n");
	/*NPDSCH scheduling result*/
	tmp1 = NULL;
	tmp = schedule_result_list_DL;
	while(tmp!=NULL)
	{
		if(NPDSCH_sf_start < tmp->output_subframe)
		{
			break;
		}
		//DEBUG("[generate_scheduling_result_DL] node output subframe %d at NPDSCH part\n", tmp->output_subframe);
		tmp1 = tmp;
		tmp = tmp->next;
	}
	if(tmp==NULL)
	{
		tmp1->next = NPDSCH_result;
	}
	else
	{
		NPDSCH_result->next = tmp;
		if(tmp1)
		{
			tmp1->next = NPDSCH_result;
		}
		else
		{
			schedule_result_list_DL = NPDSCH_result;
		}
	}
	//DEBUG("[generate_scheduling_result_DL] Generate HARQ node\n");
	/*HARQ scheduling result*/
	// be the first node of UL
	// be the first node of UL
// Eric
#if 1
// Eric
	tmp1 = NULL;
	tmp = NULL;
	if(schedule_result_list_UL == NULL)
	{
		//DEBUG("[generate_scheduling_result_DL] LIST NULL, Generate HARQ at HEAD\n");
	  //schedule_result_list_UL = (schedule_result_t*)malloc(sizeof(schedule_result_t));
	  schedule_result_list_UL = HARQ_result;
	}
	else
	{
		tmp = schedule_result_list_UL;
		while(tmp!=NULL)
		{
			if(HARQ_sf_start < tmp->output_subframe)
			{
				break;
			}
			//DEBUG("[generate_scheduling_result_DL] node output subframe %d at HARQ part\n", tmp->output_subframe);
			tmp1 = tmp;
			tmp = tmp->next;
		}
		if(tmp==NULL)
		{
			//DEBUG("[generate_scheduling_result_DL] Generate HARQ at Tail\n");
			tmp1->next = HARQ_result;
		}
		else
		{
			HARQ_result->next = tmp;
			if(tmp1)
			{
				//DEBUG("[generate_scheduling_result_DL] Generate HARQ in normal\n");
				tmp1->next = HARQ_result;
			}else
			{
				//DEBUG("[generate_scheduling_result_DL] Generate HARQ at Head\n");
				schedule_result_list_UL = HARQ_result;
			}
		}
	}
// Eric
#endif
// Eric
}

void maintain_resource_DL(eNB_MAC_INST_NB_IoT *mac_inst, sched_temp_DL_NB_IoT_t *NPDCCH_info, sched_temp_DL_NB_IoT_t *NPDSCH_info)
{
	available_resource_DL_t *temp;
	uint8_t flag_same=0;
	int align_left;
	int align_right;
	uint32_t H_temp, f_temp, sf_temp;
  	uint32_t H_temp_r, f_temp_r, sf_temp_r;

	if(NPDSCH_info==NULL)
	{
		/****Maintain NPDCCH node*******/
		//	divided into two node
		//	keep one node(align left or right)
		//	delete node
		convert_system_number(NPDCCH_info->node->start_subframe, &H_temp, &f_temp, &sf_temp);
		//align_left = (calculate_DLSF(mac_inst, NPDCCH_info->node->start_subframe, NPDCCH_info->sf_start) == 0);
		align_left=(cal_num_dlsf(mac_inst, H_temp, f_temp, sf_temp, &H_temp_r, &f_temp_r, &sf_temp_r, 1)==NPDCCH_info->sf_start);
		align_right = ((calculate_DLSF(mac_inst, NPDCCH_info->sf_end, NPDCCH_info->node->end_subframe) == 0)||(NPDCCH_info->sf_end==NPDCCH_info->node->end_subframe));
		//align_left = (calculate_DLSF(mac_inst, NPDCCH_info->node->start_subframe, NPDCCH_info->sf_start) == 0);
		//LOG_D(MAC,"[maintain_resource_DL] align left %d align right %d\n", align_left, align_right);
		switch(align_left+align_right)
		{
			case 0:
				//  divided into two nodes, insert after oritinal node.
				//	A | node | B
		      	//	A | node | temp | B
				
				temp = (available_resource_DL_t *)malloc(sizeof(available_resource_DL_t));
				
				if((available_resource_DL_t *)0 == NPDCCH_info->node->next){
					available_resource_DL_last = temp;
				}else{
				    NPDCCH_info->node->next->prev = temp;
                }
                
                
				temp->next = NPDCCH_info->node->next;
				temp->prev = NPDCCH_info->node;
				NPDCCH_info->node->next = temp;

				temp->start_subframe = NPDCCH_info->sf_end+1;
				temp->end_subframe = NPDCCH_info->node->end_subframe;

				NPDCCH_info->node->end_subframe = NPDCCH_info->sf_start - 1;
				break;
			case 1:
				//	keep one node
				if(align_left)
				{
					NPDCCH_info->node->start_subframe = NPDCCH_info->sf_end+1;
				}
				else
				{
					NPDCCH_info->node->end_subframe = NPDCCH_info->sf_start-1 ;
				}
				
				break;
			case 2:
				//	delete
				
				//  process next node element.
				if(NPDCCH_info->node->prev==NULL){
				    //  first node of list
                    available_resource_DL = NPDCCH_info->node->next;
                }else{
                    NPDCCH_info->node->prev->next = NPDCCH_info->node->next;
                }
                
                //  process prev node element.
                if(NPDCCH_info->node->next!=NULL){
                    NPDCCH_info->node->next->prev = NPDCCH_info->node->prev;
                }else{
                    //  end node of list
                    available_resource_DL_last = NPDCCH_info->node->prev;
                }
                
				free(NPDCCH_info->node);
				break;
			default:
				//error
				break;
      free(NPDCCH_info);
		}
	}
	else
	{
    if(NPDCCH_info->node==NPDSCH_info->node)
		{
			flag_same=1;
			LOG_D(MAC,"[%04d][maintain_resource_DL] NPDCCH and NPDSCH using the same node\n", mac_inst->current_subframe);
		}
		/****Maintain NPDCCH node*******/
		//	divided into two node
		//	keep one node(align left or right)
		//	delete node

		convert_system_number(NPDCCH_info->node->start_subframe, &H_temp, &f_temp, &sf_temp);
		//align_left = (calculate_DLSF(mac_inst, NPDCCH_info->node->start_subframe, NPDCCH_info->sf_start) == 0);
		align_left=(cal_num_dlsf(mac_inst, H_temp, f_temp, sf_temp, &H_temp_r, &f_temp_r, &sf_temp_r, 1)==NPDCCH_info->sf_start);
		align_right = ((calculate_DLSF(mac_inst, NPDCCH_info->sf_end, NPDCCH_info->node->end_subframe) == 0)||(NPDCCH_info->sf_end==NPDCCH_info->node->end_subframe));
		//LOG_D(MAC,"[maintain_resource_DL] align left %d align right %d\n", align_left, align_right);
		switch(align_left+align_right)
		{
			case 0:
				//  divided into two nodes, insert after oritinal node.
				//	A | node | B
		      	//	A | node | temp | B
				
				temp = (available_resource_DL_t *)malloc(sizeof(available_resource_DL_t));
				
				if((available_resource_DL_t *)0 == NPDCCH_info->node->next){
					available_resource_DL_last = temp;
				}else{
				    NPDCCH_info->node->next->prev = temp;
                }
				
				temp->next = NPDCCH_info->node->next;
				temp->prev = NPDCCH_info->node;
				NPDCCH_info->node->next = temp;

				temp->start_subframe = NPDCCH_info->sf_end+1;
				temp->end_subframe = NPDCCH_info->node->end_subframe;

				NPDCCH_info->node->end_subframe = NPDCCH_info->sf_start - 1;
				if(flag_same==1)
				{
					NPDSCH_info->node = temp;
				}
				break;
			case 1:
				//	keep one node
				if(align_left)
				{
					NPDCCH_info->node->start_subframe = NPDCCH_info->sf_end+1;
					LOG_D(MAC,"[%04d][maintain_resource_DL] NPDCCH keep one node\n", mac_inst->current_subframe);
				}
				else
				{
					NPDCCH_info->node->end_subframe = NPDCCH_info->sf_start-1 ;
				}
				break;
			case 2:
				//	delete
				LOG_D(MAC,"[%04d][maintain_resource_DL] NPDCCH delete node\n", mac_inst->current_subframe);
				
				//	calvin add
				//	delete
				
				//  process next node element.
				if(NPDCCH_info->node->prev==NULL){
				    //  first node of list
                    available_resource_DL = NPDCCH_info->node->next;
                }else{
                    NPDCCH_info->node->prev->next = NPDCCH_info->node->next;
                }
                
                //  process prev node element.
                if(NPDCCH_info->node->next!=NULL){
                    NPDCCH_info->node->next->prev = NPDCCH_info->node->prev;
                }else{
                    //  end node of list
                    available_resource_DL_last = NPDCCH_info->node->prev;
                }
                
				free(NPDCCH_info->node);
				break;
			default:
				//error
				break;
		}
		/****Maintain NPDSCH node*******/
		align_left = (calculate_DLSF(mac_inst, NPDSCH_info->node->start_subframe, NPDSCH_info->sf_start) == 0);
		align_right = ((calculate_DLSF(mac_inst, NPDSCH_info->sf_end, NPDSCH_info->node->end_subframe) == 0)||(NPDSCH_info->sf_end==NPDSCH_info->node->end_subframe));
		switch(align_left+align_right)
		{
			case 0:
				//  divided into two nodes, insert after oritinal node.
				//	A | node | B
		      	//	A | node | temp | B
				
				temp = (available_resource_DL_t *)malloc(sizeof(available_resource_DL_t));
				
				if((available_resource_DL_t *)0 == NPDSCH_info->node->next){
					available_resource_DL_last = temp;
				}else{
				    NPDSCH_info->node->next->prev = temp;
                }
				
				temp->next = NPDSCH_info->node->next;
				temp->prev = NPDSCH_info->node;
				NPDSCH_info->node->next = temp;

				temp->start_subframe = NPDSCH_info->sf_end+1;
				temp->end_subframe = NPDSCH_info->node->end_subframe;

				NPDSCH_info->node->end_subframe = NPDSCH_info->sf_start - 1;

				break;
			case 1:
				//	keep one node
				if(align_left)
				{
					NPDSCH_info->node->start_subframe = NPDSCH_info->sf_end+1;
				}
				else
				{
					NPDSCH_info->node->end_subframe = NPDSCH_info->sf_start-1 ;
				}
				break;
			case 2:
				//	delete
				
				if(NPDSCH_info->node->prev==NULL){
				    //  first node of list
                    available_resource_DL = NPDSCH_info->node->next;
                }else{
                    NPDSCH_info->node->prev->next = NPDSCH_info->node->next;
                }
                
                //  process prev node element.
                if(NPDSCH_info->node->next!=NULL){
                    NPDSCH_info->node->next->prev = NPDSCH_info->node->prev;
                }else{
                    //  end node of list
                    available_resource_DL_last = NPDSCH_info->node->prev;
                }

				free(NPDSCH_info->node);
				break;
			default:
				//error
				break;
		}
		free(NPDCCH_info);
		free(NPDSCH_info);
	}

}
/*Get MCS index*/
uint32_t get_I_mcs(int CE_level)
{
	if(CE_level==0)
	{
		return 13;
	}
	else if(CE_level==1)
	{
		return 10;
	}
	else
	{
		return 4;
	}
}

uint32_t get_max_tbs(uint32_t I_tbs)
{
	//Eric
	return MAC_TBStable_NB_IoT[I_tbs][7]/8;
	//Eric
}

uint32_t get_tbs(uint32_t data_size, uint32_t I_tbs, uint32_t *I_sf)
{
	//Eric
	for((*I_sf)=0;(*I_sf)<8;++(*I_sf))
	{
		
		//LOG_D(MAC,"[get_tbs]TBS %d SF index %d\n", TBStable_NB_IoT[I_tbs][(*I_sf)], *I_sf);
		if(MAC_TBStable_NB_IoT[I_tbs][(*I_sf)]>=data_size*8)
		{
			return MAC_TBStable_NB_IoT[I_tbs][(*I_sf)]/8;
		}
	}
	LOG_D(MAC,"error\n");
	return 0;
	//Eric
}

uint32_t get_num_sf(uint32_t I_sf)
{
	if(I_sf==6)
	{
		return 8;
	}
	else if(I_sf==7)
	{
		return 10;
	}
	else
	{
		return I_sf+1;
	}
}

/*Subcarrier_spacing 0:3.75kHz \ 1 : 15kHz*/
uint32_t get_HARQ_delay(int subcarrier_spacing, uint32_t HARQ_delay_index)
{
	if(subcarrier_spacing==1)
	{
		if(HARQ_delay_index==0)
			return 13;
		else if(HARQ_delay_index==1)
			return 15;
		else if(HARQ_delay_index==2)
			return 17;
		else
			return 18;
	}
	else
	{
		if((HARQ_delay_index==0)&&(HARQ_delay_index==1))
			return 13;
		else
			return 21;
	}
}

uint8_t get_index_Rep_dl(uint16_t R)
{
  int i;
  if(R<=128)
  {
    for(i=0;i<16;++i)
    {
      if(R==R_dl_table[i])
      {
        return i;
      }
    }
    LOG_D(MAC,"[get_index_Rep] error\n");
  }
  return 0;
}