/**
 * @file ftci.c
 *
 * @brief This file contains functions that provides demonstration of
 *        usage of FTCI frame related APIs
 *
 * The functions provided in the file provides demonstration on
 * usage of FTCI 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 "ftci.h"
#include "file_integrity_check.h"
#include "file_transfer.h"
#include "ft_config.h"
#include "segmentation.h"
#include "tc_tm_sample.h"
#include "suspend_resume.h"
#include "logger.h"
#include "ft_sender.h"


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

/* Total = control field 1 + control field 2 + checksum + mtu size *total = 1+1+4+2 >> 8 bytes */
#define FTCI_FIXED_FRAME_FIELD_SIZE 12

/* Total = memory address + size + file name length + file name/path *total = 4+4+1+50(assumption) >> 59 bytes */
#define FTCI_VARAIABLE_FRAME_FIELD_SIZE 59

/* FTCI frame max length in bytes */
#define FTCI_FRAME_MAX_LENGTH     (FTCI_FIXED_FRAME_FIELD_SIZE + FTCI_VARAIABLE_FRAME_FIELD_SIZE)

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

static s_file_tx_ctrl_info 	ftci_frame;
static uint8_t 				ftci_buffer[FTCI_FRAME_MAX_LENGTH];

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

s_transfer_info 		tx_info;
uint8_t 			    session_id ;
s_string_info 			dest_path_name_info;
s_string_info 			ftci_frame_info = { ftci_buffer,
											0
										  };

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

extern uint16_t 		get_mtu;


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

#ifndef FT_OBC
/**
 * @brief This function used to collect all inputs required to form FTCI frame if operation mode selected for file transfer
 * Such inputs are file name, file name, etc
 *
 * @param[in]  void  : none
 *
 * @return status of frame forming
 * @retval FT_TC_FTCI : frame formed success
 * @retval FT_TC_TM_MAX_ID : fail
 */
static tc_tm_id_e_ft ftci_file(void);
#endif
/**
 * @brief This function used to collect all inputs required to form FTCI frame if operation mode selected for data transfer
 * Such inputs are data buffer to send, data size, etc
 *
 *
 * @param[in]  void  : none
 *
 * @return status of frame forming
 * @retval FT_TC_FTCI : frame formed success
 * @retval FT_TC_TM_MAX_ID : fail
 */
static tc_tm_id_e_ft ftci_bulkdata(void);

/**
 * @brief This function used to convert the size in bytes to requested unit such as KB, MB,GB
 * For example it will convert input size value 4096 bytes into 4KB if unit requested for KB
 *
 *
 * @param[in]  unit  : unit to convert bytes into other units such as to bytes, kilobytes, megabytes
 * @param[in]  size  : input file/data size
 *
 * @return converted value for requested unit
 * @retval it can be anything in decimal
 */
static uint32_t size_into_userdefined_format(uint8_t unit, uint32_t size);


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

/**
 * @brief This function used to collect all inputs required to form FTCI frame if operation mode selected for data transfer
 * Such inputs are data buffer to send, data size, etc
 *
 *
 */
static tc_tm_id_e_ft ftci_bulkdata(void)
{
	long int file_size;
	tc_tm_id_e_ft ret_sts = FT_TC_TM_MAX_ID;

	file_size = tx_info.size;
//	tx_info.mem_address = 0x0;
	tx_info.mtu_size = get_mtu;
	tx_info.unit = 0;
	tx_info.size = size_into_userdefined_format(tx_info.unit, file_size);

	/*calculate file checksum */
	tx_info.checksum = calc_checksum_bulkdata(tx_info.mptr, tx_info.size);
	tx_info.unit = 0;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
    DEBUG_CPRINT(("\nSession id is %d \n",ft_tx_session_id_read()));
	DEBUG_CPRINT(("\nSize of the file is %lu bytes\n", tx_info.size));
	log_id_with_one_arg(LOG_INFO_L1, LOG_FTM, SIZE_OF_THE_FILE_IS_u32_BYTES,tx_info.size);
#pragma GCC diagnostic pop
    DEBUG_CPRINT(("Size of the MTU is %d bytes \n", tx_info.mtu_size));
	log_id_with_one_arg(LOG_INFO_L1, LOG_FTM, SIZE_OF_THE_MTU_IS_u16_BYTES,tx_info.mtu_size);
	DEBUG_CPRINT(("Size in user unit is %u\n", tx_info.unit));
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
	DEBUG_CPRINT(("Checksum is 0x%luX\n", tx_info.checksum));
#pragma GCC diagnostic pop
	/*Prepare FTCI frame */
	tx_info.resume_ctxt_mode =0;
	ftci_encode_ftciframe();

  	if(tx_info.seq_loss_detect_enable != SEQ_LOSS_DETECTION_OFF)
	{
		ft_fsm_change_state(FT_Waitfor_FTSR);
		ft_set_timer(SENDER_RESPONSE_WAIT_TIME);
	}
	else
	{
		ft_fsm_change_state(FT_Wait_for_2sec);
	}
  	tx_info.receiver_is_ready = FT_FTSR_RECEIVER_READY_NOT_RECEIVED;

#ifndef FT_SAT
	ret_sts = FT_TC_FTCI;
	ft_send_tm_to_obc(FT_TC_FTCI,ftci_frame_info.str_ptr,ftci_frame_info.size);

#else
	ret_sts = FT_TM_FTCI;
	ft_send_tm_to_obc(FT_TM_FTCI,ftci_frame_info.str_ptr,ftci_frame_info.size);

#endif

	return ret_sts;
}



