/******************************************************************************/
/**
 * @file file_transfer.c
 *
 * @brief This file contains functions that provides demonstration of
 *        usage of file transfer handler, tctm message receiving and sending APIs
 *
 * The functions provided in the file provides demonstration on
 * usage of file transfer handler, tctm message receiving and sending 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 other headers                           */
/******************************************************************************/
#include "exo_types.h"
#include "exo_common.h"

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

#include "file_transfer.h"
#include "ft_receiver.h"
#include "ft_sender.h"
#include "ft_timer.h"
#include "notify_apps.h"
#include "suspend_resume.h"
#include "ftsr.h"
#include "ftfci.h"
#include "ftci.h"
#include "ftds.h"
#include "ftsr_decode.h"
#include "ftfci_decode.h"
#include "ftci_decode.h"
#include "ftds_decode.h"
#include "ft_debug.h"
#include "logger.h"

/******************************************************************************/
/*                  Definition of local macros                  */
/******************************************************************************/

/* Timeout time if sband link failure happens during FTM */
#define SBAND_FAILURE_SCT_ENABLE_TIMEOUT          500 // in ms

/* Auto savecontext enable time */
#define AUTO_SCT_ENABLE_WAIT_TIME                 (SBAND_FAILURE_SCT_ENABLE_TIMEOUT/FT_HANDLER_DELAY_MS)

/* Sband link failure wait time */
#define CREATE_SBAND_LINK_FAILURE_CASE_WAIT_TIME  (4000 / FT_HANDLER_DELAY_MS)  // 3ms = 4000


/* This macro will add stack overflow test code tha will increase the stack usage for the task ft_handler */
//#define ENABLE_STACK_OVERFLOW_TEST_HOOK

#define FTM_QUEUE_BUF_CNT						   20
/******************************************************************************/
/*                      Definition of local variables                         */
/******************************************************************************/
static eSystemState 					eState = FT_Init;
static ft_compl_and_error_status 		ft_err_and_percent_info;
static e_ft_current_process 			ft_process = ft_process_na;
static strFTM_queue_tctm_info 			s_ftm_queue_read;
static strFTM_queue_tctm_info 			s_ftm_queue_send;
static os_timer_handle_ptr  			ft_handler_timer;
static os_sem_handle_ptr 				ft_hdlr_sem;
static uint16_t 						manual_sband_fail_create_cnt;
static uint8_t 							sband_manual_failure_simulate=0;
static uint8_t 							PLACE_IN_EXT_RAM_MEM ftm_queue_buffer_tx[FTM_QUEUE_BUF_CNT][FTM_QUEUE_TCTM_PAYLOAD_BUFFER_SIZE];
static uint8_t 							ftm_queue_buf_cntr_tx=0;
static uint8_t 							PLACE_IN_EXT_RAM_MEM ftm_queue_buffer_rx[FTM_QUEUE_BUF_CNT][FTM_QUEUE_TCTM_PAYLOAD_BUFFER_SIZE];
static uint8_t 							ftm_queue_buf_cntr_rx=0;
static uint8_t							ft_service_enable_sts;
/******************************************************************************/
/*                      Definition of exported variables                      */
/******************************************************************************/
static uint8_t 							ftds_ack_en=0;
ft_data_write_cblk_type 				ft_ram_write_fp;
os_queue_handle_ptr 					ft_queue_hdle_rcv;
os_queue_handle_ptr 					ft_queue_hdle_sen;


/******************************************************************************/
/* LOCAL FUNCTION PROTOTYPES                                                  */
/******************************************************************************/

/**
 * @brief This function is used to start timer
 *
 *
 * @param[in]  void  : none
 *
 * @return none
 * @retval none
 */
static void ft_timer_init(void);

//stop the timer
//static void ft_timer_deinit(void);

/**
 * @brief This function initialize file transfer module
 *
 * @param[in]  void  : none
 *
 * @return none
 * @retval none
 */
