/**
 * @file ftsr_decode.c
 *
 * @brief This file contains functions that provides demonstration of
 *        usage of FTSR frame decoding APIs
 *
 * The functions provided in the file provides demonstration on
 * usage of  FTSR frame decoding APIs
 */
#include "ftsr_decode.h"

#include "stdio.h"
#include "string.h"
#include "stdlib.h"

#include "data_type.h"
#include "file_transfer.h"
#include "fsm.h"
#include "ftci.h"
#include "ftds.h"
#include "message_frame_def.h"
#include "notify_apps.h"
#include "segmentation.h"
#include "upload_def.h"
#include "suspend_resume.h"
#include "ftds_decode.h"
#include "ft_debug.h"
#include "ft_sender.h"
#include "ft_receiver.h"


#define FTSR_INCOMING_RING_BUFFER_SIZE 7



s_file_tx_sts_resp ftrs_frame;
uint8_t ftsr_res_frame[10];
extern s_string_info dest_path_name_info;
s_ftsr_frame_buff ftsr_rxd_frame;
extern ftm_db_params ftm_db_test;
extern uint8_t rct_request_node_id;

volatile static uint8_t ftsr_rcv_message_incoming_cntr  = 0;
static uint8_t ftsr_rcv_message_processed_cntr = 0;
static uint8_t ftsr_incoming_msg_buf[FTSR_INCOMING_RING_BUFFER_SIZE+1][100];


static void ft_derive_session_throughput_status(void);





static uint8_t* ftsr_receive_mem_alloc(void)
{
	uint8_t *ptr;
	ptr = ftsr_incoming_msg_buf[ftsr_rcv_message_incoming_cntr];
	ftsr_rcv_message_incoming_cntr++;
	if(ftsr_rcv_message_incoming_cntr > FTSR_INCOMING_RING_BUFFER_SIZE) ftsr_rcv_message_incoming_cntr = 0;
	return ptr;
}

static uint8_t* ftsr_incoming_message_check(void)
{
	uint8_t* ptr;
	if(ftsr_rcv_message_processed_cntr != ftsr_rcv_message_incoming_cntr)
	{
		ptr =  ftsr_incoming_msg_buf[ftsr_rcv_message_processed_cntr];
		ftsr_rcv_message_processed_cntr++;
		if(ftsr_rcv_message_processed_cntr > FTSR_INCOMING_RING_BUFFER_SIZE) ftsr_rcv_message_processed_cntr = 0;
	}
	else
	{
		return NULL;
	}
	return ptr;
}



static void upload_error()
{
        ft_fsm_change_state(FT_Idle_State);
        set_file_transfer_state(ft_process_na);
        ft_tx_session_id_update(FT_SESSION_ID_DEC);
}

void log_missing_sequnce()
{
	uint16_t u16cnt;
	uint16_t frame_length_cntr;
	uint32_t *loc_ptr;
	s_file_tx_sts_resp *ftsr;
	ftsr = (s_file_tx_sts_resp*)ftsr_rxd_frame.ftsr_buf_ptr;
	frame_length_cntr = sizeof(s_file_tx_sts_resp);
	for(u16cnt = 0; u16cnt < ftsr->segment_loss_count; u16cnt++)
	{
		loc_ptr = (uint32_t*)(&ftsr_rxd_frame.ftsr_buf_ptr[frame_length_cntr]);
		update_resend_seq_id_list(*loc_ptr);
		frame_length_cntr = frame_length_cntr + 4;
	}
}


void get_head_position()
{
	uint8_t len_loc;
	len_loc = sizeof(s_file_tx_sts_resp);
	uint32_t *loc_ptr = (uint32_t*)&ftsr_rxd_frame.ftsr_buf_ptr[len_loc];

	change_head_position(*loc_ptr);
	ft_fsm_change_state(FT_Sequence_Transmission);


}