#ifndef FT_OBC
/**
 * @brief This function used to collect all inputs required to form FTCI frame if operation mode selected for file transfer
 * Such inputs are file name, file name, etc
 *
 *
 */
static tc_tm_id_e_ft ftci_file(void)
{
	long int file_size;
	uint8_t len_loc;
	tc_tm_id_e_ft ret_sts = FT_TC_TM_MAX_ID;

	len_loc = strlen(input_file_name) + 1;
	dest_path_name_info.str_ptr = os_malloc_eram(len_loc);
	if(dest_path_name_info.str_ptr != NULL)
	{
		memcpy(dest_path_name_info.str_ptr, input_file_name, len_loc);
		dest_path_name_info.size = len_loc;
		tx_info.file_name_size = len_loc;

		file_size = find_fileSize(input_file_name);
		if ((file_size != -1) && (file_size != 0))
		{
			//DEBUG_CPRINT(("Size of the file is %ld bytes \n", file_size));
			tx_info.mtu_size = get_mtu;
			tx_info.unit = 0;
			tx_info.size = size_into_userdefined_format(tx_info.unit, file_size);

			/*calculate file checksum */
			tx_info.checksum = ft_calc_filechecksum(input_file_name, file_size);
			tx_info.unit = 0;
            DEBUG_CPRINT(("\nSession id is %d \n",ft_tx_session_id_read()));
			DEBUG_CPRINT(("Size of the file is %u bytes\n", tx_info.size));
			DEBUG_CPRINT(("Size of the MTU is %d bytes \n", tx_info.mtu_size));
			DEBUG_CPRINT(("Size in user unit is %u  \n", tx_info.unit));
			DEBUG_CPRINT(("Checksum is 0x%X  \n", tx_info.checksum));

		    /*Prepare FTCI frame */
		    tx_info.resume_ctxt_mode =0;
		    ftci_encode_ftciframe();

		  	if(tx_info.seq_loss_detect_enable != SEQ_LOSS_DETECTION_OFF)
			{
				ft_fsm_change_state(FT_Waitfor_FTSR);
				ft_set_timer(SENDER_RESPONSE_WAIT_TIME);
			}
			else
			{
				ft_fsm_change_state(FT_Wait_for_2sec);
			}
		  	tx_info.receiver_is_ready = FT_FTSR_RECEIVER_READY_NOT_RECEIVED;

	#ifndef FT_SAT
			ret_sts = FT_TC_FTCI;
			ft_send_tm_to_obc(FT_TC_FTCI,ftci_frame_info.str_ptr,ftci_frame_info.size);
	#else
			ret_sts = FT_TM_FTCI;
			ft_send_tm_to_obc(FT_TM_FTCI,ftci_frame_info.str_ptr,ftci_frame_info.size);
	#endif
		}
		else
		{
			DEBUG_CPRINT(("\nFile opening error or File size is equal to 0\n"));
			ft_cancel_ftp();
			ft_set_err_sts(ft_input_file_open_error);
		}
	}
	else
	{
		DEBUG_CPRINT(("FTM Malloc fail\n"));
	}

	return ret_sts;
}
#endif


/**
 * @brief This function used to convert the size in bytes to requested unit such as KB, MB,GB
 * For example it will convert input size value 4096 bytes into 4KB if unit requested for KB
 *
 */
static uint32_t size_into_userdefined_format(uint8_t unit, uint32_t size)
{
	uint32_t ret_size;
	if (unit == 0)
	{
		/*size in bytes */
		ret_size = size;
	}
	else if (unit == 1)
	{
		/*size in KB */
		ret_size = size / 1024;
	}
	else if (unit == 2)
	{
		/*size in MB */
		ret_size = size / (1024 *1024);
	}
	else if (unit == 3)
	{
		/*size in GB */
		ret_size = size / (1024 * 1024 *1024);
	}
	else
	{
		ret_size = 0;
	}

	return ret_size;
}


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


