/**
 *@file ftsr_decode.c
 *
 *@brief This file contains functions that provides demonstration of
 *       usage of FTSR frame forming APIs
 *
 *The functions provided in the file provides demonstration on
 *usage of  FTSR frame forming 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 "ftsr.h"
#include "stdlib.h"
#include "common.h"
#include "file_transfer.h"
#include "ftci_decode.h"
#include "tc_tm_sample.h"
#include "logger.h"
#include "ft_sender.h"
#include "ft_receiver.h"

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

#define FTSR_FRAME_CTRL_FIELD_LEN 1
#define WAIT_TIME_FIELD 2
#define SEQ_COUNT_FIELD 1

/******************************************************************************/
/*                       Definition of local variables                        */
/******************************************************************************/

static s_string_info 			ftsr_data_loss;
static tc_tm_id_e_ft 			tc_response;

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

/**
 * @brief This function used to encode response frame FTSR
 * It will form FTSR frame with response code
 *
 */
void send_ftsr(e_ftsr_response_code set,uint16_t timeout_time)
{
	s_file_tx_sts_resp ready_sts;
	memset(&ready_sts, 0, sizeof(s_file_tx_sts_resp));
	ready_sts.ctrl.bit.file_tx_session_id = ft_rx_session_id_read();
	ready_sts.ctrl.bit.response_code = set;
	ready_sts.suspend_wait_timeout = timeout_time;

	ftsr_data_loss.str_ptr = os_malloc_eram(sizeof(s_file_tx_sts_resp));
	if(ftsr_data_loss.str_ptr != NULL)
	{
		memcpy(ftsr_data_loss.str_ptr, &ready_sts, sizeof(s_file_tx_sts_resp));
		ftsr_data_loss.size = sizeof(s_file_tx_sts_resp);

		if(dwnld_rqst.seq_loss_detect_enable != SEQ_LOSS_DETECTION_OFF)
		{
		#ifndef FT_SAT
			tc_response = FT_TC_FTSR;
			ft_send_tm_to_obc(FT_TC_FTSR,ftsr_data_loss.str_ptr,ftsr_data_loss.size);
		#else
			tc_response = FT_TM_FTSR;
			ft_send_tm_to_obc(FT_TM_FTSR,ftsr_data_loss.str_ptr,ftsr_data_loss.size);

		#endif
		}
		os_free(ftsr_data_loss.str_ptr);
	}
	else
	{
		DEBUG_CPRINT(("FTM malloc fail\n"));
	}

}


/**
 * @brief This function used to resend the dropped sequence Ids
 *
 *
 */
void ftsr_set_resend_flag(s_treceive_info *rx_info)
{
	uint16_t u16cntr=0;

	while(u16cntr < MAX_PCKT_LOSS_DETECTION_CNTR)
	{
		/* check sequence number is available in buffer */
		if(0 != rx_info->data_loss.lost_packet_list[u16cntr])
		{
			/* set resend flag */
			rx_info->data_loss.resend_rqst_flag = rx_info->data_loss.resend_rqst_flag | (1 << u16cntr);

		}
		u16cntr++;
	}
}

/**
 * @brief This function used to encode response frame FTSR with dropped sequence IDs
 * to request for re transmission
 *
 * prepare FTSR frame with data loss information
 */