static void ft_derive_session_throughput_status(void)
{
	/*
	 * kbps derivation
	 * kilobytes per second
	 *
	 * sample:
	 *  file size - 8kb > 8192
	 *  MTU - 128
	 *  FTDS - 10ms
	 *
	 *  FTM header 7 bytes for each sequnce
	 *  Sequence total = 8192 / (128-7) = 67
	 *  So it will take 670ms(67*10ms) to complete transmission.
	 *  hence speed is 12226 bytes pers second or 11.94 kbytes/sec
	 *
	 *     8192
	 *  =  ____         =>  8192 / 0.67 => 12226
	 *    (670/1000)
	 *
	 */

	time_t session_time;


	float data_rate;
	float delayed_data_rate;
	float temp;
	temp = ft_get_ftds_delay();
	temp = tx_info.total_pckts * temp;
	if(temp != 0)
	{

	}
	else
	{

	}
	data_rate = tx_info.size / (temp / 1000);
	printf("\r\nSession Info::\n");
	printf("a. File size                     - %d kilobyte (%d bytes)\n",tx_info.size/1024,tx_info.size);
	printf("b. Packet Interval time in ms    - %d\n",ft_get_ftds_delay());
	printf("c. MTU                           - %d\n",tx_info.mtu_size);
	printf("d. Total packets                 - %d\n",tx_info.total_pckts);

	printf("\nFTM Transmission data rate status in Kbits (kilobits per seconds)\n");

	session_time = ft_get_session_time();
	if(1)
	{
		if(session_time != 0)
		{
			delayed_data_rate = tx_info.size / session_time;
		}
		else
		{
			delayed_data_rate = tx_info.size / 1;
		}
		printf("    1. Actual rate is - %.1f Kbits, total session time taken %lu seconds\n",(delayed_data_rate/1024)*8,session_time);
		printf("    2. expected rate was - %.1f Kbits\n",(data_rate/1024)*8);

	}
	else
	{
		if(session_time != 0)
		{
			printf("    rate - %.1f Kbits, total session time taken %lu seconds\n",(data_rate/1024)*8,session_time);
		}
		else
		{
			printf("    rate - %.1f Kbits, total session time taken less than 1 seconds\n",(data_rate/1024)*8);
		}
	}
	if(ft_get_retranmission_status() > 0)
	{
		printf("    \nRe - Transmitted packets count %d\n", ft_get_retranmission_status());
	}
	else
	{
		
	}

}

uint8_t prepare_sts_and_notify_sender_app(ft_status_type status)
{
	uint8_t ret=0;
	ft_notification_info sts;
	sts.app_id = tx_info.app_id;
	sts.status = status;

	switch(status)
	{
		case transfer_upload_success:
			printf("File uploaded successfully !!! \n");
			sts.dwld_info.tx_mode = tx_info.tx_mode;
			sts.dwld_info.size_file_or_data = tx_info.size;
			ft_save_session_end_time();
			sts.dwld_info.retranmission_status = ft_get_retranmission_status();
			ft_derive_session_throughput_status();
//                        free(input_file_name);

			break;

		case crc_error:
			printf("File uploaded failed - CRC error reported !!! \n");
//                        free(input_file_name);
			break;

		case transfer_suspend_accepted:
			printf("File uploaded Suspended !!! \n");
			break;

		case transfer_suspend_savecontext_accepeted:
		case transfer_suspended_auto_savecontext:
			printf("Save context accepted by Receiver !!! \n");
#ifndef FT_OBC
			ft_file_close(tx_info.fptr,&tx_info.file_opened, tx_info.tx_mode);
#endif
			sts.context_info.session_id = 0;
			sts.context_info.node_operation = ft_upload;
			sts.dwld_info.size_file_or_data = tx_info.size;
			save_context_info(&sts);
//                        free(input_file_name);
			ft_fsm_change_state(FT_Inactive);
			set_file_transfer_state(ft_process_na);
			break;

		case transfer_suspended_savecontext:
			printf("Close file %s\n",input_file_name);
#ifndef FT_OBC
			ft_file_close(tx_info.fptr,&tx_info.file_opened, tx_info.tx_mode);
#endif
			save_context_info(&sts);
			if(sts.context_info.context_info_buf_ptr == NULL)
			{
				ft_transfer_request(Suspend_Savecontext_nack_rqst,0);
				return 0;
			}
                        else
                        {
//                            free(input_file_name);
                        }
			break;


		case transfer_resumed_restorecontext:
			/* App id will be updated with requester node id only at the case resume restore context
			 * because this sender node "tx_info.app_id" was cleared during SCT command.
			 */
			sts.app_id = rct_request_node_id;
			break;


		default:
			break;
	}

	ret = ft_notify_sender_app(&sts);
	return ret;
}


