/*! \file schedule_tool_NB_IoT.c
 * \brief scheduler helper function
 * \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"

void print_available_UL_resource(void){

    int sixtone_num=0;
    int threetone_num=0;
    int singletone1_num=0;
    int singletone2_num=0;
    int singletone3_num=0;

    available_resource_UL_t *available_resource;

    ///sixtone
    available_resource = available_resource_UL->sixtone_Head;

    while(available_resource!=NULL)
    {
        sixtone_num++;
        LOG_D(MAC,"[sixtone][Node %d] start %d , end %d\n",sixtone_num,available_resource->start_subframe,available_resource->end_subframe);
        available_resource = available_resource->next;
    }

    ///threetone
    available_resource = available_resource_UL->threetone_Head;

    while(available_resource!=NULL)
    {
        threetone_num++;
        LOG_D(MAC,"[threetone][Node %d] start %d, end %d\n",threetone_num,available_resource->start_subframe,available_resource->end_subframe);
        available_resource = available_resource->next;
    }

    ///singletone1
    available_resource = available_resource_UL->singletone1_Head;

    while(available_resource!=NULL)
    {
        singletone1_num++;
        LOG_D(MAC,"[singletone1][Node %d] start %d, end %d\n",singletone1_num,available_resource->start_subframe,available_resource->end_subframe);
        available_resource = available_resource->next;
    }

    ///singletone1
    available_resource = available_resource_UL->singletone2_Head;

    while(available_resource!=NULL)
    {
        singletone2_num++;
        LOG_D(MAC,"[singletone2][Node %d] start %d, end %d\n",singletone2_num,available_resource->start_subframe,available_resource->end_subframe);
        available_resource = available_resource->next;
    }

    ///singletone1
    available_resource = available_resource_UL->singletone3_Head;

    while(available_resource!=NULL)
    {
        singletone3_num++;
        LOG_D(MAC,"[singletone3][Node %d] start %d, end %d\n",singletone3_num,available_resource->start_subframe,available_resource->end_subframe);
        available_resource = available_resource->next;
    }

}

void print_scheduling_result_UL(void)
{
    schedule_result_t *scheduling_result_tmp;

    scheduling_result_tmp = schedule_result_list_DL;

    while(scheduling_result_tmp!=NULL)
    {
        LOG_D(MAC,"[UE:%05d][%s] output subframe : %d\n", scheduling_result_tmp->rnti, ((scheduling_result_tmp->channel==NPDCCH)? "NPDCCH":"NPDSCH"), scheduling_result_tmp->output_subframe);
        scheduling_result_tmp = scheduling_result_tmp->next;
    }

    scheduling_result_tmp = schedule_result_list_UL;

    while(scheduling_result_tmp!=NULL)
    {
        LOG_D(MAC,"[UE:%05d][NPUSCH] output subframe : %d\n", scheduling_result_tmp->rnti, scheduling_result_tmp->output_subframe);
        scheduling_result_tmp = scheduling_result_tmp->next;
    }

}


void setting_nprach(){

    nprach_list[0].nprach_Periodicity = rachperiod[4];
    nprach_list[0].nprach_StartTime = rachstart[0];
    nprach_list[0].nprach_SubcarrierOffset = rachscofst[0];
    nprach_list[0].nprach_NumSubcarriers = rachnumsc[0];
    nprach_list[0].numRepetitionsPerPreambleAttempt = rachrepeat[1];

    nprach_list[1].nprach_Periodicity = rachperiod[4];
    nprach_list[1].nprach_StartTime = rachstart[0];
    nprach_list[1].nprach_SubcarrierOffset = rachscofst[1];
    nprach_list[1].nprach_NumSubcarriers = rachnumsc[0];
    nprach_list[1].numRepetitionsPerPreambleAttempt = rachrepeat[3];

    nprach_list[2].nprach_Periodicity = rachperiod[4];
    nprach_list[2].nprach_StartTime = rachstart[0];
    nprach_list[2].nprach_SubcarrierOffset = rachscofst[2];
    nprach_list[2].nprach_NumSubcarriers = rachnumsc[1];
    nprach_list[2].numRepetitionsPerPreambleAttempt = rachrepeat[5];

    // fixed nprach configuration
}

void Initialize_Resource_node(available_resource_UL_t *tone_head, available_resource_UL_t *npusch_frame, int tone)
{
    
    int i=0;
    available_resource_UL_t *second_node;

    second_node = (available_resource_UL_t*)malloc(sizeof(available_resource_UL_t));

    if(tone == sixtone)
        i=2;
    else if(tone == threetone)
        i=1;
    else
        i=0;

    tone_head->start_subframe = ceil ( (nprach_list+i)->nprach_StartTime + 1.4*4*((nprach_list+i)->numRepetitionsPerPreambleAttempt) ) ;
    tone_head->end_subframe = (nprach_list+i)->nprach_StartTime-1 + (nprach_list+i)->nprach_Periodicity;

    second_node->start_subframe = tone_head->start_subframe + (nprach_list+i)->nprach_Periodicity;
    second_node->end_subframe = tone_head->end_subframe + (nprach_list+i)->nprach_Periodicity;
    second_node->next =NULL;
    tone_head->next = second_node;
    *npusch_frame = *tone_head->next;
    
////////////////////////CALVIN TIMING DIAGRAM GENERATOR///////////////////////////
#ifdef TIMING_GENERATOR
    uint32_t ii, jj;
    for(ii=(nprach_list+i)->nprach_StartTime; ii<tone_head->start_subframe; ++ii){
        if(ii == sim_end_time) break;
        for(jj=0; jj<(nprach_list+i)->nprach_NumSubcarriers; ++jj){
            ul_scheduled(ii, 0, (nprach_list+i)->nprach_SubcarrierOffset + jj, _NPRACH, 0, (char *)0);
        }
    }
    for(ii=tone_head->end_subframe+1; ii<second_node->start_subframe; ++ii){
        if(ii == sim_end_time) break;
        for(jj=0; jj<(nprach_list+i)->nprach_NumSubcarriers; ++jj){
            ul_scheduled(ii, 0, (nprach_list+i)->nprach_SubcarrierOffset + jj, _NPRACH, 0, (char *)0);
        }
    }
#endif
////////////////////////CALVIN TIMING DIAGRAM GENERATOR///////////////////////////
}

/*when there is SIB-2 configuration coming to MAC, filled the uplink resource grid*/
void Initialize_Resource(void){
    int i;
    available_resource_UL_t *new_node;
    ///memory allocate to Head
    available_resource_UL = (available_resource_tones_UL_t*)malloc(sizeof(available_resource_tones_UL_t));

    available_resource_UL->sixtone_Head = (available_resource_UL_t *)0;
    available_resource_UL->threetone_Head = (available_resource_UL_t *)0;
    available_resource_UL->singletone1_Head = (available_resource_UL_t *)0;
    available_resource_UL->singletone2_Head = (available_resource_UL_t *)0;
    available_resource_UL->singletone3_Head = (available_resource_UL_t *)0;

    available_resource_UL->sixtone_end_subframe = 0;
    available_resource_UL->threetone_end_subframe = 0;
    available_resource_UL->singletone1_end_subframe = 0;
    available_resource_UL->singletone2_end_subframe = 0;
    available_resource_UL->singletone3_end_subframe = 0;
    //initialize first node
    if((nprach_list+2)->nprach_StartTime!=0)
    {
        new_node = (available_resource_UL_t *)malloc(sizeof(available_resource_UL_t));
        new_node->next = (available_resource_UL_t *)0;
        new_node->prev = (available_resource_UL_t *)0;
        new_node->start_subframe = 0;
        new_node->end_subframe = (nprach_list+2)->nprach_StartTime-1;

        if( (available_resource_UL_t *)0 == available_resource_UL->sixtone_Head){
            available_resource_UL->sixtone_Head = new_node;
            new_node->prev = (available_resource_UL_t *)0;
        }
    }
    if((nprach_list+1)->nprach_StartTime!=0)
    {
        new_node = (available_resource_UL_t *)malloc(sizeof(available_resource_UL_t));
        new_node->next = (available_resource_UL_t *)0;
        new_node->prev = (available_resource_UL_t *)0;
        new_node->start_subframe = 0;
        new_node->end_subframe = (nprach_list+1)->nprach_StartTime-1;

        if( (available_resource_UL_t *)0 == available_resource_UL->threetone_Head){
            available_resource_UL->threetone_Head = new_node;
            new_node->prev = (available_resource_UL_t *)0;
        }
    }
    for(i=0;i<3;++i)
    {
        if(nprach_list->nprach_StartTime!=0)
        {
            new_node = (available_resource_UL_t *)malloc(sizeof(available_resource_UL_t));
            new_node->next = (available_resource_UL_t *)0;
            new_node->prev = (available_resource_UL_t *)0;
            new_node->start_subframe = 0;
            new_node->end_subframe = nprach_list->nprach_StartTime-1;

            if( (available_resource_UL_t *)0 == available_resource_UL->threetone_Head){
                if(i==0)
                    available_resource_UL->singletone1_Head = new_node;
                else if(i==1)
                    available_resource_UL->singletone2_Head = new_node;
                else
                    available_resource_UL->singletone3_Head = new_node;
                new_node->prev = (available_resource_UL_t *)0;
            }
        }
    }
    add_UL_Resource();
    add_UL_Resource();
    
    LOG_D(MAC,"Initialization of the UL Resource grid has been done\n");
}