static void init_file_transfer(void);


/**
 * @brief This function used for testing purpose to simulate sband failure case
 *
 * @param[in]  void  : none
 *
 * @return none
 * @retval none
 */
static void sband_clear_cntr(void);

/**
 * @brief This function monitoring the FTDS frames are receiving continously
 * this function manually check each FTDS has received in 4 seconds interval time
 * send sband comm status as fail if not recived packet in 4 seconds
 *
 * @param[in]  void  : none
 *
 * @return status of ftds frame reception
 * @retval 1 -> indicates reception normal , 0 -> reception stopped
 */
static uint8_t sband_get_link_sts(void);



/**
 * @brief This is the callback function which will be called by ft timer interrupt
 *
 * @param[in]  void  : none
 *
 * @return none
 * @retval none
 */
static void ft_timeout_expired_cb(void*);



/******************************************************************************/
/* LOCAL FUNCTION DEFINITION                                                 */
/******************************************************************************/

/**
 * @brief This is the callback function which will be called by ft timer interrupt
 *
 */
static void ft_timeout_expired_cb(void* params)
{
    os_sem_give(ft_hdlr_sem);
}


/**
 * @brief This is the initialization function for FTM timer
 *
 *
 */

 /**
  * @brief This is the initialization function for FTM timer
  *
  *
  */
void ft_service_enable(void)  //timer in seconds
 {
	if(!ft_service_enable_sts)
	{
	    os_timer_start(ft_handler_timer,(os_tick_time_t)FT_HANDLER_DELAY_MS);
		ft_service_enable_sts = 1;
		os_delay(1);
	}
 }



 /**
  * @brief This is the initialization function for FTM timer
  *
  *
  */
 void ft_service_disable(void)  //timer in seconds
 {
		if(ft_service_enable_sts)
		{
		    os_timer_stop(ft_handler_timer);
		    ft_service_enable_sts = 0;
		}
 }



 /**
  * @brief This is the initialization function for FTM timer
  *
  *
  */
 uint8_t ft_get_service_activity_status(void)  //timer in seconds
 {
		return ft_service_enable_sts;
 }


 /**
  * @brief This function is used to start timer
  *
  *
  */
 static void ft_timer_init(void)
{
   //timer to create interrupt once at specified interval
     os_timer_create(&ft_handler_timer,"ft_timer_periodic",PERIODIC,ft_timeout_expired_cb,NULL);
}



/**
 * @brief This function initialize file transfer module
 *
 */
static void init_file_transfer(void)
{
//	ft_init_notify();
	init_sus_res();
	ft_init_timer();
	ftci_init();
	ft_tx_session_id_update(FT_SESSION_ID_CLR);
}

/**
 * @brief This function used for testing purpose to simulate sband failure case
 *
 */
static void sband_clear_cntr(void)
{
	manual_sband_fail_create_cnt = 0;
}

/**
 * @brief This function monitoring the FTDS frames are receiving continously
 * this function manually check each FTDS has received in 4 seconds interval time
 * it returns sband comm status as fail if not recived packet in 4 seconds
 *
 */
static uint8_t sband_get_link_sts(void)
{
	uint8_t comm_sts;
	if(manual_sband_fail_create_cnt < CREATE_SBAND_LINK_FAILURE_CASE_WAIT_TIME)
	{
		manual_sband_fail_create_cnt++;
		comm_sts = 1;
	}
	else
	{
		manual_sband_fail_create_cnt = 0;
		comm_sts = 0;
	}
	return comm_sts;
}





/******************************************************************************/
/* GLOBAL FUNCTION DEFINITION                                                 */
/******************************************************************************/

/**
 * @brief This function used to set the current file transfer FSM state
 *
 *
 */
void ft_fsm_change_state(eSystemState set)
{
	eState = set;
}


/**
 * @brief This function used to return the current file transfer state machine state
 *
 *
 */
