/**
 * @file ftds.c
 *
 * @brief This file contains functions that provides demonstration of
 *        usage of FTDS frame related APIs
 *
 * The functions provided in the file provides demonstration on
 * usage of FTDS frame related APIs
 */
#include "tc_tm_sample.h"
#include "ftds.h"

#include "file_transfer.h"
#include "segmentation.h"
#include "tc_tm_sample.h"
#include "ftfci.h"
#include "ftci.h"
#include "notify_apps.h"
#include "ftsr_decode.h"
#include "fsm.h"
#include "ft_sender.h"







#define FTDS_INTERVAL_TIME_IN_US 100
#define MAX_PCKT_LOSS_DETECTION_CNTR 20
#define RX_PING_SEQ_CNT_MAX 60

#define FTDS_PING_RETRY_TIMEOUT   30  // in seconds
#define FTDS_PING_RETRY_MAX_TIMEOUT_SET   (FTDS_PING_RETRY_TIMEOUT*1000)

/* for every 30 seconds */
#define FTDS_PING_RETRY_T_PERIOD_IN_SEC   (5)
#define FTDS_PING_RETRY_T_PERIOD   (FTDS_PING_RETRY_T_PERIOD_IN_SEC*1000)

#define SEQ_DROP_BUF_CTR 200
static uint8_t derive_sequence_num(void);

#define SCT_DROP_BUF_CTR 20


#define WAIT_FOR_2SEC  (2*(FTDS_TICK_TIME))


//#define debug_seq_ext
#define debug_seq_int


#ifdef FTM_SEQ_DROP_EN_FILE

/* enter sequence number to be skipped during transmission to detect packet loss */
//uint32 skip_seq[20] = {3,8,9,10,11,15};
int32 skip_seq[SEQ_DROP_BUF_CTR];
uint16_t drop_seq_avail_cnt;
#endif

#ifdef FTM_SBAN_FAIL_EN
uint32_t auto_sct_enable_from_seq=0;
int32 sct_seq[SCT_DROP_BUF_CTR];
uint8_t sct_seq_avail_cnt;
#endif


/// This variable count the retransmitted sequence count.
uint32_t ft_retransmit_seq_cntr = 0;

extern uint16_t ftds_interval_delay;

uint32_t check_resend_sequence_id_available()
{
	uint16_t u16cntr=0;
	uint32_t seq_num=0;
	while(u16cntr < MAX_PCKT_LOSS_DETECTION_CNTR)
	{
		if(0 != seg_info.resend_pckt_list[u16cntr])
		{
			seq_num = seg_info.resend_pckt_list[u16cntr];
			seg_info.resend_pckt_list[u16cntr] = 0;
			seg_info.total_resend_seq_ID--;
			break;
		}
		u16cntr++;
	}
	return seq_num;
}



uint8_t update_resend_seq_id_list(uint32_t seq_num)
{
	uint8_t ret_sts = 0;
	uint16_t u16cntr=0;
	if(seg_info.total_resend_seq_ID < MAX_PCKT_LOSS_DETECTION_CNTR)
	{
		for(u16cntr=0; u16cntr < MAX_PCKT_LOSS_DETECTION_CNTR; u16cntr++)
		{
			if(seq_num == seg_info.resend_pckt_list[u16cntr])
			{
				printf("\n duplicate %d", seq_num);
				ret_sts = 1;
				break;
			}
		}

		if(ret_sts != 1)
		{
			u16cntr = 0;
			while(u16cntr < MAX_PCKT_LOSS_DETECTION_CNTR)
			{
				if(0 == seg_info.resend_pckt_list[u16cntr])
				{
					ret_sts = 1;
					seg_info.resend_pckt_list[u16cntr] = seq_num;
					seg_info.total_resend_seq_ID++;
					seg_info.resend_rqst = 1;
					printf("Log missing sequence ID %u  \n", seg_info.resend_pckt_list[u16cntr]);
					break;
				}
				u16cntr++;
			}
		}
	}
	else
	{
		/* maximum sequence ID carrier buffer is full; request sender side head position change request. */
		ret_sts = 0;
	}
	return ret_sts;
}


