/**
 * @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
 *
 * @copyright Copyright 2023 Antaris, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/******************************************************************************/
/*                      Include headers of the component                      */
/******************************************************************************/

#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 "logger.h"
#include "ft_sender.h"
#include "ft_receiver.h"


/******************************************************************************/
/*                  Definition of local function like macros                  */
/******************************************************************************/

#define FTDS_INTERVAL_TIME_IN_US 100
#define MAX_PCKT_LOSS_DETECTION_CNTR 20
#define RX_PING_SEQ_CNT_MAX 60
#define WAIT_FOR_2SEC  (2*(1000/FT_HANDLER_DELAY_MS))
#define DEBUG_SEQ_LOSS_TST
#define DEBUG_SEQ_INIT_TST


#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)

/******************************************************************************/
/*                      Definition of exported variables                      */
/******************************************************************************/

#ifdef DEBUG_SEQ_INIT_TST
/* enter sequence number to be skipped during transmission to detect packet loss */
uint32 skip_seq[20] = {0xffff,0xffff,0xffff,0xffff,0xffff,0xffff};
#endif

extern uint16_t 		ft_ftds_delay;



/******************************************************************************/
/*                  Declaration of local function prototypes                  */
/******************************************************************************/


static uint8_t derive_sequence_num(void);



/**
 * @brief This function will check and return sequence ID if any available in sequence drop list
 *
 * @param[in]  void
 *
 * @return sequence ID number
 * @retval return the sequence ID read from drop list
 *
 */
static uint32_t check_resend_sequence_id_available(void);

/**
 * @brief This function will wait for time and check the receiver is responding for the transmission
 * the terminate the transmission if receiver goes un responsive state
 *
 * @param[in]  void
 *
 * @return none
 * @retval none
 */

/*Warning removal:function not used
static void sender_side_response_periodic_check(void);
*/
/**
 * @brief This function manipulate and determine the sequence ID to sent
 * It will decide based on receiver response, it depends on lost packet re transmission , head change request
 *
 * @param[in]  void
 *
 * @return sequence ID number
 * @retval return the sequence ID to send in the current FTDS frame
 */
static uint8_t derive_sequence_num(void);

/******************************************************************************/
/*                       Definition of local functions                        */
/******************************************************************************/

/**
 * @brief This function will check and return sequence ID if any available in sequence drop list
 *
 */
static 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;
}




/**
 * @brief This function manipulate and determine the sequence ID to sent
 * It will decide based on receiver response, it depends on lost packet re transmission , head change request
 *
 */
static uint8_t derive_sequence_num(void)
{
#ifdef DEBUG_SEQ_LOSS_TST
	/* these two variables are test variables to stop seq transmission from id "seq_id_block"
	 * it will work till 512 sequences present in a file as whole
	 */
	static uint8_t testvar_stop_all_rem_seq_now_on = 1;
	uint16_t seq_id_block = 25;
#endif

	uint8_t ret=0;
	uint8_t upload_percent;
	char debug_input[5]= "ok";
	uint8_t 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  */
		DEBUG_CPRINT(("\nRe-transmission "));
		log_id(LOG_INFO_L1, LOG_FTM, RE_TRANSMISSION);
		seg_info.current_seg = check_resend_sequence_id_available();
		if(seg_info.current_seg == 0)
		{
			seg_info.resend_rqst = 0;
			DEBUG_CPRINT((" finished !!!!!!"));
			seg_info.next_seg++;
			seg_info.current_seg = seg_info.next_seg;
		}
		else
		{
			DEBUG_CPRINT(("success "));
		}
	}


	debug_input[0] = 'o';
	debug_input[1] = 'k';
	debug_input[2] = '\0';


#ifdef DEBUG_SEQ_LOSS_TST

#ifdef debug_seq_ext
	DEBUG_CPRINT(("\n enter ok to send next packet: "));
	scanf("%s",&debug_input);
#else
	for(u8cnt=0; u8cnt <20; u8cnt++)
	{
		if(seg_info.current_seg == skip_seq[u8cnt])
		{
			debug_input[0] = 'n';
			debug_input[1] = '0';
			debug_input[2] = '\0';
			skip_seq[u8cnt] = 0;
			break;
		}
	}
#endif

#endif

#ifdef DEBUG_SEQ_LOSS_TST
	if(seg_info.current_seg == seq_id_block && testvar_stop_all_rem_seq_now_on ==0)
	{
		seg_info.current_seg = 512;
		testvar_stop_all_rem_seq_now_on = 1;
		DEBUG_CPRINT(("\nWarning DON'T SEND SEQ:%u now on", seq_id_block));
	}
#endif

	if( strcmp(debug_input,"ok") == 0)
	{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
		DEBUG_CPRINT(("\nSEQ:%lu ", seg_info.current_seg));
        log_id_with_one_arg(LOG_INFO_L1, LOG_FTM, FTM_SEQ_u32_,seg_info.current_seg);
#pragma GCC diagnostic pop
		seg_sequence_num_set(seg_info.current_seg);
		ret =1;
	}
	else
	{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
		DEBUG_CPRINT(("\nWarning DON'T SEND SEQ:%lu ", seg_info.current_seg));
#pragma GCC diagnostic pop
	}
	return ret;
}