eSystemState ft_fsm_read_state()
{
	return eState;
}


/**
 * @brief This function used to print the current file transfer state machine state
 *
 *
 */
void print_current_fsmstate(void)
{
	switch (eState)
	{

		case FT_Init:
			DEBUG_CPRINT(("FSM : FT_Init\n"));
			break;

		case FT_Idle_State:
			DEBUG_CPRINT(("FSM : FT_Idle_State\n"));

			break;

		case FT_Send_Upload_Request:
			DEBUG_CPRINT(("FSM : FT_Send_Upload_Request\n"));
			break;

		case FT_Segmentation:
			DEBUG_CPRINT(("FSM : FT_Segmentation\n"));
			break;

		case FT_Sequence_Transmission:
			DEBUG_CPRINT(("FSM : FT_Sequence_Transmission\n"));
			break;

		case FT_Waitfor_FTSR:
			DEBUG_CPRINT(("FSM : FT_Waitfor_FTSR\n"));
			break;

		case FT_Suspended:
			DEBUG_CPRINT(("FSM : FT_Suspended\n"));
		break;

		case FT_Resumed:
			DEBUG_CPRINT(("FSM : FT_Resumed\n"));
			break;

		default:
			break;
	}
}


/**
 * @brief This function used to send tm message frames from FTM to OBC module
 *
 */
void ft_send_tm_to_obc(uint16 tm_id, uint8 *tm_data, uint16 tm_data_len)
{
	uint16_t u8cnt;
	if(tm_data != NULL)
	{
		for(u8cnt=0; u8cnt < tm_data_len ; u8cnt++)
		{
			ftm_queue_buffer_tx[ftm_queue_buf_cntr_tx][u8cnt] = tm_data[u8cnt];
		}
		s_ftm_queue_send.Q_payload_ptr = &ftm_queue_buffer_tx[ftm_queue_buf_cntr_tx][0];
		ftm_queue_buf_cntr_tx++;
		if(ftm_queue_buf_cntr_tx > (FTM_QUEUE_BUF_CNT-1))
		{
			ftm_queue_buf_cntr_tx = 0;
		}
		if(tm_id == FT_TM_FTSR)
		{
			s_ftm_queue_send.src_dst_id = ft_get_app_id_rx();
		}
		else
		{
			s_ftm_queue_send.src_dst_id = ft_read_app_id_tx();
		}
		s_ftm_queue_send.Q_payload_len = tm_data_len;
		s_ftm_queue_send.tc_tm_id = tm_id;
    	os_queue_send(ft_queue_hdle_sen, &s_ftm_queue_send, 200);
	}
	else
	{
		DEBUG_CPRINT(("OBC receive Null error\n"));
	}
}



/**
 * @brief This function used to monitor sband link status
 * This will get get the status during FTM transmission, if comm is broken FTM will perform auto save context
 *
 */
void ft_get_sband_link_status(void)
{
	uint8_t comm_sts;

	/* manual sband failure trigger for validtaion purpose */
	if(sband_manual_failure_simulate == 1)
	{
		comm_sts = sband_get_link_sts();
	}
	else
	{
		/* TODO
		 * actual sband status api needs to be called here
		 */
		comm_sts = 1;   /* always set sband = true */
	}

	if(comm_sts != 1)
	{
		DEBUG_CPRINT(("[FTM - SBAND link fail] Auto savecontext activated.\n"));
		log_id(LOG_ERROR, LOG_FTM, FTM_SBAND_LINK_FAIL_AUTO_SAVECONTEXT_ACTIVATED);
		ft_set_master_slave(sus_res_slave);
		ft_suspend(Suspend_Savecontext_Mode_rqst,20,3);
		prepare_sts_and_notify_receiver_app(transfer_suspended_auto_savecontext);
	}
	else
	{
		/* do nothing */
	}
}



/**
 * @brief This function used to return the current file transfer state
 *
 */