void change_head_position(uint32_t set)
{
	seg_info.next_seg = set-1;
	printf("Head change :%u done\n", seg_info.next_seg);
}

void clear_ping_request(void)
{
	seg_info.rx_ping_count = 0;
	seg_info.ping_fail_cnt = 0;
	ft_reset_timer();
}

/* this function will read seq numbers to drop during file transfer to test
 * the packet loss recovery feature
 * input text data to be :   $3,5,7,6,5,8,9#
 */
void read_seq_drop_id(void)
{

#ifdef FTM_SEQ_DROP_EN_FILE
	 FILE *fptr_read;
	 char buff[10];
	 uint16_t inc;
	 uint16_t seq_inc=0;
	 uint8_t num_avail;
	 char cdRead;
	 int dec;
	 if ((fptr_read = fopen("droplist.txt","r")) == NULL)
	 {
		 printf("Error! file droplist.txt read fail\n");
	 }
	 else
	 {
		 printf("Preparing Seq Drop list.\n");

///         sample input        $301,5,7,6,521,8,9#

		 cdRead = fgetc(fptr_read);
//		 printf("reAd:%c  %d\n",cdRead,cdRead);
		 if('$' == cdRead)
		 {
			    do {
			         inc = 0;
			         num_avail = 0;
				     memset(buff,'\0',10);
					 while(1)
					 {
						 cdRead = fgetc(fptr_read);
//						 printf("reAd:%c  %d\n",cdRead,cdRead);
						 if(cdRead != ',' && cdRead != '#')
						 {
							 buff[inc] = cdRead;
							 inc++;
						 }
						 else
						 {
							 num_avail = 1;
							 break;
						 }

						 if(cdRead == EOF)
						 {
							 break;
						 }
					 }

					 if(num_avail == 1)
					 {
						 if(seq_inc < SEQ_DROP_BUF_CTR)
						 {
							 dec = atoi(buff);
//							 printf("atoi:%d \nBUf:",dec);

							 skip_seq[seq_inc] = dec;
							 seq_inc++;
#if 0
							 for(inc =0; inc  < 10; inc++)
							 {
								 if(buff[inc] != '\0')
								 {
									 printf("%c",buff[inc]);
									 buff[inc] = '\0';
								 }
								 else
								 {
									 break;
								 }
							 }
							 printf("\n");
#endif
						 }
						 else
						 {
							 /* skip read if buffer mimit has reached */
							 cdRead = '#';
						 }
					 }


			      } while (cdRead != EOF && cdRead != '#');


				 if(seq_inc != 0)
				 {
					 drop_seq_avail_cnt = seq_inc;
					 printf("$");
					 for(inc =0; inc  < seq_inc; inc++)
					 {
						 printf("%d,",skip_seq[inc]);
					 }
					 printf("#\n");
				 }
				 else
				 {
					 drop_seq_avail_cnt =0;
					 printf("$ >>No seq avalilable<< #");
				 }

		 }
		 else
		 {
			 printf("Error ! Start key \"$\" not available in file droplist.txt\n");
		 }

		 fclose(fptr_read);
		 printf("Seq Drop list preparation completed.\n-------\r\n");
	 }

#endif

}




/* this function will read seq numbers to drop during file transfer to test
 * the packet loss recovery feature
 * input text data to be :   $3,5,7,6,5,8,9#
 */
