/**
 * @file segmentation.c
 *
 * @brief This file contains functions that provides demonstration of
 *        usage of file segmentation and data packet transfer APIs
 *
 * The functions provided in the file provides demonstration on
 * usage of file segmentation and data packet transfer 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.
 *        usage of TC TM Library APIs
 * 
 */


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

#include "debug_hooks.h"
#include "segmentation.h"
#include "ftds.h"
#include "ft_config.h"
#include "upload_def.h"
#include "ftfci_decode.h"
#include "ftci_decode.h"
#include "logger.h"
#include "ft_sender.h"
#include "ft_receiver.h"



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

#define DATA_TX_FRAME_CTRL_FIELD_LEN 1
#define MAX_SEQ_NUM_FIELD 4
#define MAX_DATA_LEN_FIELD 2

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

static s_file_tx_data_pckt	 	ftds_frame;

/******************************************************************************/
/*                      Definition of exported variables                      */
/******************************************************************************/
s_segment_info 					seg_info;


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

#ifndef FT_OBC
/**
 * @brief This function used to read data from file for the requested file position
 *
 *
 * @param[in]  file_position
 *
 * @return status of file read operation
 * @retval 1 indicates success, 0 is error
 */
static uint8_t read_data_from_file(uint32_t file_position);
#endif

/**
 * @brief This function used to read data from memory buffer for the requested buffer index
 *
 *
 * @param[in]  data_position
 *
 * @return status of file read operation
 * @retval 1 indicates success, 0 is error
 */
static uint8_t read_data_from_bulkdata(uint32_t data_position);

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

/**
 * @brief This function used to read data from memory buffer for the requested buffer index
 *
 *
 */
static uint8_t read_data_from_bulkdata(uint32_t data_position)
{
	uint8_t ret_sts = 0;
	uint16_t u16cnt;

	for (u16cnt = 0; u16cnt < tx_info.packet_data_size; u16cnt++)
	{
		DEBUG_CPRINT((" %02X", tx_info.mptr[data_position + u16cnt]));
		seg_info.ftds_buf_ptr[seg_info.packet_buf_length] = tx_info.mptr[data_position + u16cnt];
		seg_info.packet_buf_length++;
	}

	ret_sts = 1;

	return ret_sts;

}

#ifndef FT_OBC
/**
 * @brief This function used to read data from file for the requested file position
 *
 *
 */
static uint8_t read_data_from_file(uint32_t file_position)
{
	uint8_t ret_sts = 0;
	uint8_t *file_read_loc_ptr;
	uint16_t u16cnt;

	fseek(tx_info.fptr, file_position, SEEK_SET);

	file_read_loc_ptr = os_malloc_eram(tx_info.packet_data_size);
	if(file_read_loc_ptr != NULL)
	{
		fread(file_read_loc_ptr, tx_info.packet_data_size, 1, tx_info.fptr);

#ifdef FTM_CRC_CORRUPT_EN
		if(tx_info.pckt_seq_num == 3)
		{
			file_read_loc_ptr[0] = file_read_loc_ptr[0] & 0x12;
			DEBUG_CPRINT(("#### Warning ! Manual CRC corrupt at SEQ 3 in 1st byte\n"));
		}
#endif
		for (u16cnt = 0; u16cnt < tx_info.packet_data_size; u16cnt++)
		{
			//DEBUG_CPRINT((" %02X", file_read_loc_ptr[u16cnt]));
			seg_info.ftds_buf_ptr[seg_info.packet_buf_length] = file_read_loc_ptr[u16cnt];
			seg_info.packet_buf_length++;
		}

		os_free(file_read_loc_ptr);
		ret_sts = 1;
	}
	else
	{
		DEBUG_CPRINT(("FTM malloc error file read\n"));
	}

	return ret_sts;

}
#endif


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


/**
 * @brief This function used to re opening the file on sender side
 *
 *
 */
uint8_t ft_file_reopen_upload()
{
	uint8_t ret = 0;
#ifndef FT_OBC
	if(input_file_name != NULL)
	{
		if ((tx_info.fptr = fopen(input_file_name, "rb")) == NULL)
		{
			DEBUG_CPRINT(("REOPEN | Error! opening %s file\n",input_file_name));
		}
		else
		{
			DEBUG_CPRINT(("Re Opening file %s\n", input_file_name));
			tx_info.file_opened = 1;
			ret = 1;
		}
	}
	else
	{
		DEBUG_CPRINT(("Re Opening Null error\n"));
	}
#endif
	return ret;
}


/**
 * @brief This function used to re opening the file on receiver side to download
 *
 *
 */