e_ft_current_process get_file_transfer_state(void)
{
    return ft_process;
}


/**
 * @brief This function used to set the file transfer state
 *
 */
void set_file_transfer_state(e_ft_current_process set)
{
	 ft_process = set;
}


/**
 * @brief This function used to receive tctm message frames from OBC module
 *
 */
void ft_payload_parser(uint16 tc_tm_id, uint8_t *payload_ptr, uint16_t payload_len)
{

		if(payload_ptr != NULL)
		{
			sband_clear_cntr();

			switch(tc_tm_id)
			{
			case FT_TC_FTCI:
			case FT_TM_FTCI:
					ft_set_app_id_rx(s_ftm_queue_read.src_dst_id);
					ft_receive_ftci(payload_ptr,payload_len);
				break;

			case FT_TC_FTFCI:
			case FT_TM_FTFCI:
					ft_set_app_id_rx(s_ftm_queue_read.src_dst_id);
					ft_receive_ftfci(payload_ptr,payload_len);
				break;

			case FT_TC_FTDS:
			case FT_TM_FTDS:
					ft_set_app_id_rx(s_ftm_queue_read.src_dst_id);
					ft_receive_ftds(payload_ptr,payload_len);
				break;

			case FT_TC_FTSR:
			case FT_TM_FTSR:
					ft_receive_ftsr(payload_ptr,payload_len);
				break;

			case FT_TC_FTDB:
				ft_decode_debug_request_frame(payload_ptr,payload_len);
				break;

			case FT_TM_FTDR:
				ft_decode_debug_response_frame(payload_ptr,payload_len);
				break;

			default :
				DEBUG_CPRINT(("Invalid input Parser\n"));
				break;
			}


		}
		else
		{
			DEBUG_CPRINT(("FT receive Null error\n"));
			log_id(LOG_ERROR, LOG_FTM, FT_RECEIVE_NULL_ERROR);

		}
}


/**
 * @brief This function used to register ram write callback function which is
 * to be used by FTM to maintain FTDS frame
 *
 */
uint8_t ft_register_ramwrite_cblk(ft_data_write_cblk_type fp)
{
	uint8_t ret_sts = 0;
	if(fp != NULL)
	{
		DEBUG_CPRINT(("FTM/OBC RAM write callback registered\n"));
		ft_ram_write_fp = fp;
		ret_sts = 1;
	}
	return ret_sts;
}




#if 0
void determine_src_dest_id(uint16 tc_tm_id, uint16_t set_src_dst)
{

		switch(tc_tm_id)
		{
		case FT_TC_FTCI:
		case FT_TM_FTCI:
	        ft_set_app_id_rx(set_src_dst);
			break;

		case FT_TC_FTFCI:
		case FT_TM_FTFCI:
	        ft_set_app_id_rx(set_src_dst);
			break;

		case FT_TC_FTDS:
		case FT_TM_FTDS:
	        ft_set_app_id_rx(set_src_dst);
			break;

		case FT_TC_FTSR:
		case FT_TM_FTSR:

			break;


		default :
			break;

	}
}

#endif

/**
 * @brief This function used to get the current error state
 *
 */
e_ft_error_sts ft_get_error_sts(void)
{
	return ft_err_and_percent_info.error;
}


uint8_t ft_get_upload_percentage_compl(void)
{
	return ft_err_and_percent_info.upload_percentile;
}


/**
 * @brief This function used to set the error state
 *
 *
 */
void ft_set_err_sts(e_ft_error_sts err)
{
	ft_err_and_percent_info.error = err;
}

/**
 * @brief This function used to set the percentage of file operation has completed
 *
 *
 */
void ft_set_percentile_sts(uint8_t percent)
{
	ft_err_and_percent_info.upload_percentile = percent;
}



