/*! \file eNB_scheduler_ulsch_NB_IoT.c
 * \brief handle UL UE-specific scheduling
 * \author  NTUST BMW Lab./
 * \date 2017
 * \email: 
 * \version 1.0
 *
 */

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


unsigned char str20[] = "DCI_uss";
unsigned char str21[] = "DATA_uss";

// scheduling UL
int schedule_UL_NB_IoT(eNB_MAC_INST_NB_IoT *mac_inst,UE_TEMPLATE_NB_IoT *UE_info,uint32_t subframe, uint32_t frame, uint32_t H_SFN){

	int i,ndi = 0,check_DCI_result = 0,check_UL_result = 0,candidate;
	uint32_t DL_end;
    //Scheduling resource temp buffer
    sched_temp_DL_NB_IoT_t *NPDCCH_info = (sched_temp_DL_NB_IoT_t*)malloc(sizeof(sched_temp_DL_NB_IoT_t));

	candidate = UE_info->R_max/UE_info->R_dci;
    uint32_t mcs = max_mcs[UE_info->multi_tone];
    uint32_t mappedMcsIndex=UE_info->PHR+(4 * UE_info->multi_tone);
    int TBS = 0;
    int Iru = 0, Nru, I_rep,N_rep,total_ru;
    int dly = 0,uplink_time = 0;

    TBS=get_TBS_UL_NB_IoT(mcs,UE_info->multi_tone,Iru);

    sched_temp_UL_NB_IoT_t *NPUSCH_info = (sched_temp_UL_NB_IoT_t*)malloc(sizeof(sched_temp_UL_NB_IoT_t));

    DCIFormatN0_t *DCI_N0 = (DCIFormatN0_t*)malloc(sizeof(DCIFormatN0_t));

    //available_resource_DL_t *node;

    // setting of the NDI
    if(UE_info->HARQ_round == 0)
    {
        ndi = 1-UE_info->oldNDI_UL;
        UE_info->oldNDI_UL=ndi;
    }

    for (i = 0; i < candidate; i++)
	{
		/*step 1 : Check DL resource is available for DCI N0 or not*/
		check_DCI_result = check_resource_NPDCCH_NB_IoT(mac_inst,H_SFN, frame, subframe, NPDCCH_info, i, UE_info->R_dci);

        //node = check_resource_DL(mac_inst,);

        //just use to check when there is no DL function
        //NPDCCH_info->sf_start = H_SFN*10240+frame*10 +subframe + i * UE_info->R_dci;
        //NPDCCH_info->sf_end = NPDCCH_info->sf_start + (i+1) * UE_info->R_dci;

        //DEBUG("UE : %5d, NPDCCH result: %d ,NPDCCH start: %d,NPDCCH end : %d\n",UE_info->rnti,check_DCI_result,NPDCCH_info->sf_start,NPDCCH_info->sf_end);
        if( check_DCI_result != -1)
		{
			/*step 2 : Determine MCS / TBS / REP / RU number*/
            /*while((mapped_mcs[UE_info->CE_level][mappedMcsIndex]< mcs)||((TBS>UE_info->ul_total_buffer)&&(mcs>=0)))
                {
                    --mcs;
                    TBS=get_TBS_UL(mcs,UE_info->multi_tone,Iru);
                }*/

            mcs = mapped_mcs[UE_info->CE_level][mappedMcsIndex];

            while((TBS<UE_info->ul_total_buffer)&&(Iru<=7))
                {
                    Iru++;
                    TBS=get_TBS_UL_NB_IoT(mcs,UE_info->multi_tone,Iru);
                }

            //DEBUG("TBS : %d UL_buffer: %d\n", TBS, UE_info->ul_total_buffer);

            Nru = RU_table[Iru];
            DL_end = NPDCCH_info->sf_end;
            N_rep = get_N_REP(UE_info->CE_level);
            I_rep = get_I_REP(N_rep);
            total_ru = Nru * N_rep;

            printf("[%04d][UL scheduler][UE:%05d] Multi-tone:%d,MCS:%d,TBS:%d,UL_buffer:%d,DL_start:%d,DL_end:%d,N_rep:%d,N_ru:%d,Total_ru:%d\n", mac_inst->current_subframe,UE_info->rnti,UE_info->multi_tone,mcs,TBS,UE_info->ul_total_buffer,NPDCCH_info->sf_start,DL_end,N_rep,Nru,total_ru);

            /*step 3 Check UL resource for Uplink data*/
			// we will loop the scheduling delay here
            for(dly=0;dly<4;dly++)
            {
                uplink_time = DL_end +scheduling_delay[dly];
                check_UL_result = Check_UL_resource(uplink_time,total_ru, NPUSCH_info, UE_info->multi_tone, 0);
                if (check_UL_result != -1)
                {
                    // step 4 : generate DCI content
                    DCI_N0->type = 0;
                    DCI_N0->scind = NPUSCH_info->subcarrier_indication;
                    DCI_N0->ResAssign = Iru;
                    DCI_N0->mcs = mcs;
                    DCI_N0->ndi = ndi;
                    DCI_N0->Scheddly = dly;
                    DCI_N0->RepNum = I_rep;
                    DCI_N0->rv = (UE_info->HARQ_round%2==0)?0:1; // rv will loop 0 & 2
                    DCI_N0->DCIRep = get_DCI_REP(UE_info->R_dci,UE_info->R_max);

                printf("[%04d][UL scheduler][UE:%05d] DCI content = scind : %d ResAssign : %d mcs : %d ndi : %d scheddly : %d RepNum : %d rv : %d DCIRep : %d\n", mac_inst->current_subframe,UE_info->rnti,DCI_N0->scind,DCI_N0->ResAssign,DCI_N0->mcs,DCI_N0->ndi,DCI_N0->Scheddly,DCI_N0->RepNum,DCI_N0->rv,DCI_N0->DCIRep);
                // step 5 resource allocation and generate scheduling result
                generate_scheduling_result_UL(NPDCCH_info->sf_start, NPDCCH_info->sf_end,NPUSCH_info->sf_start, NPUSCH_info->sf_end,DCI_N0,UE_info->rnti, str20, str21);
                //fill_resource_DL();
                maintain_resource_DL(mac_inst,NPDCCH_info,NULL);

                adjust_UL_resource_list(NPUSCH_info);

                return 0;
                }
            }

		}
        /*break now, we only loop one candidiate*/
        //break;
	}

	printf("[%04d][UL scheduler][UE:%05d] there is no available UL resource\n", mac_inst->current_subframe, UE_info->rnti);
	return -1;
}