void ftsr_prepare_packet_loss_notify_frame(s_treceive_info *rx_info, e_ftsr_response_code response)
{

	uint16_t u16cntr=0;
	uint16_t ftsr_frame_max_length;
	uint16_t ftsr_buffer_length;
	uint8_t resend_flag;
	uint32_t *loc_ptr;

    DEBUG_CPRINT(("Prepare FTSR - loss info %d\n",rx_info->data_loss.total_lost_segment_count));
	log_id_with_one_arg(LOG_INFO_L1, LOG_FTM, PREPARE_FTSR_LOSS_INFO__u16_,rx_info->data_loss.total_lost_segment_count);
	ft_fsm_change_state(FT_Idle_State);


	if(rx_info->data_loss.total_lost_segment_count != 0)
	{



	s_file_tx_sts_resp data_loss_sts;
	data_loss_sts.ctrl.bit.file_tx_session_id = ft_rx_session_id_read();
	data_loss_sts.ctrl.bit.response_code = response;
	/* set default value 0 */
	data_loss_sts.suspend_wait_timeout = 0;


	ft_fsm_change_state(FT_Idle_State);


	/* determine ftsr frame size */
	ftsr_frame_max_length = rx_info->mtu_size - (FTSR_FRAME_CTRL_FIELD_LEN + WAIT_TIME_FIELD + SEQ_COUNT_FIELD);

	ftsr_buffer_length = (rx_info->data_loss.total_lost_segment_count * 4);
	if(ftsr_buffer_length > ftsr_frame_max_length)
	{
		/* if sequence number total byte count is higher than mtu size, use mtu size as maximum FTSR fuffer length */
		ftsr_buffer_length = (FTSR_FRAME_CTRL_FIELD_LEN + WAIT_TIME_FIELD + SEQ_COUNT_FIELD) + ftsr_frame_max_length;
	}
	else
	{
		ftsr_buffer_length = ftsr_buffer_length + (FTSR_FRAME_CTRL_FIELD_LEN + WAIT_TIME_FIELD + SEQ_COUNT_FIELD);
	}

	ftsr_data_loss.str_ptr = os_malloc_eram(ftsr_buffer_length);

	if(ftsr_data_loss.str_ptr != NULL)
	{
		DEBUG_CPRINT(("\nAllocated size %d : ",ftsr_buffer_length));

		ftsr_data_loss.size = (FTSR_FRAME_CTRL_FIELD_LEN + WAIT_TIME_FIELD + SEQ_COUNT_FIELD);

		data_loss_sts.segment_loss_count = 0;
		ftsr_buffer_length = ftsr_buffer_length /4;
		//ftsr_buffer_length++;
		rx_info->data_loss.ftsr_frame_full = 0;
		while(u16cntr < MAX_PCKT_LOSS_DETECTION_CNTR)
		{
			/* check sequence number is available in buffer */
			if(0 != rx_info->data_loss.lost_packet_list[u16cntr])
			{
				/* check seq number was npt shared to sender already */
				resend_flag = rx_info->data_loss.resend_rqst_flag & (1 << u16cntr);
				if(resend_flag != 0)
				{
					ftsr_buffer_length--;
					/* check seq id not fit in current frame length */
					if(ftsr_buffer_length != 0)
					{
						/* encode FTSR frame with sequence numbers */
						loc_ptr = (uint32_t*)&ftsr_data_loss.str_ptr[ftsr_data_loss.size];
						*loc_ptr = rx_info->data_loss.lost_packet_list[u16cntr];
						data_loss_sts.segment_loss_count++;
						ftsr_data_loss.size = ftsr_data_loss.size + 4;
						/* clear re send flag */
						rx_info->data_loss.resend_rqst_flag = rx_info->data_loss.resend_rqst_flag & (~(1 << u16cntr));
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
						DEBUG_CPRINT((" %u-%lu",u16cntr,*loc_ptr));
#pragma GCC diagnostic pop
					}
					else if(ftsr_buffer_length == 0)
					{
						/* break if sequence id encoding on ftsr frame length reaches maximum mtu size it supports */
						/* set frame full flash to indicate remaining sequence ID has to be shared in another FTSR frame */
						rx_info->data_loss.ftsr_frame_full = 1;
						ft_fsm_change_state(FT_Report_Sequence_loss);
						break;
					}
				}


			}
			u16cntr++;

		}

		memcpy(ftsr_data_loss.str_ptr, &data_loss_sts, sizeof(s_file_tx_sts_resp));

//		DEBUG_CPRINT(("\nFTSR size: %d > ",ftsr_data_loss.size));
//
//		for(u16cntr=0;  u16cntr < ftsr_data_loss.size; u16cntr++)
//		{
//			DEBUG_CPRINT(("%x ",ftsr_data_loss.str_ptr[u16cntr]));
//		}


		if(ftsr_buffer_length != 0)
		{
			if(rx_info->data_loss.head_change_request ==1)
			{
				ft_fsm_change_state(FT_Report_Head_Change);
		        dwnld_rqst.data_loss.head_change_request = 0;
			}
		}


		if(rx_info->data_loss.total_lost_segment_count != 0)
		{

#ifndef FT_SAT
			ft_send_tm_to_obc(FT_TC_FTSR,ftsr_data_loss.str_ptr, ftsr_data_loss.size);

#else
			ft_send_tm_to_obc(FT_TM_FTSR,ftsr_data_loss.str_ptr, ftsr_data_loss.size);
#endif
		}
		else
		{
			DEBUG_CPRINT(("\nFTSR skipped: > "));
		}
        os_free(ftsr_data_loss.str_ptr); // added
	}
	else
	{

	}

	}
	else
	{
		if(rx_info->data_loss.head_change_request ==1)
		{
			ft_fsm_change_state(FT_Report_Head_Change);
	        dwnld_rqst.data_loss.head_change_request = 0;
		}
	}


}