//stop the timer
/*static void ft_timer_deinit(void)
{
      os_timer_stop(ft_handler_timer);
}*/


 /**
  * @brief This function used to trigger sband failure scenario for testing purpose
  *
  *
  */
void ft_sband_manual_failure(uint8_t set)
{
	sband_manual_failure_simulate = set;
}


/**
 * @brief This function used to enable/disable ack response for transmission (testing purpose)
 *
 *
 */
void ft_ftds_ack_enable_set(uint8_t set)
{
	ftds_ack_en = set;
}

#ifdef ENABLE_STACK_OVERFLOW_TEST_HOOK

void overflow_hook_function()
{

	uint8_t buffer[1496];
	uint16_t u8cntr;
	buffer[0] = 45;
	buffer[0] = buffer[0];

	for (u8cntr = 0; u8cntr < 1496 ; u8cntr++)
	{
		buffer[u8cntr] = 0;
	}

}

#endif
/**
 * @brief This API manages sender and receiver operation of file transfer
 *
 */
void ft_handler(void* params)
{
#ifdef ENABLE_STACK_OVERFLOW_TEST_HOOK
	 uint8_t trigger_stack_overflow = 0;
#endif	
    ft_timer_init();
	os_queue_create(&ft_queue_hdle_rcv, FTM_QUEUE_BUF_CNT, sizeof(strFTM_queue_tctm_info), "FT_RECV");
	os_queue_create(&ft_queue_hdle_sen, FTM_QUEUE_BUF_CNT, sizeof(strFTM_queue_tctm_info), "FT_SEN");
	os_sem_create_bin(&ft_hdlr_sem, "FT_SEM");

    init_file_transfer();
    set_file_transfer_state(ft_process_na);
    os_delay(100);

    while(1)
    {
    	os_sem_take(ft_hdlr_sem,os_wait_forever);
        ft_update_timer();
        ft_debug_handle();

        if(ft_process == ft_download)
        {
            ft_statemachine_download();  //fsm downloader
        }
        else if(ft_process == ft_upload)
        {
            ft_statemachine_upload();   //fsm uploader
        }
        else
        {
            // do nothing
        }

#ifdef ENABLE_STACK_OVERFLOW_TEST_HOOK
//      set breakpoint in the below if case and update value as 1 for trigger_stack_overflow 
        if(trigger_stack_overflow == 1)
        {
        	overflow_hook_function();
        	trigger_stack_overflow = 0;
        }
#endif

    }
}


/**
 * @brief This API receives incoming IPC message from TCTM
 *
 */
void ft_rx_thread(void* params)
{
    os_delay(3000);
	while(1)
	{
		os_queue_receive(ft_queue_hdle_rcv, &s_ftm_queue_read, os_wait_forever);
		ft_payload_parser(s_ftm_queue_read.tc_tm_id,s_ftm_queue_read.Q_payload_ptr,s_ftm_queue_read.Q_payload_len);
	}
}

/*
 * Return queue buffer save pointer
 */
uint8_t* ft_get_queue_rx_buffer_address(void)
{
	uint8_t *ptr;
	ptr = &ftm_queue_buffer_rx[ftm_queue_buf_cntr_rx][0];
	ftm_queue_buf_cntr_rx++;
	if(ftm_queue_buf_cntr_rx > (FTM_QUEUE_BUF_CNT-1))
	{
		ftm_queue_buf_cntr_rx = 0;
	}
	return ptr;
}



void ft_initialisation(void)
{
	ft_service_enable_sts = 0;
    os_thread_create(FTM_FSM_HANDLER, T_FTM_FSM_HANDLER_STACK_SIZE, P_FTM_FSM_HANDLER, "ft_hdlr", ft_handler, NULL, NULL, NULL);
    os_thread_create(FTM_TCTM_RCV, T_FTM_TCTM_RCV_STACK_SIZE, P_FTM_TCTM_RCV, "ft_rx_thrd", ft_rx_thread, NULL, NULL, NULL);
}

