/*! \file output_handler_NB_IoT.c
 * \brief Convert MAC scheduler result and output the FAPI structure by subframe based
 * \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"

int output_handler(eNB_MAC_INST_NB_IoT *mac_inst, module_id_t module_id, int CC_id, uint32_t hypersfn, uint32_t frame, uint32_t subframe, uint8_t MIB_flag, uint8_t SIB1_flag, uint32_t current_time)
{
    //if(schedule_result_list_UL != (schedule_result_t *)0){
    //    printf("time %d\n", mac_inst->current_subframe);
    //    print_schedule_result_DL(); print_schedule_result_UL(); getchar();
    //}
    
    // MIB SIB1 PDU command here
	uint8_t MIB_size = 0;
	uint8_t SIB1_size = 0, i = 0;

	Sched_Rsp_t *SCHED_info = (Sched_Rsp_t*) malloc(sizeof(Sched_Rsp_t));
	nfapi_dl_config_request_pdu_t *dl_config_pdu;
	nfapi_hi_dci0_request_pdu_t* hi_dci0_pdu;
	nfapi_ul_config_request_pdu_t* ul_config_pdu;
	schedule_result_t *tmp;
	int DL_empty = 0, UL_empty = 0; 
	//uint32_t current_time = 0;
	//current_time = hypersfn * 10240 + frame * 10 + subframe;

	// filled common part of schedule_resoponse


	SCHED_info->module_id = module_id;
	SCHED_info->hypersfn = hypersfn;
	SCHED_info->frame = frame;
	SCHED_info->subframe = subframe;
	void *DCI_pdu;

	SCHED_info->TX_req = (nfapi_tx_request_t *) malloc (sizeof(nfapi_tx_request_t));
	SCHED_info->DL_req = (nfapi_dl_config_request_t*) malloc (sizeof(nfapi_dl_config_request_t));
	SCHED_info->DL_req->dl_config_request_body.dl_config_pdu_list = (nfapi_dl_config_request_pdu_t*)malloc(sizeof(nfapi_dl_config_request_pdu_t));
	dl_config_pdu = SCHED_info->DL_req->dl_config_request_body.dl_config_pdu_list;

	//printf("first DL node output: %d current: %d\n",schedule_result_list_DL->output_subframe,current_time);

	//printf("test current: %d\n",current_time);

	if(MIB_flag == 1)
		{
			//printf("[%d]MIB\n",current_time);
			//MIB_size = mac_rrc_data_req_eNB_NB_IoT(*MIB)
        	SCHED_info->DL_req->dl_config_request_body.number_pdu = 1;
			dl_config_pdu->pdu_type                                               	 = NFAPI_DL_CONFIG_NBCH_PDU_TYPE;
      		dl_config_pdu->pdu_size                                               	 = 2+sizeof(nfapi_dl_config_nbch_pdu_rel13_t);
      		dl_config_pdu->nbch_pdu.nbch_pdu_rel13.length                            = MIB_size;
      		dl_config_pdu->nbch_pdu.nbch_pdu_rel13.pdu_index                         = 1;
      		dl_config_pdu->nbch_pdu.nbch_pdu_rel13.transmission_power                = 6000;
      		// fill MIB PDU
      		//SCHED_info->TX_req->tx_request_body.tx_pdu_list[dl_config_pdu->NB_IoTch_pdu.NB_IoTch_pdu_rel13.pdu_index].segments[0].segment_data = MIB;
      		
      		//dl_scheduled(mac_inst->current_subframe, _NPBCH, 0, "MIB");
		}
		else if(SIB1_flag == 1)
		{
			//SIB1_size = mac_rrc_data_req_eNB_NB_IoT(*SIB1);
			SCHED_info->DL_req->dl_config_request_body.number_pdu = 1;
			dl_config_pdu->pdu_type                                                   = NFAPI_DL_CONFIG_NDLSCH_PDU_TYPE;
			dl_config_pdu->pdu_size                                                   = 2+sizeof(nfapi_dl_config_ndlsch_pdu_rel13_t);
			dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.length                         = SIB1_size;
			dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.pdu_index					  = 1;
			dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.rnti_type                      = 0;
			dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.rnti                           = 0xFFFF; // SI-rnti
			dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.modulation                     = 2;

			//SCHED_info->TX_req->tx_request_body.tx_pdu_list[dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.pdu_index].segments[0].segment_data = SIB1;

			//start symbol, Resource assignment, Repetition number, Number of subframe Resource assignment lost for now
			//dl_scheduled(mac_inst->current_subframe, _NPDSCH, SI_RNTI, "SIB1");
		}

	else if(schedule_result_list_DL==NULL)
	{
		DL_empty = 1;
		//printf("no remaining node of DL scheduling result\n");
	}else
	{
		if(schedule_result_list_DL->output_subframe < current_time)
		{
			while(schedule_result_list_DL->output_subframe < current_time)
			{
				//printf("This error if there is DL scheduling result node before the current time\n");
				tmp = schedule_result_list_DL;
				schedule_result_list_DL = schedule_result_list_DL->next;
				free(tmp);
				//printf("test2 current: %d\n",current_time);
				//break;
				if(schedule_result_list_DL == NULL){
					return -1;
				}
			}
			//printf("return\n");
			//return -1;
		}
		else if (schedule_result_list_DL->output_subframe == current_time)
		{
			switch(schedule_result_list_DL->channel)
			{
				case NPDCCH:

					if(schedule_result_list_DL->direction == DL)
					{
						//printf("Sched Info DL DCI here\n");
						SCHED_info->DL_req->dl_config_request_body.number_dci = 1;
						DCI_pdu = schedule_result_list_DL->DCI_pdu;
						// not consider the case transmitting 2 DCIs for the moment also not consider N2 now
						dl_config_pdu->pdu_type                                                          = NFAPI_DL_CONFIG_NPDCCH_PDU_TYPE;
						dl_config_pdu->pdu_size                                                          = 2+sizeof(nfapi_dl_config_npdcch_pdu_rel13_t);
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.length				                 = schedule_result_list_DL->sdu_length;
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.pdu_index                             = 1;
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.ncce_index                            = 0;
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.aggregation_level     				 = 1;
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.rnti_type                             = schedule_result_list_DL->rnti_type; // 3 = UE-specific RNTI TODO: add RNTI type in scheduling result
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.rnti                                  = schedule_result_list_DL->rnti;
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.dci_format                            = 0; // N1
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.scheduling_delay                      = ((DCIFormatN1_t *)DCI_pdu)->Scheddly;
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.resource_assignment                   = ((DCIFormatN1_t *)DCI_pdu)->ResAssign;
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.repetition_number                     = ((DCIFormatN1_t *)DCI_pdu)->RepNum;
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.mcs                                   = ((DCIFormatN1_t *)DCI_pdu)->mcs;
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.new_data_indicator                    = ((DCIFormatN1_t *)DCI_pdu)->ndi;
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.harq_ack_resource                     = ((DCIFormatN1_t *)DCI_pdu)->HARQackRes;
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.npdcch_order_indication               = ((DCIFormatN1_t *)DCI_pdu)->orderIndicator;
						dl_config_pdu->npdcch_pdu.npdcch_pdu_rel13.dci_subframe_repetition_number        = ((DCIFormatN1_t *)DCI_pdu)->DCIRep;
					}else if(schedule_result_list_DL->direction == UL)
					{
						SCHED_info->HI_DCI0_req = (nfapi_hi_dci0_request_t*)malloc(sizeof(nfapi_hi_dci0_request_t));
						SCHED_info->HI_DCI0_req->hi_dci0_request_body.hi_dci0_pdu_list = (nfapi_hi_dci0_request_pdu_t*)malloc(sizeof(nfapi_hi_dci0_request_pdu_t));
						hi_dci0_pdu = SCHED_info->HI_DCI0_req->hi_dci0_request_body.hi_dci0_pdu_list;
						DCI_pdu = schedule_result_list_DL-> DCI_pdu;
						SCHED_info-> HI_DCI0_req->hi_dci0_request_body.number_of_dci =1;
						hi_dci0_pdu->pdu_type                                                            = NFAPI_HI_DCI0_NPDCCH_DCI_PDU_TYPE;
						hi_dci0_pdu->pdu_size                                                            = 2 + sizeof(nfapi_hi_dci0_npdcch_dci_pdu_rel13_t);
						hi_dci0_pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13.ncce_index                      = 0;
						hi_dci0_pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13.aggregation_level               = 1;
						hi_dci0_pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13.rnti                            = schedule_result_list_DL->rnti;
						hi_dci0_pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13.subcarrier_indication           = ((DCIFormatN0_t *)DCI_pdu)->scind;
						hi_dci0_pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13.resource_assignment             = ((DCIFormatN0_t *)DCI_pdu)->ResAssign;
						hi_dci0_pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13.scheduling_delay                = ((DCIFormatN0_t *)DCI_pdu)->Scheddly;
						hi_dci0_pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13.mcs                             = ((DCIFormatN0_t *)DCI_pdu)->mcs;
						hi_dci0_pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13.redudancy_version               = ((DCIFormatN0_t *)DCI_pdu)->rv;
						hi_dci0_pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13.repetition_number               = ((DCIFormatN0_t *)DCI_pdu)->RepNum;
						hi_dci0_pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13.new_data_indicator              = ((DCIFormatN0_t *)DCI_pdu)->ndi;
						hi_dci0_pdu->npdcch_dci_pdu.npdcch_dci_pdu_rel13.dci_subframe_repetition_number  = ((DCIFormatN0_t *)DCI_pdu)->DCIRep;
					}
					break;
				case NPDSCH:

				    DCI_pdu = schedule_result_list_DL-> DCI_pdu;
					SCHED_info->DL_req->dl_config_request_body.number_pdu = 1;
					dl_config_pdu->pdu_type                                           = NFAPI_DL_CONFIG_NDLSCH_PDU_TYPE;
					dl_config_pdu->pdu_size                                           = 2+sizeof(nfapi_dl_config_ndlsch_pdu_rel13_t);
					dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.length                 = schedule_result_list_DL->sdu_length;
					dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.pdu_index			  = 1;
					dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.rnti_type              = 1;
					dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.rnti                   = schedule_result_list_DL->rnti; // C-RNTI
					dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.resource_assignment    = ((DCIFormatN1_t *)DCI_pdu)->ResAssign;
					dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.repetition_number      = ((DCIFormatN1_t *)DCI_pdu)->RepNum;
					dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.modulation             = 2;
					dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.number_of_subframes_for_resource_assignment = get_num_sf(dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.resource_assignment);
					//SCHED_info->TX_req->tx_request_body.tx_pdu_list[dl_config_pdu->ndlsch_pdu.ndlsch_pdu_rel13.pdu_index].segments[0].segment_data = schedule_result_list_DL->DLSCH_pdu;
					break;
				default:
					break;
			}

			if(schedule_result_list_DL->DCI_release == 1)
				free(schedule_result_list_DL->DCI_pdu);
			
            tmp = schedule_result_list_DL;
			schedule_result_list_DL = schedule_result_list_DL->next;
			

			//printf("subframe check scheduling result next %d\n",schedule_result_list_DL->output_subframe);
		}
	}
		//printf("There is no downlink transmission\n");

	

	if(schedule_result_list_UL==NULL)
	{
		UL_empty = 1;
		//printf("no remaining node of UL scheduling result\n");
	}else
	{
		if(schedule_result_list_UL->output_subframe < current_time)
		{
			while(schedule_result_list_UL->output_subframe < current_time)
			{
				//printf("This error if there is UL scheduling result node before the current time\n");
				tmp = schedule_result_list_UL;
				schedule_result_list_UL = schedule_result_list_UL->next;
				free(tmp);
				return -1;
			}
			//printf("return\n");
			//return -1;
		}
		else if(schedule_result_list_UL->output_subframe == current_time)
		{
			SCHED_info->UL_req = (nfapi_ul_config_request_t *)malloc(sizeof(nfapi_ul_config_request_t));
			SCHED_info->UL_req->ul_config_request_body.number_of_pdus = 0;
			SCHED_info->UL_req->ul_config_request_body.ul_config_pdu_list = (nfapi_ul_config_request_pdu_t *)malloc(5 * sizeof(nfapi_ul_config_request_pdu_t));
			ul_config_pdu = SCHED_info->UL_req->ul_config_request_body.ul_config_pdu_list;
		}	
		while(schedule_result_list_UL->output_subframe == current_time)
		{
			if(schedule_result_list_UL->channel == NPUSCH)
			{
				printf("first UL \n");
				SCHED_info->UL_req->ul_config_request_body.number_of_pdus ++;
				//SCHED_info->UL_req.sfn_sf = ;
				(ul_config_pdu + i) ->pdu_type                                            = NFAPI_UL_CONFIG_NULSCH_PDU_TYPE;
				(ul_config_pdu + i) ->pdu_size                                            = 2 + sizeof(nfapi_ul_config_nulsch_pdu_rel13_t);

				if(schedule_result_list_UL->npusch_format == 0)
				{
					DCI_pdu = schedule_result_list_UL->DCI_pdu;
					// bug here
					(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.nulsch_format           = 0;
					(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.size                    = UL_TBS_Table[((DCIFormatN0_t *)DCI_pdu)->mcs][((DCIFormatN0_t *)DCI)->ResAssign];
					//printf("test\n");
					(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.rnti                    = schedule_result_list_UL->rnti;
					(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.subcarrier_indication   = ((DCIFormatN0_t *)DCI_pdu)->scind;
					(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.resource_assignment     = ((DCIFormatN0_t *)DCI_pdu)->ResAssign;
					(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.mcs                     = ((DCIFormatN0_t *)DCI_pdu)->mcs;
					(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.redudancy_version       = ((DCIFormatN0_t *)DCI_pdu)->rv;
					(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.repetition_number       = ((DCIFormatN0_t *)DCI_pdu)->RepNum;
					(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.new_data_indication     = ((DCIFormatN0_t *)DCI_pdu)->ndi;

				}else if(schedule_result_list_UL->npusch_format == 1)
					{
						DCI_pdu = schedule_result_list_UL->DCI_pdu;
						(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.nulsch_format       = 1;
						(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.rnti                = schedule_result_list_UL->rnti;
						(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.ue_information.ue_information_rel13.total_number_of_repetitions = schedule_result_list_UL->R_harq;
						//for ACK /NACK
						(ul_config_pdu + i) ->nulsch_pdu.nulsch_pdu_rel13.nb_harq_information.nb_harq_information_rel13_fdd.harq_ack_resource = ((DCIFormatN1_t *)DCI_pdu)->HARQackRes;
					}
				//print_scheduling_result_UL();
				
                if(schedule_result_list_UL->DCI_release == 1){
                    free(schedule_result_list_UL->DCI_pdu);
                }
                
                tmp = schedule_result_list_UL;
				schedule_result_list_UL = schedule_result_list_UL->next;
				
				free(tmp);

				i++;
				if(schedule_result_list_UL == NULL)
					break;
			}else{
				printf("error");
			}
		}
	}

	if(DL_empty == 1 )
	{
		//printf("[hypersfn:%2d][frame:%2d][subframe:%2d]No remaining DL result\n",hypersfn,frame,subframe);
	}
	if(UL_empty == 1)
	{
		//printf("[hypersfn:%2d][frame:%2d][subframe:%2d]no remaining UL result\n",hypersfn,frame,subframe);
	}
	
	//printf("[hypersfn:%2d][frame:%2d][subframe:%2d]filling the schedule response successfully\n",hypersfn,frame,subframe);

	return 0;
}