void read_sct_drop_id(void)
{

#ifdef FTM_SBAN_FAIL_EN
	 FILE *fptr_read;
	 char buff[10];
	 uint16_t inc;
	 uint16_t seq_inc=0;
	 uint8_t num_avail;
	 char cdRead;
	 int dec;
	 if ((fptr_read = fopen("sctlist.txt","r")) == NULL)
	 {
		 printf("Error! file sctlist.txt read fail\n");
	 }
	 else
	 {
		 printf("Preparing SCT Drop list.\n");

///         sample input        $301,5,7,6,521,8,9#

		 for(inc =0; inc < SCT_DROP_BUF_CTR; inc++)
		 {
			 sct_seq[inc] = 0;
		 }

		 cdRead = fgetc(fptr_read);
//		 printf("reAd:%c  %d\n",cdRead,cdRead);
		 if('$' == cdRead)
		 {
			    do {
			         inc = 0;
			         num_avail = 0;
				     memset(buff,'\0',10);
					 while(1)
					 {
						 cdRead = fgetc(fptr_read);
//						 printf("reAd:%c  %d\n",cdRead,cdRead);
						 if(cdRead != ',' && cdRead != '#')
						 {
							 buff[inc] = cdRead;
							 inc++;
						 }
						 else
						 {
							 num_avail = 1;
							 break;
						 }

						 if(cdRead == EOF)
						 {
							 break;
						 }
					 }

					 if(num_avail == 1)
					 {
						 if(seq_inc < SCT_DROP_BUF_CTR)
						 {
							 dec = atoi(buff);
//							 printf("atoi:%d \nBUf:",dec);

							 sct_seq[seq_inc] = dec;
							 seq_inc++;
#if 0
							 for(inc =0; inc  < 10; inc++)
							 {
								 if(buff[inc] != '\0')
								 {
									 printf("%c",buff[inc]);
									 buff[inc] = '\0';
								 }
								 else
								 {
									 break;
								 }
							 }
							 printf("\n");
#endif
						 }
						 else
						 {
							 /* skip read if buffer mimit has reached */
							 cdRead = '#';
						 }
					 }


			      } while (cdRead != EOF && cdRead != '#');


				 if(seq_inc != 0)
				 {
					 sct_seq_avail_cnt = seq_inc;
					 printf("$");
					 for(inc =0; inc  < seq_inc; inc++)
					 {
						 printf("%d,",sct_seq[inc]);
					 }
					 printf("#\n");
				 }
				 else
				 {
					 sct_seq_avail_cnt =0;
					 printf("$ >>No SCT seq available<< #");
				 }

		 }
		 else
		 {
			 printf("Error ! Start key \"$\" not available in file droplist.txt\n");
		 }

		 fclose(fptr_read);
		 printf("SCT Seq list preparation completed.\n-------\r\n");
	 }


#endif

}





static uint8_t derive_sequence_num(void)
{
	uint8_t ret=0;
	uint8_t upload_percent;
	char debug_input[5]= "ok";
	uint8_t u8cnt = 0;
        u8cnt = u8cnt;
	if (seg_info.resend_rqst == 0)
	{
		seg_info.next_seg++;
		seg_info.current_seg = seg_info.next_seg;
		upload_percent = (seg_info.next_seg * 100) / tx_info.total_pckts;
		ft_set_percentile_sts(upload_percent);
	}
	else
	{
		/* update resend sequence number  */
		printf("\nRe-transmission ");
		seg_info.current_seg = check_resend_sequence_id_available();
		if(seg_info.current_seg == 0)
		{
			seg_info.resend_rqst = 0;
			printf(" finished !!!!!!");
			seg_info.next_seg++;
			seg_info.current_seg = seg_info.next_seg;
		}
		else
		{
			printf("success ");
		/*
		 * it is used to update status on sequence loss has observed on current session
		 */
			ft_retransmit_seq_cntr++;
		}

	}


	debug_input[0] = 'y';
	debug_input[1] = '\0';



#ifdef FTM_SEQ_DROP_EN_YES_NO
	printf("Enter \"y\" to send next packet\n");
	scanf("%s",&debug_input);
#endif


#ifdef FTM_SEQ_DROP_EN_FILE
	if(drop_seq_avail_cnt != 0)
	{
		for(u8cnt=0; u8cnt < drop_seq_avail_cnt; u8cnt++)
		{
			if(seg_info.current_seg == skip_seq[u8cnt])
			{
				debug_input[0] = 'n';
				debug_input[1] = '\0';
				/* clear drop ID from list */
				skip_seq[u8cnt] = 0;
				break;
			}
		}
	}
#endif


#ifdef FTM_SBAN_FAIL_EN
	if(sct_seq_avail_cnt != 0)
	{
		for(u8cnt=0; u8cnt < sct_seq_avail_cnt; u8cnt++)
		{
			if(seg_info.current_seg == sct_seq[u8cnt])
			{
				printf("\nWarning DON'T SEND after SEQ:%u\n", sct_seq[u8cnt]);
				sband_create_failure();
				sct_seq[u8cnt] = 0;


#ifdef FTM_SBAN_FAIL_EN_START5_SKIP_ALL_SEQ
				seg_info.current_seg = 0xffffffff;
#endif
#ifdef FTM_SBAN_FAIL_EN_START5_SKIP_100_SEQ
				seg_info.current_seg += 100;
#endif
#ifdef FTM_SBAN_FAIL_EN_START5_SKIP_2_SEQ
				seg_info.current_seg += 2;
#endif
#ifdef FTM_SBAN_FAIL_EN_START5_NOSKIP
				// do nothing
#endif

				break;
			}
		}
	}
#endif

	if( strcmp(debug_input,"y") == 0)
	{
		printf("\nSEQ:%u ", seg_info.current_seg);
		sequence_num_set(seg_info.current_seg);
		ret =1;
	}
	else
	{
		printf("\nWarning DON'T SEND SEQ:%u ", seg_info.current_seg);
	}
	return ret;
}