/**
 * @brief This function used to encode FTSR frame for Head change request
 *
 */
tc_tm_id_e_ft ftsr_prepare_head_change_request_frame(s_treceive_info *rx_info)
{

	tc_tm_id_e_ft tc_id;
	uint16_t ftsr_buffer_length;
	uint32_t *loc_ptr;

	s_file_tx_sts_resp data_loss_sts;
	data_loss_sts.ctrl.bit.file_tx_session_id = ft_rx_session_id_read();
	data_loss_sts.ctrl.bit.response_code = ftsr_head_change;
	/* set default value 0 */
	data_loss_sts.suspend_wait_timeout = 0;

	ft_fsm_change_state(FT_Idle_State);

	ftsr_buffer_length = (FTSR_FRAME_CTRL_FIELD_LEN + WAIT_TIME_FIELD + SEQ_COUNT_FIELD) + 4;
	ftsr_data_loss.str_ptr = os_malloc_eram(ftsr_buffer_length);

	if(ftsr_data_loss.str_ptr != NULL)
	{
		ftsr_data_loss.size = (FTSR_FRAME_CTRL_FIELD_LEN + WAIT_TIME_FIELD + SEQ_COUNT_FIELD);

		/* encode FTSR frame */
		loc_ptr = (uint32_t*)(&ftsr_data_loss.str_ptr[ftsr_data_loss.size]);
		*loc_ptr = rx_info->pckt_seq_head+1;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
		DEBUG_CPRINT(("Prepare head change %lu info\n",*loc_ptr));
		log_id_with_one_arg(LOG_INFO_L1, LOG_FTM, PREPARE_HEAD_CHANGE_TO__u32_,*loc_ptr);
#pragma GCC diagnostic pop
		ftsr_data_loss.size = ftsr_data_loss.size + 4;
		memcpy(ftsr_data_loss.str_ptr, &data_loss_sts, sizeof(s_file_tx_sts_resp));

	//    DEBUG_CPRINT(("HEAD CHANGE frame "));
	//
	//	for(u16cntr=0; u16cntr< ftsr_data_loss.size; u16cntr++)
	//	{
	//	    DEBUG_CPRINT(("%x ",ftsr_data_loss.str_ptr[u16cntr]));
	//	}
	//    DEBUG_CPRINT(("\n"));

	#ifndef FT_SAT
		tc_id = FT_TC_FTSR;
		ft_send_tm_to_obc(FT_TC_FTSR,ftsr_data_loss.str_ptr, ftsr_data_loss.size);
	#else
		tc_id = FT_TM_FTSR;
		ft_send_tm_to_obc(FT_TM_FTSR,ftsr_data_loss.str_ptr, ftsr_data_loss.size);
	#endif
		os_free(ftsr_data_loss.str_ptr);
	}
	else
	{
		DEBUG_CPRINT(("FTM malloc error\n"));
	}

	return tc_id;

}




