/**
 * @file log_thread.c
 *
 * @brief This file contains function definitions for debug logging.
 *
 * @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 "main.h"
#include "exo_common.h"
#include "log_thread.h"
#include "exo_common.h"
#include "nvm.h"
#include "exo_io_al_sos_timer.h"
#include "system_init_exit.h"
#include "comms_csw.h"
#include "ft_upload.h"
#include "logger_linux_socket.h"
#include  "exo_tctm_ipc.h"

#include "exo_ahw_al_spi_to_uart_common.h"

#define MAX_PRNT_LIMIT	(128)           ///< Maximum length of characters in single print
#define MAX_TRANS_RNG	(400)           ///< Maximum uart transfer limit
#define MAX_NVM_TIME    (10*60)         ///< Minimum NVM commit interval time

extern volatile uint8_t system_error;
extern ioal_uart_hdle ioal_huart1;
extern ioal_uart_hdle ioal_huart6;
extern ioal_uart_hdle ioal_huart4;
extern ioal_uart_hdle ioal_huart5;
logger_cb_t log_cb;
volatile uint8_t nvm_force_commit_flag=0;

#ifndef LINUX_TEMP_PORT
#define LOGGER_BUFF_SIZE (50*1024)                               ///< Total logger buffer size
uint8_t PLACE_IN_EXT_RAM_MEM mem_alloc_loc_temp[LOGGER_BUFF_SIZE];
uint8_t PLACE_IN_EXT_RAM_MEM ftp_trns_buff[LOGGER_BUFF_SIZE];
#else
#define LOGGER_BUFF_SIZE (1*1024)                                ///< Total logger buffer size
uint8_t mem_alloc_loc_temp[LOGGER_BUFF_SIZE];
#endif
#define MEM_SDRAM_START_ADDRESS (mem_alloc_loc_temp)            ///< Logger buffer start address in SDRAM
uint8_t trans_buff[MAX_TRANS_RNG];

#define IS_IRQ_MODE()             (__get_IPSR() != 0U)

/**
 * @brief This API push the data to buffer.
 */
uint8_t push_data_to_buff(char *ptr,int len)
{
	ral_status_t ret;
	if(len > MAX_PRNT_LIMIT)
	{
		len = MAX_PRNT_LIMIT;
		log_cb.trancated_print_cnt++;
	}
	if(((log_cb.end_idx+len) < log_cb.uart_idx) && log_cb.roll_back_flag && log_cb.uart_roll_back_flag) //redo
	{
		log_cb.drop_print_cnt++;
		return 0;
	}
	if((IS_IRQ_MODE()==0))
	{
		ret = os_sem_take(log_cb.sync_prot,50);
		if(ret == ral_success)
		{
			if((log_cb.end_idx+len) > LOGGER_BUFF_SIZE)
			{
				uint32_t temp_len = LOGGER_BUFF_SIZE - log_cb.end_idx;
				log_cb.roll_back_flag = 1;
				os_memcpy((log_cb.logger_buff + log_cb.end_idx),ptr,temp_len);
				log_cb.end_idx = (log_cb.end_idx + temp_len) % LOGGER_BUFF_SIZE;
				len = len - temp_len;
				ptr = ptr + temp_len;
				if(log_cb.uart_idx == 0)
				{
					os_sem_give(log_cb.sync_prot);
					return 0;
				}
			}
			os_memcpy((log_cb.logger_buff + log_cb.end_idx),ptr,len);
			log_cb.end_idx = (log_cb.end_idx + len) % LOGGER_BUFF_SIZE;
			if(log_cb.roll_back_flag)
			{
				log_cb.start_idx = (log_cb.end_idx + 1)%LOGGER_BUFF_SIZE;
			}
			os_sem_give(log_cb.sync_prot);
		}
		else if(ret == ral_err_timeout)
		{
			log_cb.time_out++;
		}
		else
		{
			log_cb.semtake_error++;
		}
	}
	else
	{
		log_cb.is_irq_mode++;
	}

	return 1;
}

/**
 * @brief This API send the debug log to ground station.
 */