void check_ftsr_response_code(e_ftsr_response_code response, uint16 waiting_time)
{

	static uint8_t test_var = 1;
	clear_ping_request();
	switch (response)
	{
		case ftsr_receiver_ready:
			/*change to next state since receiver node is ready for data packet receive */
			ft_fsm_change_state(FT_Segmentation);
			prepare_sts_and_notify_sender_app(transfer_upload_ready);
			break;

		case ftsr_rx_api_not_found:
			ft_set_err_sts(ft_receiver_app_not_reg_error);
                        upload_error();
                        prepare_sts_and_notify_sender_app(invalid_receiver_app_id);
			break;

//		case ftsr_disk_full:
//			/*change to cancelled state since receiver node is not ready to receive data packet */
//			if (currentState == Waitfor_Upload_ack_State)
//			{
//				ft_fsm_change_state(Transfer_Cancel_State);
//			}
//			break;

		case ftsr_missing_pckts:
			if(ft_process != ft_process_na)
			{
				log_missing_sequnce();
				ft_fsm_change_state(FT_Sequence_Transmission);
			}
			else
			{
				printf("FTM, No service, ftsr_missing_pckts\n");
			}
			break;

		case ftsr_download_complete:
			/*transmission success */
			printf("Upload success\n");
			prepare_sts_and_notify_sender_app(transfer_upload_success);
			if(test_var == 1)
			{
				ft_fsm_change_state(FT_Inactive);
#ifndef FT_OBC
				ft_file_close(tx_info.fptr,&tx_info.file_opened, tx_info.tx_mode);
#endif
				ft_process = ft_process_na;
			}
			else
			{
				test_var = 1;
			}
			break;

		case ftsr_crc_fail:
			printf("CRC error\n");
			prepare_sts_and_notify_sender_app(crc_error);
			ft_fsm_change_state(FT_Inactive);
#ifndef FT_OBC
			ft_file_close(tx_info.fptr,&tx_info.file_opened, tx_info.tx_mode);
#endif
			set_file_transfer_state(ft_process_na);
			break;

		case ftsr_file_not_found:
			printf("\r\n File Creating Error in Receiver Side");
			ft_set_err_sts(ft_receiver_side_undefined_error);
                        upload_error();
                        prepare_sts_and_notify_sender_app(transfer_upload_rejected);
			break;


		case ftsr_head_change:
			if(ft_process != ft_process_na)
			{
				if(tx_info.receiver_is_ready == FT_FTSR_RECEIVER_READY_NOT_RECEIVED)
				{
					/*
					 * FT download acceptance not received , so cancel FT request
					 */
					ft_transfer_request(T_Txnode,0);

					printf("Warning !!! Sender wait for acceptance but receiver has sent Head change request instead FT download ready cmd\n");
					printf("Hence file upload terminated, pls try another FTM session.\n");
				}
				else
				{
					get_head_position();
				}
			}
			else
			{
				printf("HC rqst Rejected\n");
			}
			break;

		case ftsr_ack:
#ifdef FT_SUSRES_TEST
			if(ftm_db_test.dont_send_ack_for_sus_rqst != 1 &&
			   ftm_db_test.dont_send_ack_for_res_rqst != 1 &&
			   ftm_db_test.skip_all_sus_ack_ != 1 &&
			   ftm_db_test.skip_all_res_ack_ != 1)
#endif
			{
				printf("Suspend/ Resume ACK received\n");
				ft_acceptance_received(prepare_sts_and_notify_sender_app);
			}
#ifdef FT_SUSRES_TEST
			else
			{
				printf("[FT-DB-Test] Suspend/ Resume ACK received\n");
				ftm_db_test.dont_send_ack_for_sus_rqst = 0;
				ftm_db_test.dont_send_ack_for_res_rqst = 0;
			}
#endif
			break;

		case ftsr_nak:
			printf("Suspend/ Resume NACK received\n");
			ft_acceptance_nack(prepare_sts_and_notify_sender_app);
			break;

		case ftsr_suspend:
			ft_set_master_slave(sus_res_slave);
			ft_suspend(Suspend_Timeout_Mode_rqst,waiting_time,1);
//			prepare_sts_and_notify_sender_app(transfer_suspended);
			break;

		case ftsr_suspend_savecontext:
			ft_set_master_slave(sus_res_slave);
			ft_suspend(Suspend_Savecontext_Mode_rqst,waiting_time,3);
			if(s_sus_res_info.sus_savctxt_ack_sent != 1)
			{
				prepare_sts_and_notify_sender_app(transfer_suspended_savecontext);
			}
			else
			{
				/* already context saved so just send ACK */
				printf("FTM - send ACK only\n");
				ft_savecontext_ack(1);
			}
			break;

		case ftsr_resume:
			ft_set_master_slave(sus_res_slave);
			ft_resume(Resume_Timeout_Mode_rqst,1);
//			prepare_sts_and_notify_sender_app(transfer_resumed);
			break;

		case ftsr_resume_savecontext:
			ft_set_master_slave(sus_res_slave);
			set_file_transfer_state(ft_upload);
			prepare_sts_and_notify_sender_app(transfer_resumed_restorecontext);
			break;

		case ftsr_cancelled_by_suspend_timeoout:
			ft_terminate(T_SuspendTimeout, 0);
			prepare_sts_and_notify_sender_app(tx_terminated_suspend_timeout_expired);
			break;

		case ftsr_terminate_rxnode:
			ft_terminate(T_Rxnode, 0);
			prepare_sts_and_notify_sender_app(transmission_terminated_by_rx_node);
			break;

		case ftsr_ping:
			printf("FTM Ping clear\n");
			#ifdef FTDS_ACK_EN
                        ft_fsm_change_state(FT_Sequence_Transmission);
			#endif
			break;
      
                case ftsr_invalid_session_id:
                        printf("\r\n ERROR Receiver discarded last frame due to Invalid session ID.\n");
                        break;

                case ftsr_dwnld_ptr_null:
                        printf("\r\n ERROR - Invalid Memory Address has been Received in receiver side\n");
                        ft_set_err_sts(ft_rx_mem_ptr_null_error);
                        upload_error();
                        prepare_sts_and_notify_sender_app(transfer_upload_rejected);
                        break;

                case ftsr_ftm_busy:
                        printf("\r\n ERROR RX busy with another session\n");
                        ft_set_err_sts(ft_framework_busy_error);
                        upload_error();
                        prepare_sts_and_notify_sender_app(transfer_upload_rejected);
                        break;
                case ftsr_invalid_cfg_input:
                        printf("\r\n ERROR Input configuration invalid\n");
                        ft_set_err_sts(ft_rx_invld_input_cfg);
                        upload_error();
                        prepare_sts_and_notify_sender_app(transfer_upload_rejected);
                        break;
                case ftsr_rx_system_error:
                        printf("\r\n ERROR Receiver side system / memory allocation error\n");
                        ft_set_err_sts(ft_rx_sys_error);
                        upload_error();
                        prepare_sts_and_notify_sender_app(transfer_upload_rejected);
                        break;        

		default:
			printf("\r\n ERROR FTSR invalid response code rxd\n");      
			break;
	}
}