uint8_t ft_file_reopen_download(void)
{
	uint8_t ret = 0;
#ifndef FT_OBC
	if(dwnld_rqst.file_name != NULL)
	{
		if ((dwnld_rqst.fptr = fopen((char*)dwnld_rqst.file_name, "rb+")) == NULL)
		{
			DEBUG_CPRINT(("REOPEN | Error! opening %s file\n",dwnld_rqst.file_name));
		}
		else
		{
			DEBUG_CPRINT(("Re Opening file %s\n", dwnld_rqst.file_name));
			dwnld_rqst.file_opened = 1;
			ret = 1;
		}
	}
	else
	{
		DEBUG_CPRINT(("Re Opening Null error\n"));
	}
#endif
	return ret;
}


#ifndef FT_OBC


/**
 * @brief This function used to close a file
 *
 *
 */
void ft_file_close(FILE * fptr, uint8_t *file_open_sts, uint8_t tx_mode)
{
#ifndef FT_OBC
	if(tx_mode != 1)
	{
		if(*file_open_sts == 1)
		{
			fclose(fptr);
			*file_open_sts = 0;
		}
		else
		{
			DEBUG_CPRINT(("File closed already\n"));
		}
	}
#endif
}

#endif

/**
 * @brief This function manipulates the parameters required to start transmission
 * It will find the total number of sequence to be sent and FTDS frame length
 *
 *
 */
void segmentation(void)
{
	init_seg();
	tx_info.packet_data_size = tx_info.mtu_size - (DATA_TX_FRAME_CTRL_FIELD_LEN + MAX_SEQ_NUM_FIELD + MAX_DATA_LEN_FIELD);
	/*making packet size in multiples of 4 */
	tx_info.packet_data_size = tx_info.packet_data_size / 4;
	tx_info.packet_data_size = tx_info.packet_data_size * 4;
	tx_info.packet_data_size_copy = tx_info.packet_data_size;

	tx_info.total_pckts = tx_info.size / tx_info.packet_data_size;
	tx_info.final_packet_data_size = tx_info.size % tx_info.packet_data_size;
	if (tx_info.final_packet_data_size != 0)
	{
		/*remaining packet observed */
		tx_info.total_pckts = tx_info.total_pckts + 1;
	}

	tx_info.pckt_seq_num = 0;
	tx_info.receiver_is_ready = FT_FTSR_RECEIVER_READY;

	if(tx_info.tx_mode == 0)
	{
#ifndef FT_OBC
		DEBUG_CPRINT(("Opening file %s for segmentation", input_file_name));
		if ((tx_info.fptr = fopen(input_file_name, "rb")) == NULL)
		{
			DEBUG_CPRINT(("SEG | Error! opening %s file",input_file_name));
			ft_fsm_change_state(FT_Idle_State);
			ft_set_err_sts(ft_input_file_open_error);
		}
		else
		{
			tx_info.file_opened = 1;
			/*update session id */
			ftds_frame.ctrl.bit.file_tx_session_id = session_id;
			ft_fsm_change_state(FT_Sequence_Transmission);
		}
#endif
	}
	else if(tx_info.tx_mode == 1)
	{
		ftds_frame.ctrl.bit.file_tx_session_id = session_id;
		ft_fsm_change_state(FT_Sequence_Transmission);
	}
	else
	{
		// do nothing
	}

	DEBUG_CPRINT(("\nData packet frame length : %d in bytes", tx_info.packet_data_size));
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
	DEBUG_CPRINT(("\nTotal pack to be transfered is %lu \n", tx_info.total_pckts));
	log_id_with_one_arg(LOG_INFO_L1, LOG_FTM, TOTAL_PACK_TO_BE_TRANSFERED_IS_u32_, tx_info.total_pckts);

#pragma GCC diagnostic pop

}

void send_ftds(void)
{
	uint8_t u8cnt;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
	DEBUG_CPRINT(("\nPCK %lu :", seg_info.current_seg));
#pragma GCC diagnostic pop
	for (u8cnt = 0; u8cnt < seg_info.packet_buf_length; u8cnt++)
	{
		DEBUG_CPRINT(("%02X ", seg_info.ftds_buf_ptr[u8cnt]));
	}

	DEBUG_CPRINT(("\n"));
}




/**
 * @brief This function used to encode FTDS frame.
 * It will read file based on sequence number and data length then fill the FTDS data buffer
 * with file content data
 *
 *
 */