void send_buff_data(uint32_t len,uint32_t mtu)
{
	int buff_idx = (int)log_cb.end_idx - len;
	if(buff_idx >= 0)
	{
		os_memcpy(ftp_trns_buff,(log_cb.logger_buff + buff_idx),len);
	}
	else if(buff_idx < 0)
	{
		int nxt_len = log_cb.end_idx;
		int prv_len = 0;
		if(log_cb.roll_back_flag)
		{
			prv_len = len - log_cb.end_idx;
			buff_idx = LOGGER_BUFF_SIZE - prv_len - 1;
			os_memcpy(ftp_trns_buff,(log_cb.logger_buff + buff_idx),prv_len);
		}
		os_memcpy((ftp_trns_buff+prv_len),log_cb.logger_buff,nxt_len);
		len = nxt_len + prv_len;
	}
	ft_register_sender_app(TM_SRC_FTM_DBGLOG, log_thread_notificataion_api_upld);
	ft_config_app_id(TM_SRC_FTM_DBGLOG);
	ft_mode_select(M_DATA);
	ft_config_mtu_size(mtu);
	ft_config_bulk_data_input_buffer(ftp_trns_buff,len);
	ft_sequenceLoss_mode_select(1);
	ft_transfer_request(Start_Transmission_rqst,0);
}

/**
 * @brief This API initialize the logger thread.
 */
void log_thread_init()
{
	log_cb.logger_buff = MEM_SDRAM_START_ADDRESS;
	log_cb.total_buff_size = 0;
	log_cb.start_idx = 0;
	log_cb.end_idx = 0;
	log_cb.uart_idx = 0;
	log_cb.roll_back_flag = 0;
	log_cb.uart_roll_back_flag = 1;
	log_cb.time_out =0;
	log_cb.is_irq_mode =0;
	log_cb.semtake_error =0;
	os_sem_create_bin(&log_cb.sync_prot, "LOG_SYNC_PROT");
	os_sem_create_bin(&log_cb.wakeup, "LOG_WAKEUP");
	os_sem_give(log_cb.wakeup);
	os_thread_create(DEBUG_LOGGER,T_LOG_THREAD_STACK_SIZE,P_DEBUG_LOGGER,"LOGGER THREAD",log_hdlr_thrd,NULL,NULL,NULL);
}

/**
 * @brief This function returns valid buffer length.
 */
int get_trns_len()
{
	int len = log_cb.end_idx - log_cb.uart_idx;
	if(len == 0)
	{
		return len;
	}
	else if(len < 0)
	{
		len = LOGGER_BUFF_SIZE - log_cb.uart_idx;
		if(len > MAX_TRANS_RNG)
		{
			len = MAX_TRANS_RNG;
		}
		else
		{
			log_cb.uart_roll_back_flag = 0;
		}
	}
	if(len > MAX_TRANS_RNG)
	{
		len = MAX_TRANS_RNG;
	}
	return len;
}

/**
 * @brief This API is unblock the logger thread
 */
void wakeup_print()
{
	os_sem_take(log_cb.sync_prot,os_wait_forever);
	if(get_trns_len())
	{
		os_sem_give(log_cb.sync_prot);
		os_sem_give(log_cb.wakeup);
	}
	else
	{
		os_sem_give(log_cb.sync_prot);
	}
}

/**
 * @brief Logger thread entry function.
 */
void log_hdlr_thrd(void* args)
{
	while(1)
	{
//		if(nvm_force_commit_flag==1)
//		{
//			DEBUG_CPRINT(("First NVM Commit\n"));
//			exo_wdt_refresh();
//			os_delay(100);
//			nvm_commit();
//			nvm_force_commit_flag=0;
//		}

		os_sem_take(log_cb.wakeup,10000);
		os_sem_take(log_cb.sync_prot,os_wait_forever);
#ifndef DBG_PRNT_EN
		log_cb.uart_idx = log_cb.end_idx;
#endif
		int len = get_trns_len();
		os_sem_give(log_cb.sync_prot);
		if(len)
		{
			uint8_t *ptr = trans_buff;
			os_memcpy(ptr,(log_cb.logger_buff+log_cb.uart_idx),len);
			os_sem_take(log_cb.sync_prot,os_wait_forever);
			log_cb.uart_idx = (log_cb.uart_idx + len) % LOGGER_BUFF_SIZE;
			os_sem_give(log_cb.sync_prot);

#ifdef ENABLE_SOC_PRINT
			logger_simu_data_write((uint8_t *)ptr,(uint16_t)len);
#else

#if(UART_PRINT)

#if defined(JANUS1) && defined(PRINT_UART_4)
			io_hal_uart_transmit(&ioal_huart4, (uint8_t *)ptr,(uint16_t)len,300);
#endif
#ifdef PRINT_UART_5
			io_hal_uart_transmit(&ioal_huart5, (uint8_t *)ptr,(uint16_t)len,300);
#endif

#if defined(JANUS2) && defined(PRINT_SPI_UART_CH2)
			ahw_al_uart_conv_transmit(UART_EXPANDER_MAX14830_OBC,MAX14830_CH2,ptr,len);
#endif

#ifdef STM32F769xx
			io_hal_uart_transmit(&ioal_huart1, (uint8_t *)ptr,(uint16_t)len,300);
#endif

#endif
#endif

#if(SWV_PRINT)
			int DataIdx;

			for (DataIdx = 0; DataIdx < len; DataIdx++)
			{
				__io_putchar(*ptr++);
			}
#endif
		}
	}
}