void decode_ftsr_frame()
{
	s_file_tx_sts_resp *ftsr = (s_file_tx_sts_resp*)ftsr_rxd_frame.ftsr_buf_ptr;
  uint8_t sid = ft_tx_session_id_read();

	if(ftsr->ctrl.bit.file_tx_session_id != sid)
	{
		// send invalid session id response to sender node
    printf("\r\n ERROR Sender discarded last frame due to Invalid session ID RCV:%d != STR:%d. RES:%d\n", ftsr->ctrl.bit.file_tx_session_id,sid,ftsr->ctrl.bit.response_code);
    if(ftsr->ctrl.bit.response_code == ftsr_terminate_rxnode || ftsr->ctrl.bit.response_code == ftsr_invalid_session_id || ftsr->ctrl.bit.response_code == ftsr_ftm_busy)
    {
        check_ftsr_response_code(ftsr->ctrl.bit.response_code,ftsr->suspend_wait_timeout);                 
    }
  }
	else
	{
		check_ftsr_response_code(ftsr->ctrl.bit.response_code,ftsr->suspend_wait_timeout);
    }
}

void ft_receive_ftsr(uint8_t *ftsr_data, uint16_t ftsr_data_length)
{
	uint8_t* ptr;
	uint16_t u16cnt;
	eSystemState currentState;
	currentState = ft_fsm_read_state();

	ptr = ftsr_receive_mem_alloc();
	if (ptr != NULL)
	{
		//ftsr_rxd_frame.buf_length = ftsr_data_length;
		for (u16cnt = 0; u16cnt < ftsr_data_length; u16cnt++)
		{
			ptr[u16cnt] = ftsr_data[u16cnt];
		}
		if(currentState == FT_Inactive)
		{
			// process the recent received frame
			ftsr_rxd_frame.ftsr_buf_ptr = ptr;
			if(ftsr_rxd_frame.ftsr_buf_ptr != NULL)
			{
				decode_ftsr_frame();
			}
		}
	}
	else
	{
		printf("FTM-FTSR malloc fail\n");
	}
}

/*
 * This api is used to check incoming message container then
 *  decode ftsr frame if available
 */
void ft_decode_ftsr_frame(void)
{
	ftsr_rxd_frame.ftsr_buf_ptr = ftsr_incoming_message_check();
	if(ftsr_rxd_frame.ftsr_buf_ptr != NULL)
	{
		decode_ftsr_frame();
	}
}


void ftsr_incoming_frame_buffer_clear(void)
{
	ftsr_rcv_message_processed_cntr = 0;
	ftsr_rcv_message_incoming_cntr = 0;
}