/**
 * @brief This function used to determine number of fields required to accomadate the file/data
 * size
 * For example,  3 bytes is required to encode the file size 3760 in FTCI frame. so this function
 * will find the no of fields required to hold file size in FTCI frame.
 *
 */
uint8_t ftci_determine_size_field_holding_len(uint32_t size)
{
	uint8_t field_length;
	if (size < 0x100)
	{
		field_length = 0;
	}
	else if (size < 0x10000)
	{
		field_length = 1;
	}
	else if (size < 0x1000000)
	{
		field_length = 2;
	}
	else
	{
		field_length = 3;
	}

	return field_length;
}


/**
 * @brief This function used to encode FTCI frame with required inputs
 * File name, file size, MTU size, input data buffer to send, data size
 * which will be encoded in frame
 *
 *
 */
void ftci_encode_ftciframe()
{
	uint8_t u8cnt;
	uint8_t * loc_ptr;
	uint8_t * size_ptr;
	ftci_frame.ctrl_1.file_tx_session_id = ft_tx_session_id_read();
	ftci_frame.ctrl_1.resume_context_mode = tx_info.resume_ctxt_mode;
	ftci_frame.ctrl_2.transfer_mode_sel = tx_info.tx_mode;
	ftci_frame.ctrl_2.file_format = 3;
	ftci_frame.ctrl_2.unit = tx_info.unit;
	ftci_frame.ctrl_2.size_field_length = ftci_determine_size_field_holding_len(tx_info.size);
	ftci_frame.checksum = tx_info.checksum;
	ftci_frame.mtu_size = tx_info.mtu_size;
	ftci_frame.ctrl_1.seq_loss_detection_mode = tx_info.seq_loss_detect_enable;
	ftci_frame.ctrl_1.file_id = tx_info.file_id;
	ftci_frame.rx_con_fail_timeout = tx_info.rx_node_conn_fail_timeout;
	/*start framing frame */
	ftci_frame_info.size = 0;

	/*copy fixed filed */
	memcpy(ftci_buffer, &ftci_frame, sizeof(s_file_tx_ctrl_info));
	ftci_frame_info.size += FTCI_FIXED_FRAME_FIELD_SIZE;

	/*copy file/data size */
	loc_ptr = &ftci_buffer[sizeof(s_file_tx_ctrl_info)];

	size_ptr = (uint8_t*) &tx_info.size;
	for (u8cnt = 0; u8cnt <= ftci_frame.ctrl_2.size_field_length; u8cnt++)
	{
		*loc_ptr++ = *size_ptr++;
		ftci_frame_info.size++;
	}

	if (ftci_frame.ctrl_2.transfer_mode_sel == 0)
	{
		/*file transfer */
		DEBUG_CPRINT(("\r\nFile TX mode FTCI preparing"));
		log_id(LOG_INFO_L1, LOG_FTM, FILE_TX_MODE_FTCI_PREPARING);

		/*update file path length */
		*loc_ptr = dest_path_name_info.size;
		ftci_frame_info.size++;
		loc_ptr++;
		DEBUG_CPRINT(("\r\nPath:"));

		/*update destination path */
		for (u8cnt = 0; u8cnt < dest_path_name_info.size; u8cnt++)
		{
			*loc_ptr = dest_path_name_info.str_ptr[u8cnt];
			DEBUG_CPRINT(("%c", *loc_ptr));
			loc_ptr++;
			//dest_path_name_info.str_ptr++;
			ftci_frame_info.size++;
		}
	}
	else
	{
		DEBUG_CPRINT(("\r\n Bulkdata TX mode "));
                log_id(LOG_INFO_L1, LOG_FTM, FTM_BULKDATA_TX_MODE);
		/*bulk data transfer */
		*((uint32_t*) loc_ptr) = tx_info.mem_address;
		ftci_frame_info.size = ftci_frame_info.size + 4;
	}

	DEBUG_CPRINT(("\r\n"));

}



/**
 * @brief This function used to prepare ftci frame based on two operation mode such as
 * file and data mode
 *
 */
void ftci(void)
{

	if(tx_info.tx_mode == 0)
	{
#ifndef FT_OBC
		ftci_file();
#endif
	}
	else if(tx_info.tx_mode == 1)
	{
		ftci_bulkdata();
	}
	else
	{
		// do nothing
	}

}

/**
 * @brief This function used to initialize ftci
 *
 */
void ftci_init(void)
{
	memset(&tx_info,0,sizeof(s_transfer_info));
	tx_info.app_id = 0xFFFF;
	tx_info.rx_node_conn_fail_timeout = 45;
}
