/*
 * 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 procedures related to eNB for the DLSCH transport channel
 * \author  TaiwanTech
 * \date 2017
 * \email: navid.nikaein@eurecom.fr
 * \version 1.0
 * @ingroup _mac

 */

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

void 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)
{
	//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));
	//DCI N1
	DCIFormatN1_t *DCI_N1 = (DCIFormatN1_t*)malloc(sizeof(DCIFormatN1_t));
	//RLC Status
	//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];
	logical_chan_id_t logical_channel;

	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_NB_IoT(UE_info->CE_level);
	I_tbs = I_mcs;
	//get max TBS
	TBS = get_max_tbs(I_tbs);
	printf("[%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(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;
	}
	/*Retransmission*/
	else
	{
		data_size = UE_info->DLSCH_pdu_size;
		flag_retransmission = 1;
		if((UE_info->HARQ_round>0)&&(TBS<data_size))
		{
			printf("[%04d][DLSchedulerUSS][Fail] TBS is not enough for retransmission\n", mac_inst->current_subframe);
			return;
		}
	}
	printf("[%04d][DLSchedulerUSS] UE data size %d\n", mac_inst->current_subframe, data_size);
	//Have DCCH data
	if(data_size == 0)
	{
		printf("[%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);
		printf("[%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);
  printf("[%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);
	/*Loop all NPDCCH candidate position*/
	for(cdd_num=0;cdd_num<UE_info->R_max/UE_info->R_dci;++cdd_num)
	{
		//DEBUG("[%04d][DLSchedulerUSS] Candidate num %d DCI Rep %d\n", cdd_num, UE_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)
		{
		  //DEBUG("[%04d][DLSchedulerUSS] Candidate num %d allocate success\n", cdd_num);
			//DEBUG("[%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);
				DEBUG("[%04d][DLSchedulerUSS] TBS change to %d because data size is smaller than previous TBS\n", mac_inst->current_subframe, TBS);
			}
			*/
			//Get number of subframe this UE need per repetition
			n_sf = get_num_sf(I_sf);
			//DEBUG("[%04d][DLSchedulerUSS] Number SF %d index SF %d\n", n_sf, I_sf);
			//DEBUG("[%04d][DLSchedulerUSS] Require total %d DL SF Rep %d\n", n_sf*UE_info->R_dl, UE_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_info->R_dl, n_sf);
		          //Have available resource
		          /*Check HARQ resource*/
		          if(end_flagSCH!=-1)
		          {
		            //DEBUG("[%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));
		            //DEBUG("[%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)
		            {
		              //DEBUG("[%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_info->R_harq, HARQ_info, 0, 1);
		              if(end_flagHARQ!=-1)
		              {
		                //DEBUG("[%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);
		                  logical_channel=DCCH0_NB_IoT;
		                  //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);
		                  UE_info->DLSCH_pdu.pdu_size=TBS;
		                }
		                //SCHEDULE_LOG("[%04d][DLSchedulerUSS][Success] RNTI %d complete scheduling\n", mac_inst->current_subframe, UE_info->rnti);
		                //SCHEDULE_LOG("[%04d][DLSchedulerUSS] RNTI %d\n", mac_inst->current_subframe, UE_info->rnti);
		                //SCHEDULE_LOG("[%04d][DLSchedulerUSS][Success] 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);
		                //SCHEDULE_LOG("[%04d][DLSchedulerUSS][Success] Scheduling delay index: %d value: %d + 4\n", mac_inst->current_subframe, I_delay, get_scheduling_delay(I_delay, UE_info->R_max));
		                //SCHEDULE_LOG("[%04d][DLSchedulerUSS][Success] Allocate NPDSCH subframe %d to subframe %d\n", mac_inst->current_subframe, NPDSCH_info->sf_start, NPDSCH_info->sf_end);
		                //SCHEDULE_LOG("[%04d][DLSchedulerUSS][Success] Allocate HARQ feedback subframe %d to subframe %d\n", mac_inst->current_subframe, HARQ_info->sf_start, HARQ_info->sf_end);
		                //DEBUG("[%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);
		                //DEBUG("[%04d][DLSchedulerUSS] Scheduling delay index: %d value: %d + 4\n", mac_inst->current_subframe, I_delay, get_scheduling_delay(I_delay, UE_info->R_max));
		                //DEBUG("[%04d][DLSchedulerUSS] Allocate NPDSCH subframe %d to subframe %d\n", mac_inst->current_subframe, NPDSCH_info->sf_start, NPDSCH_info->sf_end);
		                //DEBUG("[%04d][DLSchedulerUSS] Allocate HARQ feedback subframe %d to subframe %d\n", mac_inst->current_subframe, 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);
		                //DEBUG("[%04d][DLSchedulerUSS] HARQ index %d\n", HARQ_info->ACK_NACK_resource_field);
		                printf("[%04d][DLSchedulerUSS] 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, DCI_N1->type, DCI_N1->orderIndicator, DCI_N1->mcs, DCI_N1->HARQackRes, DCI_N1->RepNum, DCI_N1->ResAssign, DCI_N1->Scheddly, DCI_N1->DCIRep);
		                //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);
		                //DEBUG("[%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)
		                {
		                  DEBUG("[%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);
		                printf("[%04d][DLSchedulerUSS] Complete DL scheduling\n", mac_inst->current_subframe);
		                

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

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

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

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);
	//DEBUG("[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);
	//DEBUG("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);
	}
	//DEBUG("check_resource_DL_NB_IoT flag 1\n");
	/*initialize*/
	schedule_info->node = available_resource_DL;
	//DEBUG("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
		//DEBUG("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);
		}
		//DEBUG("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;
	}
	//DEBUG("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;
	SCH_SUBHEADER_FIXED_NB_IoT *mac_header=(SCH_SUBHEADER_FIXED_NB_IoT*)pdu;
	uint32_t offset=0;

	for(i=0;i<num_sdu;++i)
	{
		printf("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)
	{
		printf("[ERROR]TBS less than require subheader and control element\n");
		return -1;
	}
	printf("total SDU size %d\n", total_sdu_size);
	printf("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++;
            printf("last sdu\n");
        }
        else
        {
            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;
            }
        }
	}
	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, uint32_t scheddly, uint32_t I_sf, uint32_t I_harq)
{
	DCI_N1->type=1;
	DCI_N1->orderIndicator = 0;
	DCI_N1->Scheddly = scheddly;
	DCI_N1->ResAssign = I_sf;
	DCI_N1->mcs = UE_info->I_mcs_dl;
	DCI_N1->RepNum = get_index_Rep_dl(UE_info->R_dl);
	DCI_N1->HARQackRes = I_harq;
	//DCI_N1->DCIRep = 3-UE_info->R_max/UE_info->R_dci/2;
	DCI_N1->DCIRep = UE_info->R_dci;
}

void generate_scheduling_result_DL(uint32_t DCI_subframe, uint32_t NPDSCH_subframe, uint32_t HARQ_subframe, 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 = DCI_subframe;
	NPDCCH_result->sdu_length = TBS;
	NPDCCH_result->direction = 1;
	NPDCCH_result->rnti_type = 3;
	NPDCCH_result->DLSCH_pdu = NULL;
	NPDCCH_result->DCI_pdu = DCI_pdu;
	NPDCCH_result->DCI_release = 0;
	NPDCCH_result->channel = NPDCCH;
	NPDCCH_result->next = NULL;
	/*fill NPDSCH result*/
	NPDSCH_result->rnti=rnti;
	NPDSCH_result->output_subframe = NPDSCH_subframe;
	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 = DCI_pdu;
	NPDSCH_result->DCI_release = 0;
	NPDSCH_result->channel = NPDSCH;
	NPDSCH_result->next = NULL;
	/*fill HARQ result*/
	HARQ_result->rnti=rnti;
	HARQ_result->output_subframe = HARQ_subframe;
	HARQ_result->sdu_length = 0;
	HARQ_result->direction = 1;
	HARQ_result->rnti_type = 3;
	HARQ_result->DLSCH_pdu = NULL;
	HARQ_result->DCI_pdu = DCI_pdu;
	HARQ_result->DCI_release = 1;
	HARQ_result->channel = NPUSCH;
	HARQ_result->npusch_format = 1;
	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;
		printf("[generate_scheduling_result_DL] Generate NPDCCH node at head\n");
	}
	else
	{
		tmp = schedule_result_list_DL;
		while(tmp!=NULL)
		{
			if(DCI_subframe < 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_subframe < 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
	
	tmp1 = NULL;
	if(schedule_result_list_UL == NULL)
	{
	  //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_subframe < 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)
		{
			tmp1->next = HARQ_result;
		}
		else
		{
			HARQ_result->next = tmp;
			if(tmp1)
			{
				tmp1->next = HARQ_result;
			}else
			{
				schedule_result_list_UL = HARQ_result;
			}
		}
	}
	/*
	DEBUG("---[generate_scheduling_result_DL] schedule result after generate---\n");
	tmp = schedule_result_list_DL;
	while(tmp!=NULL)
	{
		DEBUG("[generate_scheduling_result_DL] node output subframe %d\n", tmp->output_subframe);
		tmp = tmp->next;
	}
	*/
}

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);
		//DEBUG("[maintain_resource_DL] align left %d align right %d\n", align_left, align_right);
		switch(align_left+align_right)
		{
			case 0:
				//	divided into two node
				temp = (available_resource_DL_t *)malloc(sizeof(available_resource_DL_t));
				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
				if(NPDCCH_info->node->prev==NULL)
        {
          available_resource_DL = NPDCCH_info->node->next;
        }
        else
        {
          NPDCCH_info->node->prev->next = NPDCCH_info->node->next;
          if(NPDCCH_info->node->next!=NULL)
          {
            NPDCCH_info->node->next->prev = 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;
			printf("[%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));
		//DEBUG("[maintain_resource_DL] align left %d align right %d\n", align_left, align_right);
		switch(align_left+align_right)
		{
			case 0:
				//	divided into two node
				temp = (available_resource_DL_t *)malloc(sizeof(available_resource_DL_t));
				
				//	calvin added
				if((available_resource_DL_t *)0 == NPDCCH_info->node->next){
					available_resource_DL_last = 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;
					printf("[%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
				printf("[%04d][maintain_resource_DL] NPDCCH delete node\n", mac_inst->current_subframe);
				
				//	calvin add
				if((available_resource_DL_t *)0 == NPDCCH_info->node->next){
					available_resource_DL_last = NPDCCH_info->node->prev;
				}
				
        if(NPDCCH_info->node->prev==NULL)
        {
          available_resource_DL = NPDCCH_info->node->next;
        }
        else
        {
          NPDCCH_info->node->prev->next = NPDCCH_info->node->next;
          if(NPDCCH_info->node->next!=NULL)
          {
            NPDCCH_info->node->next->prev = 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 node
				temp = (available_resource_DL_t *)malloc(sizeof(available_resource_DL_t));
				
				//	calvin added
				if((available_resource_DL_t *)0 == NPDSCH_info->node->next){
					available_resource_DL_last = 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
				
				//	calvin added
				if((available_resource_DL_t *)0 == NPDSCH_info->node->next){
					available_resource_DL_last = NPDSCH_info->node->prev;
				}
				
				if(NPDSCH_info->node->prev==NULL)
        {
          available_resource_DL = NPDSCH_info->node->next;
        }
        else
        {
          NPDSCH_info->node->prev->next = NPDSCH_info->node->next;
          if(NPDSCH_info->node->next!=NULL)
          {
            NPDSCH_info->node->next->prev = NPDSCH_info->node->prev;
          }
        }
				free(NPDSCH_info->node);
				break;
			default:
				//error
				break;
		}
		free(NPDCCH_info);
		free(NPDSCH_info);
	}
}

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;
      }
    }
    printf("[get_index_Rep] error\n");
  }
  return -1;
}