int single_tone_ru_allocation(uint32_t uplink_time, int total_ru, sched_temp_UL_NB_IoT_t *NPUSCH_info, int fmt2_flag)
{
    available_resource_UL_t *single_node_tmp;
    uint32_t uplink_time_end;

    if(fmt2_flag == 0)
        // 16 * 0.5 (slot) = 8 subframe
        uplink_time_end = uplink_time + total_ru*8 -1;
    else
        // 4 * 0.5 (slot) = 2 subframe
        uplink_time_end = uplink_time + total_ru*2 -1;

    //check first list of single tone
    single_node_tmp = available_resource_UL->singletone1_Head;

    while(single_node_tmp!=NULL)
    {
        if (uplink_time >= single_node_tmp->start_subframe)
        {
            
            if ( uplink_time_end <= single_node_tmp->end_subframe)
            {
                NPUSCH_info->sf_end = uplink_time_end;
                NPUSCH_info->sf_start = uplink_time;
                NPUSCH_info->tone = singletone1;
                NPUSCH_info->subcarrier_indication = 0 ; // Isc when single tone : 0-2
                NPUSCH_info->node = single_node_tmp;
                printf("[UL scheduler] Use uplink resource single tone 1, sf_start: %d, sf_end: %d\n",NPUSCH_info->sf_start,NPUSCH_info->sf_end);
                return 0;
            }
        }
        single_node_tmp = single_node_tmp->next;
    }

    //check second list of single tone
    single_node_tmp = available_resource_UL->singletone2_Head;

    while(single_node_tmp!=NULL)
    {
        if (uplink_time >= single_node_tmp->start_subframe)
        {
            if ( uplink_time_end <= single_node_tmp->end_subframe)
            {
                NPUSCH_info->sf_end = uplink_time_end;
                NPUSCH_info->sf_start = uplink_time;
                NPUSCH_info->tone = singletone2;
                NPUSCH_info->subcarrier_indication = 1 ; // Isc when single tone : 0-2
                NPUSCH_info->node = single_node_tmp;
                printf("[UL scheduler] Use uplink resource single tone 2, sf_start: %d, sf_end: %d\n",NPUSCH_info->sf_start,NPUSCH_info->sf_end);
                return 0;
            }
        }
        single_node_tmp = single_node_tmp->next;
    }

    //check third list of single tone
    single_node_tmp = available_resource_UL->singletone3_Head;

    while(single_node_tmp!=NULL)
    {
        if (uplink_time >= single_node_tmp->start_subframe)
        {
            if ( uplink_time_end <= single_node_tmp->end_subframe)
            {
                NPUSCH_info->sf_end = uplink_time_end;
                NPUSCH_info->sf_start = uplink_time;
                NPUSCH_info->tone = singletone3;
                NPUSCH_info->subcarrier_indication = 2 ; // Isc when single tone : 0-2
                NPUSCH_info->node = single_node_tmp;
                printf("[UL scheduler]Use uplink resource single tone 3, sf_start: %d, sf_end: %d\n",NPUSCH_info->sf_start,NPUSCH_info->sf_end);
                return 0;
            }
        }
        single_node_tmp = single_node_tmp->next;
    }

    //DEBUG("[UL scheduler][singletone]no proper resource for this allocation\n");
    return -1;

}