/*Warning removal:function not used
static void sender_side_response_periodic_check(void)
{
	uint8_t timer_flg;
	timer_flg = ft_get_flag();
	if(timer_flg == 1)
	{
		DEBUG_CPRINT(("Receiver not responsive !!! Terminate transmission\n"));
		ft_transfer_request(T_Txnode,0);
	}

}*/

/******************************************************************************/
/*                      Definition of exported functions                      */
/******************************************************************************/


/**
 * @brief This function will update sequence drop list to be re transmitted
 *
 */
uint8_t ftds_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])
			{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
				DEBUG_CPRINT(("\n duplicate %lu", seq_num));
#pragma GCC diagnostic pop
				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;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
					DEBUG_CPRINT(("Log missing sequence ID %lu  \n", seg_info.resend_pckt_list[u16cntr]));
					log_id_with_one_arg(LOG_INFO_L1, LOG_FTM, LOG_MISSING_SEQUENCE_ID_u32_,seg_info.resend_pckt_list[u16cntr]);
#pragma GCC diagnostic pop
					break;
				}
				u16cntr++;
			}
		}
	}
	else
	{
		/* maximum sequence ID carrier buffer is full; request sender side head position change request. */
		ret_sts = 0;
	}
	return ret_sts;
}



/**
 * @brief This function is used to change the sequence counter head
 *
 */
void ftds_change_head_position(uint32_t set)
{
	seg_info.next_seg = set-1;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
	DEBUG_CPRINT(("Head change :%lu done\n", seg_info.next_seg));
    log_id_with_one_arg(LOG_INFO_L1, LOG_FTM, HEAD_CHANGE_u32_DONE_,seg_info.next_seg);
#pragma GCC diagnostic pop
}


/**
 * @brief This function is used to clear the ping request failure counter logic
 *
 */
void ftds_clear_ping_request(void)
{
	seg_info.rx_ping_count = 0;
	seg_info.ping_fail_cnt = 0;
	ft_reset_timer();
}


/**
 * @brief This function maintain delay time for 2 seconds
 *
 */
void ftds_waitfor2seconds(void)
{
	static uint16_t u16cnt=0;

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

/**
 * @brief This API manages FTDS message transaction and monitoring the transmission
 * It will perform segemntation, FTDS frame formation and taansmit FTDS
 *
 */
void ftds(void)
{
	uint16_t ret_sts=0;
	static uint16_t delay_wait=0;
	eSystemState currentState;


#ifdef debug_seq_ext
	delay_wait = 2;
#endif

    /*
     *  FTDS Delay:
     *  This is where packet delay interval is controlled.
     *  if FTDS has set for 20ms, it will until th time elapse
     *  this function will called every FT_HANDLER_DELAY_MS time
     *  if FT_HANDLER_DELAY_MS = 5 and FTDS = 20, so fourth iteration will reach 20ms
     *  */
	if(delay_wait < (ft_ftds_delay / FT_HANDLER_DELAY_MS))
	{
		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++;
	       	 }


			currentState = ft_fsm_read_state();
			if(currentState != FT_Waitfor_FTSR)
			{
				ret_sts = derive_sequence_num();
				if(ret_sts == 1)
				{
					ret_sts = seg_preapre_frame_ftds();
				}

				if (ret_sts == 1)
				{

	#ifndef FT_SAT
					ret_sts = FT_TC_FTDS;
					ft_send_tm_to_obc(FT_TC_FTDS,seg_info.ftds_buf_ptr, seg_info.packet_buf_length);
                    os_free(seg_info.ftds_buf_ptr);
					#else
					ret_sts = FT_TM_FTDS;
					ft_send_tm_to_obc(FT_TM_FTDS,seg_info.ftds_buf_ptr, seg_info.packet_buf_length);
                    os_free(seg_info.ftds_buf_ptr);
	#endif
				}
				else
				{
					ret_sts = FT_TC_TM_MAX_ID;
				}

			}
			else
			{
		       	if(tx_info.seq_loss_detect_enable == SEQ_LOSS_DETECTION_OFF)
		       	{
					DEBUG_CPRINT(("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
					set_file_transfer_state(ft_process_na);
					ft_service_disable();
		       	}
		       	else
				{
					DEBUG_CPRINT(("No SEQ to be sent.\n"));
				}
			}

		}
		else
		{
			seg_info.rx_ping_count = 0;

			if(seg_info.ping_fail_cnt > (FTDS_PING_RETRY_MAX_TIMEOUT_SET/ft_ftds_delay))
			{
				// Auto terminate not work instead of auto save context activated if other node was not respond.
				DEBUG_CPRINT(("Receiver not responsive !!! Terminate transmission\n"));
				if(tx_info.receiver_is_ready != 1)
				{
					ft_tx_session_id_update(FT_SESSION_ID_DEC);
					DEBUG_CPRINT(("Error : File upload cancelled, check receiver node is active !!!"));
				}
				ft_transfer_request(T_Txnode,0);
				ft_service_disable();
				seg_info.ping_fail_cnt = 0;
			}
			else
			{


				if(seg_info.ping_fail_cnt % (FTDS_PING_RETRY_T_PERIOD/ft_ftds_delay) != 0)
				{
					// do nothing
				}
				else
				{
					send_ftfci(ftfci_ping,0);
					DEBUG_CPRINT(("Ping requesting..\n"));
					log_id(LOG_INFO_L1, LOG_FTM, PING_REQUESTING);
				}

			}
			seg_info.ping_fail_cnt++;

		}



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