/**
 * @brief This function print the error metric.
 */
void printf_metrix_sts()
{
	if((log_cb.time_out) || (log_cb.is_irq_mode) || (log_cb.semtake_error))
	{
		DEBUG_CPRINT(("sem take time out:%lu irq_sts:%lu sem_take_err:%lu\n",log_cb.time_out,log_cb.is_irq_mode,log_cb.semtake_error));
	}
}

/**
 * @brief This API to notify the file transfer error status.
 */
void log_thread_notificataion_api_upld(ft_notification_info *ft_sts)
{
	uint8_t init_start = 0;
	init_start = init_start;
	DEBUG_CPRINT(("Log App notification API invoked !!!!\n"));
	if(ft_sts->status == transfer_upload_success)
	{
		DEBUG_CPRINT(("1. upload success\n"));
		init_start = 0;
	}
	else if(ft_sts->status == crc_error)
	{
		DEBUG_CPRINT(("1. upload failed/ CRC error\n"));
		init_start = 0;
	}
	else if(ft_sts->status == transfer_suspended)
	{
		DEBUG_CPRINT(("1. upload suspended by receiver [Timeout mode]\n"));
		display_ft_sts();
	}
	else if(ft_sts->status == transfer_suspend_accepted)
	{
		DEBUG_CPRINT(("1. upload Suspend - Timeout mode Accepted by Receiver\n"));
		display_ft_sts();
	}
	else if(ft_sts->status == transfer_resume_accepted)
	{
		DEBUG_CPRINT(("1. upload Resume - Timeout mode Accepted by Receiver\n"));
	}
	else if(ft_sts->status == transfer_resume_savecontext_accepeted)
	{
		DEBUG_CPRINT(("1. upload Resume - Savecontext mode Accepted by Receiver\n"));
		init_start = 1;
	}
	else if(ft_sts->status == transfer_upload_ready)
	{
		DEBUG_CPRINT(("1. Receiver ready for Download\n"));
	}
	else if(ft_sts->status == invalid_receiver_app_id)
	{
		DEBUG_CPRINT(("1. Upload request cancelled - Receiver API not registered !!!\n"));
		init_start = 0;
	}
	else if(ft_sts->status == transfer_upload_rejected)
	{
		DEBUG_CPRINT(("1. Upload request rejected - Issue in FTCI decoding in Rx node !!!\n"));
		init_start = 0;
	}
	else if(ft_sts->status == transmission_terminated_receiver_not_responsive)
	{
		DEBUG_CPRINT(("1. Upload Terminated due to Receiver not responsive !!!\n"));
		init_start = 0;
	}
	else if(ft_sts->status == transmission_terminated_by_rx_node)
	{
		DEBUG_CPRINT(("1. download Terminated by Receiver node\n"));
		init_start = 0;
	}
	else if(ft_sts->status == transfer_suspend_tout_not_accepted)
	{
		DEBUG_CPRINT(("1. suspend timeout request not completed\n"));
	}
	else if(ft_sts->status == transfer_suspend_savecontext_failed)
	{
		DEBUG_CPRINT(("1. suspend savecontext request not completed\n"));
	}
	else if(ft_sts->status == transfer_rseume_tout_not_accepted)
	{
		DEBUG_CPRINT(("1. resume timeout request not completed\n"));
	}
	else if(ft_sts->status == transfer_resume_restorecontext_failed)
	{
		DEBUG_CPRINT(("1. resume savecontext request not completed\n"));
	}
	else
	{
		DEBUG_CPRINT(("Invalid Notification not processed\n"));
	}
	DEBUG_CPRINT(("\n-------------/End/---------------\n\n"));
}