int multi_tone_ru_allocation(uint32_t uplink_time, int total_ru, sched_temp_UL_NB_IoT_t *NPUSCH_info)
{
    available_resource_UL_t *Next_Node;
    int single_tone_result = -1;
    uint32_t uplink_time_end;
    /*This checking order may result in the different of the resource optimization*/
    /*check 6 tones first*/
    Next_Node = available_resource_UL->sixtone_Head;

    // 4 * 0.5 (slot) = 2 subframe
    uplink_time_end = uplink_time + total_ru*2 -1;
    while(Next_Node!=NULL)
    {
        if (uplink_time >= Next_Node->start_subframe)
        {
            if ( uplink_time_end <= Next_Node->end_subframe)
            {
                NPUSCH_info->sf_end = uplink_time_end;
                NPUSCH_info->sf_start = uplink_time;
                NPUSCH_info->tone = sixtone;
                NPUSCH_info->subcarrier_indication = 17 ; // Isc when 6 tone : 6 - 12
                NPUSCH_info->node = Next_Node;
                printf("[UL scheduler] Use uplink resource six tone, sf_start: %d, sf_end: %d\n",NPUSCH_info->sf_start,NPUSCH_info->sf_end);
                return 0;
            }
        }
        Next_Node = Next_Node->next;
    }

    /*check 3 tones*/
    Next_Node = available_resource_UL->threetone_Head;
    // 8 * 0.5 (slot) = 4 subframe
    uplink_time_end = uplink_time + total_ru * 4 -1;
    while(Next_Node!=NULL)
    {
        if (uplink_time >= Next_Node->start_subframe)
        {
            if ( uplink_time_end <= Next_Node->end_subframe)
            {
                NPUSCH_info->sf_end = uplink_time_end;
                NPUSCH_info->sf_start = uplink_time;
                NPUSCH_info->tone = threetone;
                NPUSCH_info->subcarrier_indication = 13 ; // Isc when 3 tone : 3-5
                NPUSCH_info->node = Next_Node;
                printf("[UL scheduler] Use uplink resource three tone, sf_start: %d, sf_end: %d\n",NPUSCH_info->sf_start,NPUSCH_info->sf_end);
                return 0;
            }
        }
        Next_Node = Next_Node->next;
    }

    /*if there is no multi-tone resource, try to allocate the single tone resource*/
    single_tone_result = single_tone_ru_allocation(uplink_time,total_ru,NPUSCH_info,0);
    if(single_tone_result == 0)
        return 0;

    //DEBUG("[UL scheduler][multi_tone]there is no available UL resource !\n");
    return -1;
}

int Check_UL_resource(uint32_t uplink_time, int total_ru, sched_temp_UL_NB_IoT_t *NPUSCH_info, int multi_tone, int fmt2_flag)
{

    int result =-1;
    if(fmt2_flag ==0)
    {
        if(multi_tone == 1)
            result = multi_tone_ru_allocation(uplink_time, total_ru, NPUSCH_info);
        else if(multi_tone == 0)
           result = single_tone_ru_allocation(uplink_time, total_ru, NPUSCH_info,0);

    }else if (fmt2_flag == 1)
        {
            result = single_tone_ru_allocation(uplink_time, total_ru, NPUSCH_info, 1);
            printf("harq result %d, time:%d total ru:%d\n", result, uplink_time, total_ru);
            //if(result == 0)
                    //NPUSCH_info->ACK_NACK_resource_field = get_resource_field_value(NPUSCH_info->subcarrier_indication,ack_nack_delay[i]);
                    //DEBUG("[UL scheduler] There is available resource for ACK / NACK\n");
        }
    if(result == 0)
    {
        return 0;
    }
    //DEBUG("[UL scheduler] no available UL resource\n");
    return -1;
}