void add_UL_Resource_node(available_resource_UL_t **head, uint32_t *end_subframe, uint32_t ce_level){
    available_resource_UL_t *new_node, *iterator;
    new_node = (available_resource_UL_t *)malloc(sizeof(available_resource_UL_t));
    
    new_node->next = (available_resource_UL_t *)0;

    new_node->prev = (available_resource_UL_t *)0;
    
    new_node->start_subframe = *end_subframe + ceil( (nprach_list+ce_level)->nprach_StartTime + 1.4*4*((nprach_list+ce_level)->numRepetitionsPerPreambleAttempt) ) ;
    
    new_node->end_subframe = *end_subframe + (nprach_list+ce_level)->nprach_Periodicity - 1;
    
    if( (available_resource_UL_t *)0 == *head){
        *head = new_node;
        new_node->prev = (available_resource_UL_t *)0;
    }else{
        iterator = *head;
        while( (available_resource_UL_t *)0 != iterator->next){
            iterator = iterator->next;
        }
        iterator->next = new_node;
        new_node->prev = iterator;
    }
    
////////////////////////CALVIN TIMING DIAGRAM GENERATOR///////////////////////////
#ifdef TIMING_GENERATOR
    uint32_t ii, jj;
    for(ii=*end_subframe+(nprach_list+ce_level)->nprach_StartTime; ii<new_node->start_subframe; ++ii){
        if(ii >= sim_end_time) break;
        for(jj=0; jj<(nprach_list+ce_level)->nprach_NumSubcarriers; ++jj){
            ul_scheduled(ii, 0, (nprach_list+ce_level)->nprach_SubcarrierOffset + jj, _NPRACH, 0, (char *)0);
        }
    }
#endif
////////////////////////CALVIN TIMING DIAGRAM GENERATOR///////////////////////////

    *end_subframe += (nprach_list+ce_level)->nprach_Periodicity;
}

/// Use to extend the UL resource grid (5 list) at the end of nprach peroid time
void add_UL_Resource(void)
{   
    add_UL_Resource_node(&available_resource_UL->sixtone_Head, &available_resource_UL->sixtone_end_subframe, 2);
    add_UL_Resource_node(&available_resource_UL->threetone_Head, &available_resource_UL->threetone_end_subframe, 1);
    add_UL_Resource_node(&available_resource_UL->singletone1_Head, &available_resource_UL->singletone1_end_subframe, 0);
    add_UL_Resource_node(&available_resource_UL->singletone2_Head, &available_resource_UL->singletone2_end_subframe, 0);
    add_UL_Resource_node(&available_resource_UL->singletone3_Head, &available_resource_UL->singletone3_end_subframe, 0);
}

int get_I_TBS_NB_IoT(int x,int y)
{
    int I_TBS = 0;
    if(y==1) I_TBS=x;
    else
    {
        if(x==1)    I_TBS=2;
        else if(x==2)   I_TBS=1;
        else
        {
            I_TBS=x;
        }
    }
    return I_TBS;
}

int get_TBS_UL_NB_IoT(uint32_t mcs,uint32_t multi_tone,int Iru)
{
    int TBS;
    uint32_t I_TBS=get_I_TBS_NB_IoT(mcs,multi_tone);
    TBS=UL_TBS_Table[I_TBS][Iru];
    return TBS>>3;
}
int get_N_REP(int CE_level)
{
    int N_rep= 0;
    if(CE_level == 0)
    {
        N_rep = (nprach_list)->numRepetitionsPerPreambleAttempt;
    }else if (CE_level == 1)
    {
        N_rep = (nprach_list+1)->numRepetitionsPerPreambleAttempt;
    }else if (CE_level == 2)
    {
        //N_rep = (nprach_list+2)->numRepetitionsPerPreambleAttempt;
        N_rep = 1;
    }else
    {
        LOG_D(MAC,"unknown CE level!\n");
        return -1;
    }

    return N_rep;
}

int get_I_REP(int N_rep)
{
    int i;
    for(i = 0; i < 8;i++)
        {
            if(N_rep == rachrepeat[i])
                return i;
        }
    LOG_D(MAC,"unknown repetition value!\n");
    return -1;
}

int get_DCI_REP(uint32_t R,uint32_t R_max)
{
    int value = -1;
    if (R_max == 1)
    {
        if(R == 1)
        {
            value =0;
        }

    }else if (R_max == 2)
    {
        if(R == 1)
            value = 0;
        if(R == 2)
            value = 1;
     }else if (R_max == 4)
     {
        if(R == 1)
            value = 0;
        if(R == 2)
            value = 1;
        if(R == 4)
            value = 2;
     }else if (R_max >= 8)
     {
        if(R == R_max/8)
            value = 0;
        if(R == R_max/4)
            value = 1;
        if(R == R_max/2)
            value = 2;
        if(R == R_max)
            value = 3;
     }
     return value;
}

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;
                LOG_D(MAC,"[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;
                LOG_D(MAC,"[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;
                LOG_D(MAC,"[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;
    }

    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;
                LOG_D(MAC,"[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;
                LOG_D(MAC,"[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;

    return -1;
}

int get_resource_field_value(int subcarrier, int k0)
{
    int value = 0;
    if (k0 == 13)
        value = subcarrier;
    else if (k0 == 15)
        value = subcarrier + 4;
    else if (k0 == 17)
        value = subcarrier + 8;
    else if (k0 == 18)
        value = subcarrier + 12;

    return value;
}

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);
            LOG_D(MAC,"harq result %d, time:%d total ru:%d\n", result, uplink_time, total_ru);
        }
    if(result == 0)
    {
        return 0;
    }
    return -1;
}