uint16_t seg_preapre_frame_ftds(void)
{
	uint8_t * src_ptr;
	uint16_t u16cnt;
	uint32_t file_position;
	uint16_t ret_sts = 0;
	uint16_t required_buf_length;
	// read binary as packet with size MAX_BYTE_TRANSFER_IN_PACKT_FRAME
	if (tx_info.pckt_seq_num <= tx_info.total_pckts)
	{

		ftds_frame.ctrl.bit.file_tx_session_id = ft_tx_session_id_read();
		/*update seq number field length */
		ftds_frame.ctrl.bit.seq_num_field_size_sel = ftci_determine_size_field_holding_len(tx_info.pckt_seq_num);
		/*update data length field length */
		ftds_frame.ctrl.bit.data_length_field_size_sel = ftci_determine_size_field_holding_len(tx_info.packet_data_size);

		/*update segmnet type */
		if ((tx_info.pckt_seq_num == 1) && (tx_info.total_pckts != 1))
		{
			/*first data segment */
			ftds_frame.ctrl.bit.seg_type = 0;
		}
		else if (tx_info.pckt_seq_num < tx_info.total_pckts)
		{
			/*middle data segment */
			ftds_frame.ctrl.bit.seg_type = 1;
			tx_info.packet_data_size = tx_info.packet_data_size_copy;
		}
		else
		{
			/*last data segment */
			ftds_frame.ctrl.bit.seg_type = 2;
			/*update data length field length */
			tx_info.final_packet_data_size = tx_info.size - ((tx_info.pckt_seq_num-1) * tx_info.packet_data_size_copy);
			tx_info.packet_data_size = tx_info.final_packet_data_size;
			ftds_frame.ctrl.bit.data_length_field_size_sel = ftci_determine_size_field_holding_len(tx_info.packet_data_size);
		}

		file_position = (tx_info.pckt_seq_num - 1) *tx_info.packet_data_size_copy;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
		DEBUG_CPRINT(("F_POS:%lu ", file_position));
#pragma GCC diagnostic pop
		DEBUG_CPRINT(("CTRL:S%X T%X SQL%X SZL%X", ftds_frame.ctrl.bit.file_tx_session_id, ftds_frame.ctrl.bit.seg_type, ftds_frame.ctrl.bit.seq_num_field_size_sel, ftds_frame.ctrl.bit.data_length_field_size_sel));

		required_buf_length = (1 + (ftds_frame.ctrl.bit.seq_num_field_size_sel + 1) + (ftds_frame.ctrl.bit.data_length_field_size_sel + 1) + tx_info.packet_data_size);
		DEBUG_CPRINT((" TC_LEN:%u", required_buf_length));
		seg_info.ftds_buf_ptr = os_malloc_eram(required_buf_length);
		if(seg_info.ftds_buf_ptr != NULL)
		{
			seg_info.ftds_buf_ptr[0] = (uint8_t) ftds_frame.ctrl.all;
			seg_info.packet_buf_length = 1;

			DEBUG_CPRINT((" SEQ_L:"));
			src_ptr = (uint8_t*) &tx_info.pckt_seq_num;
			for (u16cnt = 0; u16cnt <= ftds_frame.ctrl.bit.seq_num_field_size_sel; u16cnt++)
			{
				DEBUG_CPRINT(("%02X", *src_ptr));
				seg_info.ftds_buf_ptr[seg_info.packet_buf_length] = *src_ptr;
				src_ptr++;
				seg_info.packet_buf_length++;
			}

			DEBUG_CPRINT((" DATA_L:"));
			src_ptr = (uint8_t*) &tx_info.packet_data_size;
			for (u16cnt = 0; u16cnt <= ftds_frame.ctrl.bit.data_length_field_size_sel; u16cnt++)
			{
				DEBUG_CPRINT(("%d", *src_ptr));
				seg_info.ftds_buf_ptr[seg_info.packet_buf_length] = *src_ptr;
				src_ptr++;
				seg_info.packet_buf_length++;
			}

			if(tx_info.tx_mode == 0)
			{
	#ifndef FT_OBC
				ret_sts = read_data_from_file(file_position);
	#endif
			}
			else
			{
				ret_sts = read_data_from_bulkdata(file_position);
			}
		}
		else
		{
			DEBUG_CPRINT(("FTM malloc fail\n"));
		}

	}
	else
	{
		ft_fsm_change_state(FT_Waitfor_FTSR);
		DEBUG_CPRINT(("Wait for response.. \n"));
	}

	DEBUG_CPRINT(("\n"));

	return ret_sts;

}


/**
 * @brief This function used to update the sequence number
 *
 *
 */
void seg_sequence_num_set(uint32_t set)
{
	tx_info.pckt_seq_num = set;
}


/**
 * @brief This function initialize segmentation data variables
 *
 *
 */
void init_seg()
{
	int index;
	seg_info.next_seg = 0;
	seg_info.current_seg = 0;
	seg_info.resend_rqst = 0;
	seg_info.next_seg = 0;
	seg_info.total_resend_seq_ID = 0;
	seg_info.rx_ping_count = 0;
	seg_info.ping_fail_cnt = 0;

	for (index = 0; index < 100; index++)
	{
		seg_info.resend_pckt_list[index] = 0;
	}
}