void generate_scheduling_result_UL(int32_t DCI_subframe, int32_t DCI_end_subframe, uint32_t UL_subframe, uint32_t UL_end_subframe, DCIFormatN0_t *DCI_pdu, rnti_t rnti, uint8_t *ul_debug_str, uint8_t *dl_debug_str){

    // create the schedule result node for this time transmission
    schedule_result_t *UL_result = (schedule_result_t*)malloc(sizeof(schedule_result_t));
    schedule_result_t *DL_result;
    schedule_result_t *tmp1, *tmp;

	UL_result->direction = UL;
    UL_result->output_subframe = UL_subframe;
	UL_result->end_subframe = UL_end_subframe;
    UL_result->DCI_pdu = DCI_pdu;
    UL_result->npusch_format = 0;
    UL_result->DCI_release = 1;
    UL_result->channel = NPUSCH;
    UL_result->rnti = rnti;
    UL_result->next = NULL;
	UL_result->debug_str = ul_debug_str;
	
    if(-1 == DCI_subframe){
        printf("[UL scheduler][UE:%05d] UL_result = output subframe : %d\n", rnti, UL_result->output_subframe);

    }else{
        DL_result = (schedule_result_t*)malloc(sizeof(schedule_result_t));

        DL_result->output_subframe = DCI_subframe;
        DL_result->end_subframe = DCI_end_subframe;
		DL_result->DCI_pdu = DCI_pdu;
        DL_result->DCI_release = 0;
        DL_result->direction = UL;
        DL_result->channel = NPDCCH;
        DL_result->rnti = rnti;
        DL_result->next = NULL;
		DL_result->debug_str = dl_debug_str;
		
	insert_schedule_result(&schedule_result_list_DL, DCI_subframe, DL_result);
        
        printf("[UL scheduler][UE:%05d] DL_result = output subframe : %d UL_result = output subframe : %d\n", rnti, DL_result->output_subframe,UL_result->output_subframe);
    }

    tmp1 = NULL;

    // be the first node of UL
    if(schedule_result_list_UL == NULL)
    {
        //schedule_result_list_UL = (schedule_result_t*)malloc(sizeof(schedule_result_t));
        schedule_result_list_UL = UL_result;
    }else
    {
        tmp = schedule_result_list_UL;
				while(tmp!=NULL)
				{
					if(UL_subframe < tmp->output_subframe)
					{
						break;
					}
					tmp1 = tmp;
					tmp = tmp->next;
				}
				if(tmp==NULL)
				{
					tmp1->next = UL_result;
				}
				else
				{
					UL_result->next = tmp;
					if(tmp1){
						tmp1->next = UL_result;
					}else{
						schedule_result_list_UL = UL_result;
					}
				}
    }

}

void adjust_UL_resource_list(sched_temp_UL_NB_IoT_t *NPUSCH_info)
{
	available_resource_UL_t *temp;
	available_resource_UL_t *node = NPUSCH_info->node;
	//	divided into two node
	//	keep one node(align left or right)
	//	delete node
	int align_left = (node->start_subframe==NPUSCH_info->sf_start);
	int align_right = (node->end_subframe==NPUSCH_info->sf_end);

	switch(align_left+align_right){
		case 0:
			//	divided into two node
			temp = (available_resource_UL_t *)malloc(sizeof(available_resource_UL_t));

			temp->next = node->next;
			node->next = temp;

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

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

			break;
		case 1:
			//	keep one node
			if(align_left){
				node->start_subframe = NPUSCH_info->sf_end +1;
			}else{
				node->end_subframe = NPUSCH_info->sf_start - 1 ;
			}
			break;
		case 2:
			//	delete
			node->prev->next = node->next;
			node->next->prev = node->prev;
			free(node);
			break;
		default:
			//error
			break;
	}

//	free(NPUSCH_info);
}