void insert_schedule_result(schedule_result_t **list, int subframe, schedule_result_t *node){
    schedule_result_t *tmp, *tmp1;
    if((schedule_result_t *)0 == *list){
            *list = node;
        }else{
            tmp = *list;
            tmp1 = (schedule_result_t *)0;
            while((schedule_result_t *)0 != tmp){
                if(subframe < tmp->output_subframe){
                    break;
                }
                tmp1 = tmp;
                tmp = tmp->next;
            }
            if((schedule_result_t *)0 == tmp){
                tmp1->next = node;
            }else{
        node->next = tmp;
        if(tmp1){
            tmp1->next = node;
        }else{
            *list = node;
        }
            }
        }
}

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_inst, rnti_t rnti, uint8_t *ul_printf_str, uint8_t *dl_printf_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_inst;
    UL_result->npusch_format = 0;
    UL_result->DCI_release = 1;
    UL_result->channel = NPUSCH;
    UL_result->rnti = rnti;
    UL_result->next = NULL;
    //UL_result->printf_str = ul_printf_str;
    
    if(-1 == DCI_subframe){
        LOG_D(MAC,"[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_inst;
        DL_result->DCI_release = 0;
        DL_result->direction = UL;
        DL_result->channel = NPDCCH;
        DL_result->rnti = rnti;
        DL_result->next = NULL;
        //DL_result->printf_str = dl_printf_str;
        
    insert_schedule_result(&schedule_result_list_DL, DCI_subframe, DL_result);
        
        LOG_D(MAC,"[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 = 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->prev = node;

            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:

            if(node!=NULL)
            {
                //  delete
                if(node->prev==(available_resource_UL_t *)0)
                {
                    if(NPUSCH_info->tone==sixtone)
                        available_resource_UL->sixtone_Head = node->next;
                    else if(NPUSCH_info->tone==threetone)
                        available_resource_UL->threetone_Head = node->next;
                    else if(NPUSCH_info->tone==singletone1)
                        available_resource_UL->singletone1_Head = node->next;
                    else if(NPUSCH_info->tone==singletone2)
                        available_resource_UL->singletone2_Head = node->next;
                    else if(NPUSCH_info->tone==singletone3)
                        available_resource_UL->singletone3_Head = node->next;
                }else{

                    node->prev->next = node->next;
                }

                if(node->next!=(available_resource_UL_t *)0)
                {
                    node->next->prev = node->prev;
                }else{
                    node->prev->next = (available_resource_UL_t *)0;
                }

                free(node);
                break;
            }
        default:
            //error
            break;
    }
}

void add_ue_NB_IoT(eNB_MAC_INST_NB_IoT *mac_inst, uint16_t rnti, ce_level_t ce, uint32_t PHR, uint32_t ul_total_buffer){
    int32_t i;
    UE_list_NB_IoT_t *UE_list = (mac_inst->UE_list_spec + (uint32_t)ce);
    for(i=0; i<MAX_NUMBER_OF_UE_MAX_NB_IoT; ++i){
        if(UE_list->UE_template_NB_IoT[i].active == 0){
            UE_list->UE_template_NB_IoT[i].active = 1;
            UE_list->UE_template_NB_IoT[i].rnti = rnti;
            UE_list->UE_template_NB_IoT[i].PHR = PHR;
            UE_list->UE_template_NB_IoT[i].ul_total_buffer = ul_total_buffer;
            //New UE setting start
            UE_list->UE_template_NB_IoT[i].R_dl = dl_rep[(uint32_t)ce];;
            UE_list->UE_template_NB_IoT[i].I_mcs_dl = 0;
            UE_list->UE_template_NB_IoT[i].CE_level = (uint32_t)ce;
            //assume random select direction
            UE_list->UE_template_NB_IoT[i].R_dci = dci_rep[(uint32_t)ce];
            UE_list->UE_template_NB_IoT[i].R_max = UE_list->NPDCCH_config_dedicated.R_max;
            //UE_list->UE_template_NB_IoT[i].R_max = 16;
            UE_list->UE_template_NB_IoT[i].R_harq = harq_rep[(uint32_t)ce];
            UE_list->UE_template_NB_IoT[i].HARQ_round = 0;
            UE_list->UE_template_NB_IoT[i].oldNDI_UL = 0;
            UE_list->UE_template_NB_IoT[i].oldNDI_DL = 0;

            UE_list->UE_template_NB_IoT[i].multi_tone = 0;
            //New UE setting ending
            UE_list->UE_template_NB_IoT[i].prev = -1;
            
            if(-1 == UE_list->head){
                UE_list->UE_template_NB_IoT[i].next = -1;
            }else{
                UE_list->UE_template_NB_IoT[i].next = UE_list->head;
            }
            UE_list->head = i;
            return ;
        }
    }
}

void remove_ue(eNB_MAC_INST_NB_IoT *mac_inst, uint16_t rnti, ce_level_t ce){
    int32_t i;
    UE_list_NB_IoT_t *UE_list = (mac_inst->UE_list_spec + (uint32_t)ce);
    for(i=0; i<MAX_NUMBER_OF_UE_MAX_NB_IoT; ++i){
        if(UE_list->UE_template_NB_IoT[i].active == 1 && UE_list->UE_template_NB_IoT[i].rnti == rnti){
            UE_list->UE_template_NB_IoT[i].active = 0;
            return ;
        }
    }
}

//Transfrom source into hyperSF, Frame, Subframe format
void convert_system_number(uint32_t source_sf,uint32_t *hyperSF, uint32_t *frame, uint32_t *subframe)
{
    if(source_sf>=1024*1024*10)
    {
        source_sf=source_sf%(1024*1024*10);
    }
    *hyperSF = (source_sf/10)/1024;
    *frame = (source_sf/10)%1024;
    *subframe = (source_sf%10240)%10;
}

//Trnasform hyperSF, Frame, Subframe format into subframe unit
uint32_t convert_system_number_sf(uint32_t hyperSF, uint32_t frame, uint32_t subframe)
{
    return hyperSF*1024*10+frame*10+subframe;
}

/*input start position amd num_dlsf DL subframe, caculate the last subframe number*/
uint32_t cal_num_dlsf(eNB_MAC_INST_NB_IoT *mac_inst, uint32_t hyperSF, uint32_t frame, uint32_t subframe, uint32_t* hyperSF_result, uint32_t* frame_result, uint32_t* subframe_result, uint32_t num_dlsf_require)
{
  uint16_t sf_dlsf_index;
  uint16_t dlsf_num_temp;
  uint32_t abs_sf_start = 0;
  uint32_t abs_sf_end = 0;
  uint8_t period_count=0;
  uint8_t shift_flag=0;
  uint8_t scale_flag=0;
  //uint8_t flag_printf=0;

  abs_sf_start=convert_system_number_sf(hyperSF, frame, subframe);
  sf_dlsf_index = abs_sf_start%2560%(mac_inst->sib1_period*10);
  dlsf_num_temp = DLSF_information.sf_to_dlsf_table[sf_dlsf_index];
  while(num_dlsf_require>DLSF_information.num_dlsf_per_period)
  {
    //flag_printf=1;
    period_count++;
    num_dlsf_require-=DLSF_information.num_dlsf_per_period;
  }
  abs_sf_end = abs_sf_start+period_count*mac_inst->sib1_period*10;
  //LOG_D(MAC,"[cal_num_dlsf]abs_sf_end %d after loop\n", abs_sf_end);
  if(num_dlsf_require>DLSF_information.num_dlsf_per_period-dlsf_num_temp+1)
  {
    if(is_dlsf(mac_inst, sf_dlsf_index)==1)
    {
      num_dlsf_require-=DLSF_information.num_dlsf_per_period-dlsf_num_temp+1;
    }
    else
    {
      num_dlsf_require-=DLSF_information.num_dlsf_per_period-dlsf_num_temp;
    }
    abs_sf_end+=mac_inst->sib1_period*10-abs_sf_end%(mac_inst->sib1_period*10);
    dlsf_num_temp = 0;
    scale_flag = 1;
  }
  
  if(num_dlsf_require!=0)
  {
    if(scale_flag!=1)
    {
      if(is_dlsf(mac_inst, abs_sf_end)==1)
      {
        shift_flag = 1;
      }
    }
    if(abs_sf_end%(mac_inst->sib1_period*10)!=0)
    {
      abs_sf_end-=abs_sf_end%(mac_inst->sib1_period*10);
      //LOG_D(MAC,"[cal_num_dlsf] abs_sf_end is %d mod period =  %d\n", abs_sf_end, abs_sf_end%(mac_inst->sib1_NB_IoT_sched_config.sib1_period*10));
    }
    if(shift_flag==1)
    {
      abs_sf_end +=DLSF_information.dlsf_to_sf_table[dlsf_num_temp+num_dlsf_require-2];
    }
    else
    {
      abs_sf_end +=DLSF_information.dlsf_to_sf_table[dlsf_num_temp+num_dlsf_require-1];
    }

    }
  convert_system_number(abs_sf_end, hyperSF_result, frame_result, subframe_result);
  return abs_sf_end;
}


void init_dlsf_info(eNB_MAC_INST_NB_IoT *mac_inst, DLSF_INFO_t *DLSF_info)
{
  uint16_t dlsf_num_temp=0;
  uint16_t i;
  uint16_t j=0;

  DLSF_info->sf_to_dlsf_table=(uint16_t*)malloc(mac_inst->sib1_period*10*sizeof(uint16_t));
  for(i=0;i<mac_inst->sib1_period*10;++i)
  {
    if(is_dlsf(mac_inst, i)==1)
    {
      dlsf_num_temp++;
      DLSF_info->sf_to_dlsf_table[i]=dlsf_num_temp;
    }
    else
    {
      DLSF_info->sf_to_dlsf_table[i]=dlsf_num_temp;
    }
  }
  DLSF_info->num_dlsf_per_period = dlsf_num_temp;
  DLSF_info->dlsf_to_sf_table = (uint16_t*)malloc(dlsf_num_temp*sizeof(uint16_t));
  for(i=0;i<mac_inst->sib1_period*10;++i)
  {
    if(is_dlsf(mac_inst, i)==1)
    {
      DLSF_info->dlsf_to_sf_table[j]= i;
      j++;
    }
  }
}

void init_tool_sib1(eNB_MAC_INST_NB_IoT *mac_inst){
    int i, j;

    //int repetition_pattern = 1;// 1:every2frame, 2:every4frame, 3:every8frame, 4:every16frame
    for(i=0;i<8;++i){
        mac_inst->sib1_flag[(i<<1)+mac_inst->rrc_config.sib1_NB_IoT_sched_config.starting_rf] = 1;
    }

    for(i=0, j=0;i<64;++i){
        if(mac_inst->sib1_flag[i]==1){
            ++j;
        }
        mac_inst->sib1_count[i]=j;
    }

    mac_inst->sib1_period = 256 / mac_inst->rrc_config.sib1_NB_IoT_sched_config.repetitions;

    return ;
}

uint32_t calculate_DLSF(eNB_MAC_INST_NB_IoT *mac_inst, int abs_start_subframe, int abs_end_subframe){   //LOG_D(MAC,"calcu %p %d %d\n", mac_inst, abs_start_subframe, abs_end_subframe);
    int i;
    int num_dlsf=0;
    //int diff_subframe = abs_end_subframe - abs_start_subframe;

    int start_frame = abs_start_subframe / 10;
    int end_frame = abs_end_subframe / 10;
    int start_subframe = abs_start_subframe % 10;
    int end_subframe = abs_end_subframe % 10;

    int start_frame_mod_64 = start_frame & 0x0000003f;
    int end_frame_mod_64 = end_frame & 0x0000003f;
    int start_frame_div_64 = (start_frame & 0xffffffc0)>>6;
    int end_frame_div_64 = (end_frame & 0xffffffc0)>>6;

    if(abs_start_subframe > abs_end_subframe){
        return calculate_DLSF(mac_inst, abs_start_subframe, (MAX_FRAME*10)+9) + calculate_DLSF(mac_inst, 0, abs_end_subframe);
    }
    if(start_frame_div_64==end_frame_div_64 && start_frame==end_frame){
        for(i=abs_start_subframe;i<=abs_end_subframe;++i){
            num_dlsf += is_dlsf(mac_inst, i);
        }
    }else{
        num_dlsf = mac_inst->dlsf_table[end_frame_mod_64];
        num_dlsf -= (start_frame_mod_64==0)?0:mac_inst->dlsf_table[start_frame_mod_64-1];
        for(i=0;i<start_subframe;++i, --abs_start_subframe){
            num_dlsf -= is_dlsf(mac_inst, abs_start_subframe-1);
        }
        for(i=end_subframe;i<9;++i, ++abs_end_subframe){
            num_dlsf -= is_dlsf(mac_inst, abs_end_subframe+1);
        }
        if(start_frame_div_64!=end_frame_div_64){
            num_dlsf+= (472+(end_frame_div_64-start_frame_div_64-1)*472);
        }
    }
    return num_dlsf;
}


int is_dlsf(eNB_MAC_INST_NB_IoT *mac_inst, int abs_subframe){
    int frame = abs_subframe/10;
    int subframe = abs_subframe%10;

    return !(subframe==0||subframe==5||((frame&0x1)==0&&subframe==9)||(mac_inst->sib1_flag[frame%mac_inst->sib1_period]==1&&subframe==4));
}

void init_dl_list(eNB_MAC_INST_NB_IoT *mac_inst){
    available_resource_DL_t *node;

    node = (available_resource_DL_t *)malloc(sizeof(available_resource_DL_t));
    node->next = (available_resource_DL_t *)0;
    node->prev = (available_resource_DL_t *)0;

    available_resource_DL = node;
    available_resource_DL_last = node;

    node->start_subframe = 0;
    node->end_subframe = 0;
    mac_inst->schedule_subframe_DL = 0;

    //node->end_subframe = mac_inst->rrc_config.si_window_length;
    //mac_inst->schedule_subframe_DL = mac_inst->rrc_config.si_window_length;

    //  init sibs for first si-window
    //schedule_sibs(mac_inst, 0, 0);    //  TODO, check init
}

#if 1
//  extend subframe align to si-period
void extend_available_resource_DL(eNB_MAC_INST_NB_IoT *mac_inst, int max_subframe){

    available_resource_DL_t *new_node;
    uint32_t i, i_div_si_window;
    
    LOG_D(MAC,"[extend DL] max_subframe: %d, current schedule subframe: %d\n", max_subframe, mac_inst->schedule_subframe_DL);
    print_available_resource_DL(mac_inst);
    
    if(max_subframe > mac_inst->schedule_subframe_DL){
        //  align to si-period

        max_subframe = ((max_subframe%mac_inst->rrc_config.si_window_length)==0)? max_subframe : (((max_subframe/mac_inst->rrc_config.si_window_length)+1)*mac_inst->rrc_config.si_window_length);
        
        if(mac_inst->schedule_subframe_DL == available_resource_DL_last->end_subframe){
            LOG_D(MAC,"[extend DL] last node is align to schedule_sf_dl\n");

            available_resource_DL_last->end_subframe = max_subframe;
        }else{
            
            LOG_D(MAC,"[extend DL] add new node !\n");
            new_node = (available_resource_DL_t *)malloc(sizeof(available_resource_DL_t));
            new_node->prev= available_resource_DL_last;
            available_resource_DL_last->next = new_node;
            new_node->start_subframe = mac_inst->schedule_subframe_DL+1;
            new_node->end_subframe = max_subframe;
            new_node->next = (available_resource_DL_t *)0;
            available_resource_DL_last = new_node;
        }
        
        //  do schedule sibs after extend.
        for(i=mac_inst->schedule_subframe_DL; i<max_subframe; i+=mac_inst->rrc_config.si_window_length){
            
            i_div_si_window = (i / mac_inst->rrc_config.si_window_length)%256;
            if(-1 != mac_inst->sibs_table[i_div_si_window]){
                LOG_D(MAC,"[sibs%d] %d\n", mac_inst->sibs_table[i_div_si_window], i + (mac_inst->rrc_config.si_radio_frame_offset*10));
                schedule_sibs(mac_inst, mac_inst->sibs_table[i_div_si_window], i + (mac_inst->rrc_config.si_radio_frame_offset*10));    //  add si-radio-frame-offset carried in SIB1
            }
        }

        mac_inst->schedule_subframe_DL = max_subframe;
    }
}
#endif
#if 0
//  extend subframe align to si-period
void extend_available_resource_DL(eNB_MAC_INST_NB_IoT *mac_inst, int max_subframe){ //  assume max_subframe is found.
    

    available_resource_DL_t *new_node;
    //int temp;
    uint32_t i, i_div_si_window;
    //uint32_t si_period_div_window;
    //pt = available_resource_DL;
    
    LOG_D(MAC,"[extend DL] max_subframe: %d, current schedule subframe: %d\n", max_subframe, mac_inst->schedule_subframe_DL);
    print_available_resource_DL(mac_inst);
    
    if(max_subframe > mac_inst->schedule_subframe_DL){
        //  align to si-period

        max_subframe = ((max_subframe%mac_inst->rrc_config.si_window_length)==0)? max_subframe : (((max_subframe/mac_inst->rrc_config.si_window_length)+1)*mac_inst->rrc_config.si_window_length);
        
        if(mac_inst->schedule_subframe_DL == available_resource_DL_last->end_subframe){
            LOG_D(MAC,"[extend DL] last node is align to schedule_sf_dl\n");

            available_resource_DL_last->end_subframe = max_subframe;
        }else{
            
            LOG_D(MAC,"[extend DL] add new node !\n");
            new_node = (available_resource_DL_t *)malloc(sizeof(available_resource_DL_t));

            available_resource_DL_last->next = new_node;
            new_node->start_subframe = mac_inst->schedule_subframe_DL+1;
            new_node->end_subframe = max_subframe;
            new_node->next = (available_resource_DL_t *)0;
            available_resource_DL_last = new_node;
        }
        
        LOG_D(MAC,"sf_dl:%d max:%d siw:%d\n",mac_inst->schedule_subframe_DL,max_subframe,mac_inst->rrc_config.si_window_length);

        //  do schedule sibs after extend.
        for(i=mac_inst->schedule_subframe_DL;i<max_subframe;i+=mac_inst->rrc_config.si_window_length){
            
            i_div_si_window = (i / mac_inst->rrc_config.si_window_length)%256;
                LOG_D(MAC,"[sibs out:%d] schedule_DL:%d i_div_si_window:%d\n", mac_inst->sibs_table[i_div_si_window], i, i_div_si_window);
            if(-1 != mac_inst->sibs_table[i_div_si_window]){
                LOG_D(MAC,"[sibs%d] %d\n", mac_inst->sibs_table[i_div_si_window], i);
                schedule_sibs(mac_inst, mac_inst->sibs_table[i_div_si_window], i);
            }
        }

        mac_inst->schedule_subframe_DL = max_subframe;
    }
    
    return ;
}
#endif
void maintain_available_resource(eNB_MAC_INST_NB_IoT *mac_inst){

    available_resource_DL_t *pfree, *iterator;
    available_resource_UL_t *pfree2, *iterator2;
    schedule_result_t *iterator1;
    if(available_resource_DL != (available_resource_DL_t *)0){
    LOG_D(MAC,"[maintain]current:%d, end:%d\n",mac_inst->current_subframe,available_resource_DL->end_subframe);
    if(mac_inst->current_subframe >= available_resource_DL->end_subframe){
        pfree = available_resource_DL;

        if(available_resource_DL->next == (available_resource_DL_t *)0){
            LOG_D(MAC,"[maintain_available_resource]=====t:%d=====dl resource list next is NULL %d\n", mac_inst->current_subframe, available_resource_DL->end_subframe);
            available_resource_DL = (available_resource_DL_t *)0;
        }else{
            LOG_D(MAC,"[maintain_available_resource]=====t:%d=====dl resource list remove next:%d-%d\n", mac_inst->current_subframe, available_resource_DL->next->start_subframe, available_resource_DL->next->end_subframe);
            available_resource_DL = available_resource_DL->next;
            available_resource_DL->prev = (available_resource_DL_t *)0;
        }
        free((available_resource_DL_t *)pfree);
        
    }else{
        // only update when current subframe bigger than to start subframe
        if(mac_inst->current_subframe > available_resource_DL->start_subframe)
        {
            LOG_D(MAC,"[maintain] update from %d to current %d, ori end %d\n",available_resource_DL->start_subframe,mac_inst->current_subframe,available_resource_DL->end_subframe);
            available_resource_DL->start_subframe = mac_inst->current_subframe;
        }else
            LOG_D(MAC,"[maintain] do nothing\n");
    }
}
    //  UL 
    iterator2 = available_resource_UL->singletone1_Head;
    if(iterator2 != (available_resource_UL_t *)0){
    if(mac_inst->current_subframe > iterator2->end_subframe){
        pfree2 = iterator2;

        available_resource_UL->singletone1_Head = iterator2->next;
        available_resource_UL->singletone1_Head->prev = (available_resource_UL_t *)0;
        
        free((available_resource_UL_t *)pfree2);
        
    }else{
        if(iterator2->start_subframe<mac_inst->current_subframe)
        iterator2->start_subframe = mac_inst->current_subframe;
    }
}
    iterator2 = available_resource_UL->singletone2_Head;
    if(iterator2 != (available_resource_UL_t *)0){
    if(mac_inst->current_subframe > iterator2->end_subframe){
        pfree2 = iterator2;

        available_resource_UL->singletone2_Head = iterator2->next;
        available_resource_UL->singletone2_Head->prev = (available_resource_UL_t *)0;
        
        free((available_resource_UL_t *)pfree2);
        
    }else{
        if(iterator2->start_subframe<mac_inst->current_subframe)
        iterator2->start_subframe = mac_inst->current_subframe;
    }
}
    iterator2 = available_resource_UL->singletone3_Head;
    if(iterator2 != (available_resource_UL_t *)0){
    if(mac_inst->current_subframe > iterator2->end_subframe){
        pfree2 = iterator2;

        available_resource_UL->singletone3_Head = iterator2->next;
        available_resource_UL->singletone3_Head->prev = (available_resource_UL_t *)0;
        
        free((available_resource_UL_t *)pfree2);
        
    }else{
        if(iterator2->start_subframe<mac_inst->current_subframe)
        iterator2->start_subframe = mac_inst->current_subframe;
    }
}
    iterator2 = available_resource_UL->sixtone_Head;
    if(iterator2 != (available_resource_UL_t *)0){
    if(mac_inst->current_subframe > iterator2->end_subframe){
        pfree2 = iterator2;

        available_resource_UL->sixtone_Head = iterator2->next;
        available_resource_UL->sixtone_Head->prev = (available_resource_UL_t *)0;
        
        free((available_resource_UL_t *)pfree2);
        
    }
    else{
        if(iterator2->start_subframe<mac_inst->current_subframe)
        iterator2->start_subframe = mac_inst->current_subframe;
    }
}
    iterator2 = available_resource_UL->threetone_Head;
    if(iterator2 != (available_resource_UL_t *)0){
    if(mac_inst->current_subframe > iterator2->end_subframe){
        pfree2 = iterator2;

        available_resource_UL->threetone_Head = iterator2->next;
        available_resource_UL->threetone_Head->prev = (available_resource_UL_t *)0;
        
        free((available_resource_UL_t *)pfree2);
        
    }else{
        if(iterator2->start_subframe<mac_inst->current_subframe)
        iterator2->start_subframe = mac_inst->current_subframe;
    }
 }   
    if(mac_inst->current_subframe == 0){
        //  DL available cross zero
        iterator = available_resource_DL;
        while(iterator != (available_resource_DL_t *)0){
            if(iterator->start_subframe >= MAX_SUBFRAME)
                iterator->start_subframe -= MAX_SUBFRAME;
            if(iterator->end_subframe >= MAX_SUBFRAME)
                iterator->end_subframe -= MAX_SUBFRAME;
            iterator = iterator->next;
        }
        if(mac_inst->schedule_subframe_DL >= MAX_SUBFRAME)
            mac_inst->schedule_subframe_DL -= MAX_SUBFRAME;
        //  UL available cross zero
        iterator2 = available_resource_UL->sixtone_Head;
        while(iterator2 != (available_resource_UL_t *)0){
            if(iterator2->start_subframe >= MAX_SUBFRAME)
                iterator2->start_subframe -= MAX_SUBFRAME;
            if(iterator2->end_subframe >= MAX_SUBFRAME)
                iterator2->end_subframe -= MAX_SUBFRAME;
            iterator2 = iterator2->next;
        }
        iterator2 = available_resource_UL->threetone_Head;
        while(iterator2 != (available_resource_UL_t *)0){
            if(iterator2->start_subframe >= MAX_SUBFRAME)
                iterator2->start_subframe -= MAX_SUBFRAME;
            if(iterator2->end_subframe >= MAX_SUBFRAME)
                iterator2->end_subframe -= MAX_SUBFRAME;
            iterator2 = iterator2->next;
        }
        iterator2 = available_resource_UL->singletone3_Head;
        while(iterator2 != (available_resource_UL_t *)0){
            if(iterator2->start_subframe >= MAX_SUBFRAME)
                iterator2->start_subframe -= MAX_SUBFRAME;
            if(iterator2->end_subframe >= MAX_SUBFRAME)
                iterator2->end_subframe -= MAX_SUBFRAME;
            iterator2 = iterator2->next;
        }
        iterator2 = available_resource_UL->singletone1_Head;
        while(iterator2 != (available_resource_UL_t *)0){
            if(iterator2->start_subframe >= MAX_SUBFRAME)
                iterator2->start_subframe -= MAX_SUBFRAME;
            if(iterator2->end_subframe >= MAX_SUBFRAME)
                iterator2->end_subframe -= MAX_SUBFRAME;
            iterator2 = iterator2->next;
        }
        iterator2 = available_resource_UL->singletone2_Head;
        while(iterator2 != (available_resource_UL_t *)0){
            if(iterator2->start_subframe >= MAX_SUBFRAME)
                iterator2->start_subframe -= MAX_SUBFRAME;
            if(iterator2->end_subframe >= MAX_SUBFRAME)
                iterator2->end_subframe -= MAX_SUBFRAME;
            iterator2 = iterator2->next;
        }
        
        if(available_resource_UL->singletone1_end_subframe >= MAX_SUBFRAME)
            available_resource_UL->singletone1_end_subframe -= MAX_SUBFRAME;
        if(available_resource_UL->singletone2_end_subframe >= MAX_SUBFRAME)
            available_resource_UL->singletone2_end_subframe -= MAX_SUBFRAME;
        if(available_resource_UL->singletone3_end_subframe >= MAX_SUBFRAME)
            available_resource_UL->singletone3_end_subframe -= MAX_SUBFRAME;
        if(available_resource_UL->sixtone_end_subframe >= MAX_SUBFRAME)
            available_resource_UL->sixtone_end_subframe -= MAX_SUBFRAME;
        if(available_resource_UL->threetone_end_subframe >= MAX_SUBFRAME)
            available_resource_UL->threetone_end_subframe -= MAX_SUBFRAME;
        //  DL result cross zero
        iterator1 = schedule_result_list_DL;
        while(iterator1 != (schedule_result_t *)0){
            if(iterator1->output_subframe >= MAX_SUBFRAME)
                iterator1->output_subframe -= MAX_SUBFRAME;
            if(iterator1->end_subframe >= MAX_SUBFRAME)
                iterator1->end_subframe -= MAX_SUBFRAME;
            iterator1 = iterator1->next;
        }
        //  UL result cross zero
        iterator1 = schedule_result_list_UL;
        while(iterator1 != (schedule_result_t *)0){
            if(iterator1->output_subframe >= MAX_SUBFRAME)
                iterator1->output_subframe -= MAX_SUBFRAME;
            if(iterator1->end_subframe >= MAX_SUBFRAME)
                iterator1->end_subframe -= MAX_SUBFRAME;
            iterator1 = iterator1->next;
        }
    }
    
    
    return ;
}

void fill_resource_DL(eNB_MAC_INST_NB_IoT *mac_inst, available_resource_DL_t *node, int start_subframe, int end_subframe, schedule_result_t *new_node){
    //printf_FUNCTION_IN("[FILL DL]");
    available_resource_DL_t *temp;
    schedule_result_t *iterator, *temp1;
    //  divided into two node
    //  keep one node(align left or right)
    //  delete node

//LOG_D(MAC,"fill dl test1\n"); 
    int align_left = (node->start_subframe==start_subframe)||(calculate_DLSF(mac_inst, node->start_subframe, start_subframe-1) == 0);
    int align_right = (end_subframe==node->end_subframe)||(calculate_DLSF(mac_inst, end_subframe+1, node->end_subframe) == 0);
//LOG_D(MAC,"fill dl test2\n");
    switch(align_left+align_right){
        case 0:
            //  divided into two node, always insert before original node, so won't happen that temp is the last node of the list.
            //  A | node | B
            //  A | temp | node | B
            LOG_D(MAC,"Case 0 [b], node : %p node_prev : %p\n",node,node->prev);
            temp = (available_resource_DL_t *)malloc(sizeof(available_resource_DL_t));
            if(node->prev){
                //LOG_I(MAC,"start_subframe : %d\n",node->prev->start_subframe);
                node->prev->next = temp;
            }else{
                available_resource_DL = temp;
            }
            
            temp->prev = node->prev;
            temp->next = node;
            node->prev = temp;
            
            temp->start_subframe = node->start_subframe;
            temp->end_subframe = start_subframe - 1;

            node->start_subframe = end_subframe + 1;
            LOG_D(MAC,"Case 0 [a], node : %p node_prev : %p\n",node,node->prev);
            break;
        case 1:
            LOG_D(MAC,"Case 1, node : %p node_prev : %p\n",node,node->prev);
            //  keep one node
            if(align_left){
                node->start_subframe = end_subframe + 1 ;
            }else{
                node->end_subframe = start_subframe - 1 ;
            }

            break;
        case 2:
            LOG_D(MAC,"Case 2 [b], node : %p node_prev : %p\n",node,node->prev);
            //  delete
            if(node->next){
                node->next->prev = node->prev;
            }else{
                available_resource_DL_last = node->prev;
            }
                
            if(node->prev){
                node->prev->next = node->next;
            }else{
                available_resource_DL = node->next;
            }
            LOG_D(MAC,"Case 2 [a], node : %p node_prev : %p\n",node,node->prev);

            free(node);
            break;
        default:
            //error
            break;
    }
    
    //  new node allocate from up-layer calling function.
    iterator = schedule_result_list_DL;
    temp1 = (schedule_result_t *)0;
    if((schedule_result_t *)0 == schedule_result_list_DL){
        schedule_result_list_DL = new_node;
    }else{
        while((schedule_result_t *)0 != iterator){
            if(start_subframe < iterator->output_subframe){
                break;
            }
            temp1 = iterator;
            iterator = iterator->next;
        }
        if((schedule_result_t *)0 == iterator){
            temp1->next = new_node;
        }else{
            new_node->next = iterator;
            if(temp1){
                temp1->next = new_node;
            }else{
                schedule_result_list_DL = new_node;
            }
        }
    }
    
    //printf_FUNCTION_OUT("[FILL DL]");
}

//  check_subframe must be DLSF, you can use is_dlsf() to check before call function
available_resource_DL_t *check_resource_DL(eNB_MAC_INST_NB_IoT *mac_inst, int check_subframe, int num_subframes, int *out_last_subframe, int *out_first_subframe){
    available_resource_DL_t *pt;
    pt = available_resource_DL;
    int end_subframe = check_subframe + num_subframes - 1;
    int diff_gap;

    while((available_resource_DL_t *)0 != pt){
        if(pt->start_subframe <= check_subframe && pt->end_subframe >= check_subframe){
            break;
        }
        pt = pt->next;
    }

    if((available_resource_DL_t *)0 == pt){
        return (available_resource_DL_t *)0;
    }else{
        if(num_subframes <= calculate_DLSF(mac_inst, check_subframe, pt->end_subframe)){

            diff_gap = num_subframes - calculate_DLSF(mac_inst, check_subframe, end_subframe);
            
            LOG_D(MAC,"Diff_gap : %d num_subframes : %d \n",diff_gap,num_subframes);

            while(diff_gap){
                ++end_subframe;
                if(is_dlsf(mac_inst, end_subframe)){
                    --diff_gap;
                }
            }

            *out_last_subframe = end_subframe;
            while(!is_dlsf(mac_inst, check_subframe)){
                ++check_subframe;
            }
            *out_first_subframe = check_subframe;
            return pt;
        }else{
            return (available_resource_DL_t *)0;
        }
    }
}

available_resource_DL_t *check_sibs_resource(eNB_MAC_INST_NB_IoT *mac_inst, int check_start_subframe, int check_end_subframe, int num_subframe, int *residual_subframe, int *out_last_subframe, int *out_first_subframe){
    available_resource_DL_t *pt;
    uint32_t num_dlsf;
    uint8_t output = 0x0;
    pt = available_resource_DL;
    
    //  TODO find the pt which can cover part of check_start_subframe, e.g. 1280-> 1281-1440
    while((available_resource_DL_t *)0 != pt){
        if(pt->start_subframe <= check_start_subframe && pt->end_subframe >= check_start_subframe){
            break;
        }
        pt = pt->next;
    }
    if((available_resource_DL_t *)0 == pt){
        return (available_resource_DL_t *)0;
    }
    
    num_dlsf = calculate_DLSF(mac_inst, check_start_subframe, pt->end_subframe);

    if((available_resource_DL_t *)0 == pt){
        return (available_resource_DL_t *)0;
    }else{

        if(num_subframe <= num_dlsf){

            while(num_subframe>0){
                if(is_dlsf(mac_inst, check_start_subframe)){
                    --num_subframe;
                    if(output == 0x0){
                        *out_first_subframe = check_start_subframe;
                        output = 0x1;
                    }
                }
                if(num_subframe==0||check_start_subframe>=check_end_subframe){
                    break;
                }else{
                    ++check_start_subframe;
                }
            }

            *residual_subframe = num_subframe;
            *out_last_subframe = check_start_subframe;

        }else{
            if(num_dlsf == 0){
                return (available_resource_DL_t *)0;
            }else{
                while(!is_dlsf(mac_inst, check_start_subframe)){
                    ++check_start_subframe;
                }
                *out_first_subframe = check_start_subframe;
            }
            *residual_subframe = num_subframe - num_dlsf;
            *out_last_subframe = pt->end_subframe;
        }
        return pt;
    }
}

void print_available_resource_DL(eNB_MAC_INST_NB_IoT *mac_inst){
    available_resource_DL_t *pt;
    pt = available_resource_DL;
    int i=0;
    LOG_D(MAC,"=== print available resource === t=%d\nsched subframe: %d, list end: %d-%d\n", mac_inst->current_subframe, mac_inst->schedule_subframe_DL, available_resource_DL_last->start_subframe, available_resource_DL_last->end_subframe);
    while(pt){
        LOG_D(MAC,"[%2d] %p %3d-%3d prev:%p\n", i, pt, pt->start_subframe, pt->end_subframe, pt->prev);
        pt = pt->next;
    }
    LOG_D(MAC,"\n");
}

void print_schedule_result(void){
    schedule_result_t *iterator_dl = schedule_result_list_DL;
    schedule_result_t *iterator_ul = schedule_result_list_UL;
    schedule_result_t *iterator;
    int i = 0;
    char str[20];
    char str1[20];
    char str2[20];
    LOG_D(MAC,"=== print schedule result ===\n");

    while((schedule_result_t *)0 != iterator_dl || (schedule_result_t *)0 != iterator_ul){
        if((schedule_result_t *)0 == iterator_dl){
            iterator = iterator_ul;
            iterator_ul = iterator_ul->next;
        }
        else if((schedule_result_t *)0 == iterator_ul){
            iterator = iterator_dl;
            iterator_dl = iterator_dl->next;
        }else{
            if(iterator_ul->output_subframe < iterator_dl->output_subframe){
                iterator = iterator_ul;
                iterator_ul = iterator_ul->next;
            }else{
                iterator = iterator_dl;
                iterator_dl = iterator_dl->next;
            }
        }


        if(iterator->rnti == P_RNTI){
            sprintf(str, " PAGING");
        }
        else if(iterator->rnti == SI_RNTI){
            sprintf(str, "SI-RNTI");
        }
        else if(iterator->rnti <= RA_RNTI_HIGH && iterator->rnti >= RA_RNTI_LOW){
            sprintf(str, "RA-RNTI");
        }else{
            sprintf(str, "UE%05d", iterator->rnti-C_RNTI_LOW);
        }

        if(iterator->direction == DL){
            sprintf(str1, "DL");
        }else{
            sprintf(str1, "UL");
        }

        switch(iterator->channel){
            case NPDCCH:
                sprintf(str2, "NPDCCH");
                break;
            case NPDSCH:
                sprintf(str2, "NPDSCH");
                break;
            case NPUSCH:
                sprintf(str2, "NPUSCH");
                break;
            default:
                break;
        }

        LOG_D(MAC,"[%2d][%s][%s][%s] output(%4d)\n", i++, str, str1, str2, iterator->output_subframe);
/*      if((uint8_t *)0 != iterator->printf_str){
            LOG_D(MAC," printf: %s\n", iterator->printf_str);
        }else{
            LOG_D(MAC,"\n");
        }*/
    }
}


void print_schedule_result_DL(void){
    schedule_result_t *iterator = schedule_result_list_DL;
    int i=0;
    char str[20];
    LOG_D(MAC,"=== print schedule result DL ===\n");
    while((schedule_result_t *)0 != iterator){
        if(iterator->rnti == P_RNTI){
            sprintf(str, " PAGE");
        }
        else if(iterator->rnti == SI_RNTI){
            sprintf(str, "   SI");
        }
        else if(iterator->rnti <= RA_RNTI_HIGH && iterator->rnti >= RA_RNTI_LOW){
            sprintf(str, "   RA");
        }else{
            sprintf(str, "UE%03d", iterator->rnti-C_RNTI_LOW);
        }
        LOG_D(MAC,"[%2d][%s][""DL""] output(%4d)\n", i++, str, iterator->output_subframe);
        /*if((uint8_t *)0 != iterator->printf_str){
            LOG_D(MAC," printf: %s\n", iterator->printf_str);
        }else{
            LOG_D(MAC,"\n");
        }*/
        iterator = iterator->next;
    }
}

void print_schedule_result_UL(void){
    schedule_result_t *iterator = schedule_result_list_UL;
    int i=0;

    char str[20];

    LOG_D(MAC,"=== print schedule result UL ===\n");
    while((schedule_result_t *)0 != iterator){

        sprintf(str, "UE%03d", iterator->rnti-C_RNTI_LOW);
        LOG_D(MAC,"[%2d][%s][""UL""] output(%4d)\n", i++, str, iterator->output_subframe);
        /*if((uint8_t *)0 != iterator->printf_str){
            LOG_D(MAC," printf: %s\tnext %p\n", iterator->printf_str, iterator->next);
        }else{
            LOG_D(MAC,"\n");
        }*/
        iterator = iterator->next;
    }
}

uint32_t get_scheduling_delay(uint32_t I_delay, uint32_t R_max)
{
    if(I_delay==0)
    {
        return 0;
    }
    else
    {
        if(R_max<128)
        {
            if(I_delay<=4)
                return 4*I_delay;
            else
                return (uint32_t)(2<<I_delay);
        }
        else
        {
            return (uint32_t)(16<<(I_delay-1));
        }
    }
}

/*
uint8_t *parse_ulsch_header( uint8_t *mac_header,
                             uint8_t *num_ce,
                             uint8_t *num_sdu,
                             uint8_t *rx_ces,
                             uint8_t *rx_lcids,
                             uint16_t *rx_lengths,
                             uint16_t tb_length ){

uint8_t not_done=1, num_ces=0, num_sdus=0, lcid,num_sdu_cnt;
uint8_t *mac_header_ptr = mac_header;
uint16_t length, ce_len=0;

    while(not_done==1){

        if(((SCH_SUBHEADER_FIXED_NB_IoT*)mac_header_ptr)->E == 0){
            not_done = 0;
        }

        lcid = ((SCH_SUBHEADER_FIXED_NB_IoT *)mac_header_ptr)->LCID;

        if(lcid < EXTENDED_POWER_HEADROOM){
            if (not_done==0) { // last MAC SDU, length is implicit
                mac_header_ptr++;
                length = tb_length-(mac_header_ptr-mac_header)-ce_len;

                for(num_sdu_cnt=0; num_sdu_cnt < num_sdus ; num_sdu_cnt++){
                    length -= rx_lengths[num_sdu_cnt];
                }
            }else{
                if(((SCH_SUBHEADER_SHORT_NB_IoT *)mac_header_ptr)->F == 0){
                    length = ((SCH_SUBHEADER_SHORT_NB_IoT *)mac_header_ptr)->L;
                    mac_header_ptr += 2;//sizeof(SCH_SUBHEADER_SHORT_NB_IoT);
                }else{ // F = 1
                    length = ((((SCH_SUBHEADER_LONG_NB_IoT *)mac_header_ptr)->L_MSB & 0x7f ) << 8 ) | (((SCH_SUBHEADER_LONG_NB_IoT *)mac_header_ptr)->L_LSB & 0xff);
                    mac_header_ptr += 3;//sizeof(SCH_SUBHEADER_LONG);
                }
            }

            rx_lcids[num_sdus] = lcid;
            rx_lengths[num_sdus] = length;
            num_sdus++;
        }else{ // This is a control element subheader POWER_HEADROOM, BSR and CRNTI
            if(lcid == SHORT_PADDING){
                mac_header_ptr++;
            }else{
                rx_ces[num_ces] = lcid;
                num_ces++;
                mac_header_ptr++;

                if(lcid==LONG_BSR){
                    ce_len+=3;
                }else if(lcid==CRNTI){
                    ce_len+=2;
                }else if((lcid==POWER_HEADROOM) || (lcid==TRUNCATED_BSR)|| (lcid== SHORT_BSR)) {
                    ce_len++;
                }else{
                    // wrong lcid
                }
            }
        }
    }

    *num_ce = num_ces;
    *num_sdu = num_sdus;

    return(mac_header_ptr);
}
*/

//  calvin
//  maybe we can try to use hash table to enhance searching time.
UE_TEMPLATE_NB_IoT *get_ue_from_rnti(eNB_MAC_INST_NB_IoT *inst, rnti_t rnti){
    uint32_t i;
    for(i=0; i<MAX_NUMBER_OF_UE_MAX_NB_IoT; ++i){
        if(inst->UE_list_spec->UE_template_NB_IoT[i].active == 1){
            if(inst->UE_list_spec->UE_template_NB_IoT[i].rnti == rnti){
                return &inst->UE_list_spec->UE_template_NB_IoT[i];
            }
        }
    }
    return (UE_TEMPLATE_NB_IoT *)0;
}