/* commenting for warning removal
static void sender_side_response_periodic_check(void)
{
	uint8_t timer_flg;
	// check suspend timeout is expired
	timer_flg = ft_get_flag();
	if(timer_flg == 1)
	{
		printf("Receiver not responsive !!! Terminate transmission\n");
		//ft_transfer_request(T_Txnode,0);
		printf("Auto savecontext activated.\n");
		ft_set_master_slave(sus_res_master);
		ft_suspend(Suspend_Savecontext_Auto_rqst,20,0);
		prepare_sts_and_notify_sender_app(transfer_suspended_auto_savecontext);
	}

}*/



/*
 * this function will print no sequence message for every 500ms
 */
void printf_no_seq_message()
{
	static uint8_t u16_no_seq_msg_delay_cntr = 0;

	if(ftds_interval_delay < 500)
	{
	   u16_no_seq_msg_delay_cntr++;
	   if(u16_no_seq_msg_delay_cntr > (500/ftds_interval_delay))
	   {
		   printf("No SEQ available to send\n");
		   u16_no_seq_msg_delay_cntr = 0;
	   }
	}
	else
	{
			printf("No SEQ available to send\n");
	}

}




uint16_t ftds(void)
{
	uint16_t ret_sts;
	static uint16_t delay_wait=0;
	eSystemState currentState;


#ifdef FTM_SEQ_DROP_EN_YES_NO
	delay_wait = ftds_interval_delay;
#endif

/* delay for each FTDS, it will wait until  ftds_interval_delay
 * */
  	if(delay_wait < ftds_interval_delay)
	{
		delay_wait++;
	}
	else
	{


		if((seg_info.rx_ping_count < RX_PING_SEQ_CNT_MAX) && (seg_info.ping_fail_cnt == 0))
		{

       	 if(tx_info.seq_loss_detect_enable == SEQ_LOSS_DETECTION_OFF)
       	 {
       		 /* ping check disabled since communication is simplex mode */
 			seg_info.rx_ping_count = 0;
 			seg_info.ping_fail_cnt = 0;
       	 }
       	 else
       	 {
 			seg_info.rx_ping_count++;
       	 }


			#ifdef FTDS_ACK_EN
			seg_info.rx_ping_count = 0;
			#endif
			currentState = ft_fsm_read_state();
			if(currentState != FT_Waitfor_FTSR)
			{
				ret_sts = derive_sequence_num();
				if(ret_sts == 1)
				{
					ret_sts = preapre_frame_ftds();
				}

				if (ret_sts == 1)
				{

	#ifndef FT_SAT
					ret_sts = FT_TC_FTDS;
					ft_save_tc_request(FT_TC_FTDS);
					ft_send_tctm(FT_TC_FTDS,seg_info.ftds_buf_ptr, seg_info.packet_buf_length);
					printf("FTM-TC len:%u\n",seg_info.packet_buf_length);
	#else
					ret_sts = FT_TM_FTDS;
					ft_save_tm_request(FT_TM_FTDS);
					ft_send_tctm(FT_TM_FTDS,seg_info.ftds_buf_ptr, seg_info.packet_buf_length);
	#endif
                                        free(seg_info.ftds_buf_ptr);
	#ifdef FTDS_ACK_EN
                    ft_fsm_change_state(FT_Idle_State);
	#endif
				}
				else
				{
					ret_sts = FT_TC_TM_MAX_ID;
				}

			}
			else
			{
		       	if(tx_info.seq_loss_detect_enable == SEQ_LOSS_DETECTION_OFF)
		       	{
					printf("Upload completed\n");
					prepare_sts_and_notify_sender_app(transfer_upload_success);
					ft_fsm_change_state(FT_Idle_State);
		#ifndef FT_OBC
					ft_file_close(tx_info.fptr,&tx_info.file_opened, tx_info.tx_mode);
		#endif
					ft_process = ft_process_na;
		       	}
		       	else
				{
					if(tx_info.receiver_is_ready != FT_FTSR_RECEIVER_READY_NOT_RECEIVED)
					{
						printf_no_seq_message();
					}
					else
					{
						printf("Wait for Receiver Acceptance\n");
					}
				}
			}

		}
		else
		{
			seg_info.rx_ping_count = 0;

			if(seg_info.ping_fail_cnt > (FTDS_PING_RETRY_MAX_TIMEOUT_SET/ftds_interval_delay))
			{
//				seg_info.ping_fail_cnt = 0;
				printf("Receiver not responsive !!! Terminate transmission\n");
				if(tx_info.receiver_is_ready != FT_FTSR_RECEIVER_READY)
				{
					ft_tx_session_id_update(FT_SESSION_ID_DEC);
					printf("Error : File upload cancelled, check receiver node is active !!!\n");
				}
				if((tx_info.app_id != APPID_DST_FTM_OTSU_) && \
				(tx_info.app_id != APPID_DST_FTM_OBC_COMMS_) && \
				(tx_info.app_id != APPID_DST_FTM_OBC_EPS_) && \
				(tx_info.app_id != APPID_DST_FTM_OBC_ADCS_))
				{
					ft_transfer_request(T_Txnode,0);
				}
				else
				{
					/*
					 * Auto save context has been enabled only for OBC modules such as otsu, comms
					 * eps and adcs
					 */
					printf("Auto savecontext activated.\n");
					ft_set_master_slave(sus_res_master);
					ft_suspend(Suspend_Savecontext_Auto_rqst,20,0);
					prepare_sts_and_notify_sender_app(transfer_suspended_auto_savecontext);
				}
			}
			else
			{
				if(seg_info.ping_fail_cnt % (FTDS_PING_RETRY_T_PERIOD/ftds_interval_delay) != 0)
				{
					// do nothing
				}
				else
				{
					send_ftfci(ftfci_ping,0);
					printf("FTM - Checking Rx node is active...\n");
				}
			}
			seg_info.ping_fail_cnt++;
		}



		delay_wait = 0;
	}
	//sender_side_response_periodic_check();
	return ret_sts;
}




void waitfor2seconds(void)
{
	static int u16cnt=0;

	if(u16cnt < WAIT_FOR_2SEC)
	{
		u16cnt++;
	}
	else
	{
		u16cnt = 0;
		ft_fsm_change_state(FT_Segmentation);
	}
}

/*
 * this api will return retransmission status
 */
uint32_t ft_get_retranmission_status(void)
{
	return ft_retransmit_seq_cntr;
}


/*
 * this api will clear retransmission status
 */
void ft_clear_retranmission_status(void)
{
	ft_retransmit_seq_cntr = 0;
}
