/**
 * @file main.c
 *
 * @brief This file contains definitions for application main.
 *
 * @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 <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jansson.h>
#include "main.h"
#include "exo_hal_common.h"
#include "obc_tc_tm.h"
#include "mfcc_sample_app.h"
#include "tc_tm_mngr.h"
#include "tc_tm_mngr_buffer.h"
#include "comms_csw.h"
#include "xband_main.h"
#include "comms_uhf_main.h"
#include "csp_comms.h"
#include "schedular.h"
#include "exo_debug.h"
#include "nvm.h"
#include "exo_io_al_sos_timer.h"
#include "adcs_csw.h"
#include "obc_main.h"
#include "exo_common.h"
#include "sensor_driver_intf.h"
#include "version.h"
#include "eps_ctrlr.h"
#include "logger.h"
#include "sdr_app_main.h"
#include "utfw_test_main.h"
#include "gps_app.h"
#include "thruster_ctrlr.h"

extern s_obc_sock_info sns_sock;
extern s_obc_sock_info thr_sock;
extern s_obc_sock_info icd_soc_inf;
extern s_obc_sock_info obc_gs1_soc_inf;
extern s_obc_sock_info obc_gs2_soc_inf;
extern s_obc_sock_info obc_uhf_soc_inf;
extern s_obc_sock_info eps_soc_inf;
extern s_obc_sock_info adcs_soc_inf;
extern s_obc_sock_info sludp_tctm_soc_inf;

#if (PERIODIC_LOG_ENB == 1)
extern s_obc_sock_info logger_soc_inf;
#endif
extern s_obc_sock_info obc_iqs_sim_soc_inf;
extern s_obc_sock_info obc_iqs_sim_soc1_inf;

/**
 * @brief Test function
 */
void ral_main();

/**
 * @brief Global variables for thread and IPC operations
 */
s_exo_debug exo_dbg_cb;
os_status_t t_sts;
os_thread_handle_ptr thread1;
os_thread_handle_ptr thread2;
os_itc_msg_handle_t ipc_msg;
os_queue_handle_ptr itc_queue;

/**
 * @brief Global variable for flight mode
 */
volatile uint8_t flight_mode = 1;

/**
 * @brief Define the watchdog enable or disable
 */
#ifdef WDT_ENABLE
volatile uint8_t WDT_ENB =1;
#else
volatile uint8_t WDT_ENB =0;
#ifdef COREBOARD
#error "WDT Disabled"
#endif
#endif

/**
 * @brief Define the core board configuration
 */
#ifdef STM32F765xx
volatile uint8_t BOARD = 1;
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcpp"
#warning  "#####################This is not compiled for CORE BOARD #######################"
#pragma GCC diagnostic pop
volatile uint8_t BOARD = 0;
#endif

/**
 * @Brief Enable or disable the UART print
 */
#ifndef PRINT_UART_4                /*!< Enable print in UART_4 */
#ifndef PRINT_UART_5                /*!< Enable print in UART_5 */
volatile uint8_t PRINT_ENB=1;       /*!< Enable print */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcpp"
#warning  "################### Print was enabled #####################"
#pragma GCC diagnostic pop
#endif
#endif
#ifdef PRINT_UART_4
#ifndef PRINT_UART_5
volatile uint8_t PRINT_ENB=0;
#endif
#endif
#ifndef PRINT_UART_4
#ifdef PRINT_UART_5
volatile uint8_t PRINT_ENB=0;
#endif
#endif

/**
 * @brief Enable or disable Bootloader
 */
#ifdef BTLR_ON
volatile uint8_t BOOTLOADER =1;
#else
#ifdef COREBOARD
#warning  "################### Bootloader was disabled#####################"
//#error   "################### Bootloader was disabled#####################"
#endif
volatile uint8_t BOOTLOADER =0;
#endif

/**
 * @brief Configure the interface for ground station communication.
 */
#ifndef GSOBC_USB_EN
#ifdef COREBOARD
#error    "################### GS via Ethernet was enabled, so PS based validaiton cannot be performed #####################"
#endif
volatile uint8_t GS_OBC_VIA_USB_DISABLE=1;
#else
volatile uint8_t GS_OBC_VIA_USB_DISABLE =0;
#endif


/**
 * @brief Configure the interface for payload communication.
 */
#ifndef PSOBC_ETH_EN
volatile uint8_t PS_OBC_VIA_ETH_DISABLE =1;
#else
#ifdef COREBOARD
#error   "################### PS via Ethernet was enabled, #####################"
#endif
volatile uint8_t PS_OBC_VIA_ETH_DISABLE =0;
#endif

/**
 * @brief Configure the coreboard communication.
 */
#ifndef S_BAND_SIM_CB
volatile uint8_t CORE_BRD_S_BAND_SIM_DISABLE =1;
#else
#ifdef COREBOARD
#error  "################### S_BAND Simulator was enabled #####################"
#endif
volatile uint8_t CORE_BRD_S_BAND_SIM_DISABLE =0;
#endif

/**
 * @brief Configure EPS default state.
 */
#ifdef EPS_HRM_ON_DFLT
volatile uint8_t EPS_HRM_ON_DEFLT =1;
#else
#ifdef COREBOARD
#error  "################### HRM ON was disabled  #####################"
#endif
volatile uint8_t EPS_HRM_ON_DEFLT =0;
#endif
#if 0
/**
 * @brief Configure payload server default state.
 */
#ifndef EPS_PS_ON_DFLT
volatile uint8_t EPS_PS_ON_DEFLT_DISABLE =1;
#else
#ifdef COREBOARD
#error  "################### PS ON was default enabled  #####################"
#endif
volatile uint8_t EPS_PS_ON_DEFLT_DISABLE =0;
#endif
#endif
/**
 * @brief Disable heat sink
 */
#ifdef HEAT_SINK_DISABLE
#error  "################### HEAT SINK Auto ON/OFF was disabled   #####################"
#endif



void sock_info_init()
{
#if 0
   FILE* inf;
   inf = fopen("obc_soc_cfg.txt","r");
    if (inf == NULL) {
        DEBUG_CPRINT(("Error in opening Socket configuration file\n"));
    }

   fread(&obc_gs2_soc_inf,sizeof(s_obc_sock_info),1,inf);
   fscanf(inf,"\n");
   fread(&icd_soc_inf,sizeof(s_obc_sock_info),1,inf);
   fscanf(inf,"\n");
   fread(&adcs_soc_inf,sizeof(s_obc_sock_info),1,inf);
   fscanf(inf,"\n");
   fread(&eps_soc_inf,sizeof(s_obc_sock_info),1,inf);
   fscanf(inf,"\n");
   fread(&sns_sock,sizeof(s_obc_sock_info),1,inf);
   fscanf(inf,"\n");
   fread(&obc_gs1_soc_inf,sizeof(s_obc_sock_info),1,inf);
   fscanf(inf,"\n");
   fread(&obc_iqs_sim_soc_inf,sizeof(s_obc_sock_info),1,inf);
   fscanf(inf,"\n");
   fread(&obc_iqs_sim_soc1_inf,sizeof(s_obc_sock_info),1,inf);
   fscanf(inf,"\n");
   fread(&obc_uhf_soc_inf,sizeof(s_obc_sock_info),1,inf);
   fscanf(inf,"\n");
   fread(&thr_sock,sizeof(s_obc_sock_info),1,inf);
   fscanf(inf,"\n");
#if (PERIODIC_LOG_ENB == 1)
   fread(&logger_soc_inf,sizeof(s_obc_sock_info),1,inf);
   fscanf(inf,"\n");
#endif
   
   
   DEBUG_CPRINT(("\n GS2 SOC IP =%s Port =%s\n",obc_gs2_soc_inf.ip_addr,obc_gs2_soc_inf.port_num));
   DEBUG_CPRINT(("\n PS SOC IP =%s Port =%s\n",icd_soc_inf.ip_addr,icd_soc_inf.port_num));
   DEBUG_CPRINT(("\n ADCS SOC IP =%s Port =%s\n",adcs_soc_inf.ip_addr,adcs_soc_inf.port_num));
   DEBUG_CPRINT(("\n EPS SOC IP =%s Port =%s\n",eps_soc_inf.ip_addr,eps_soc_inf.port_num));
   DEBUG_CPRINT(("\n SENSOR SOC IP =%s Port =%s\n",sns_sock.ip_addr,sns_sock.port_num));
   DEBUG_CPRINT(("\n GS1 SOC IP =%s Port =%s\n",obc_gs1_soc_inf.ip_addr,obc_gs1_soc_inf.port_num));
   DEBUG_CPRINT(("\n IQS SOC IP =%s Port =%s\n",obc_iqs_sim_soc_inf.ip_addr,obc_iqs_sim_soc_inf.port_num));
   DEBUG_CPRINT(("\n IQS RX SOC IP =%s Port =%s\n",obc_iqs_sim_soc1_inf.ip_addr,obc_iqs_sim_soc1_inf.port_num));
   DEBUG_CPRINT(("\n UHF SOC IP =%s Port =%s\n",obc_uhf_soc_inf.ip_addr,obc_uhf_soc_inf.port_num));
   DEBUG_CPRINT(("\n THRUSTER SOC IP =%s Port =%s\n",thr_sock.ip_addr,thr_sock.port_num));
#if (PERIODIC_LOG_ENB == 1)
   DEBUG_CPRINT(("\n LOGGER SOC IP =%s Port =%s\n",logger_soc_inf.ip_addr,logger_soc_inf.port_num));
#endif
#endif
    /** Read the JSON config file */
    FILE *fp = fopen("obc_soc_cfg.json", "r");
    if (!fp) {
        perror("Unable to open file");
        exit(EXIT_FAILURE);
    }

    fseek(fp, 0, SEEK_END);
    long length = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    char *data = malloc(length+1);
    if (!data) {
        perror("Unable to allocate memory");
        exit(EXIT_FAILURE);
    }

    fread(data, 1, length, fp);
    fclose(fp);

    /** Load the JSON data */
    json_error_t error;
    json_t *root = json_loads(data, 0, &error);
    free(data);

    if (!root) {
        fprintf(stderr, "Error parsing JSON data: %s\n", error.text);
        exit(EXIT_FAILURE);
    }

    /** Get all the subsystems */
    json_t *nodes_array = json_object_get(root, "subsystem");
    if (!json_is_array(nodes_array)) {
        fprintf(stderr, "Error finding 'nodes' array in JSON data\n");
        json_decref(root);
        exit(EXIT_FAILURE);
    }

    size_t index;
    json_t *node_obj;
    /** Iterate through all the subsystems */
    json_array_foreach(nodes_array, index, node_obj) 
    {
        /** - Retrieve the name from the configuration */
        json_t *name_obj = json_object_get(node_obj, "name");

        /** - Retrieve the ip address from the configuration */
        json_t *ip_obj = json_object_get(node_obj, "ip_address");

        /** - Retrieve the port from the configuration */
        json_t *port_obj = json_object_get(node_obj, "port");

        /** - Convert the name object into string */
        const char *name = json_string_value(name_obj);

        /** - Convert the ip address object into string */
        const char *ip_address = json_string_value(ip_obj);

        /** - Convert the port object address object into integer */
        unsigned short port = json_integer_value(port_obj);

        /** - Compare the configuration name */
        if(0 == strcmp(name,"OBC<->GS"))
        {
            /**  - Copy the IP address from local variable to global config struct */
            strcpy(obc_gs2_soc_inf.ip_addr, ip_address);

            /**  - Copy the port from local variable to global config struct */
            obc_gs2_soc_inf.port= port;

            /**  - Copy the IP address from local variable to global config struct */
            strcpy(sludp_tctm_soc_inf.ip_addr, ip_address);

            /**  - Copy the port from local variable to global config struct */
            sludp_tctm_soc_inf.port = port;

        }

        /** - Compare the configuration name */
        else if(0 == strcmp(name,"OBC<->PS"))
        {
            /**  - Copy the IP address from local variable to global config struct */
            strcpy(icd_soc_inf.ip_addr, ip_address);

            /**  - Copy the port from local variable to global config struct */
            icd_soc_inf.port= port;
        }

        /** - Compare the configuration name */
        else if(0 == strcmp(name,"OBC<->ADCS"))
        {
            /**  - Copy the IP address from local variable to global config struct */
            strcpy(adcs_soc_inf.ip_addr, ip_address);

            /**  - Copy the port from local variable to global config struct */
            adcs_soc_inf.port= port;
        }

        /** - Compare the configuration name */
        else if(0 == strcmp(name,"OBC<->EPS"))
        {
            /**  - Copy the IP address from local variable to global config struct */
            strcpy(eps_soc_inf.ip_addr, ip_address);

            /**  - Copy the port from local variable to global config struct */
            eps_soc_inf.port= port;
        }

        /** - Compare the configuration name */
        else if(0 == strcmp(name,"OBC<->SENS"))
        {
            /**  - Copy the IP address from local variable to global config struct */
            strcpy(sns_sock.ip_addr, ip_address);

            /**  - Copy the port from local variable to global config struct */
            sns_sock.port= port;
        }

        /** - Compare the configuration name */
        else if(0 == strcmp(name,"OBC<->GS2"))
        {
            /**  - Copy the IP address from local variable to global config struct */
            strcpy(obc_gs1_soc_inf.ip_addr, ip_address);

            /**  - Copy the port from local variable to global config struct */
            obc_gs1_soc_inf.port= port;
        }
        /** - Compare the configuration name */
        else if(0 == strcmp(name,"OBC<->IQS_TX"))
        {
            /**  - Copy the IP address from local variable to global config struct */
            strcpy(obc_iqs_sim_soc_inf.ip_addr, ip_address);

            /**  - Copy the port from local variable to global config struct */
            obc_iqs_sim_soc_inf.port= port;
        }
        /** - Compare the configuration name */
        else if(0 == strcmp(name,"OBC<->IQS_RX"))
        {
            /**  - Copy the IP address from local variable to global config struct */
            strcpy(obc_iqs_sim_soc1_inf.ip_addr, ip_address);

            /**  - Copy the port from local variable to global config struct */
            obc_iqs_sim_soc1_inf.port= port;
        }
        /** - Compare the configuration name */
        else if(0 == strcmp(name,"OBC<->UHF"))
        {
            /**  - Copy the IP address from local variable to global config struct */
            strcpy(obc_uhf_soc_inf.ip_addr, ip_address);

            /**  - Copy the port from local variable to global config struct */
            obc_uhf_soc_inf.port= port;
        }
        /** - Compare the configuration name */
        else if(0 == strcmp(name,"OBC<->THRUST"))
        {
            /**  - Copy the IP address from local variable to global config struct */
            strcpy(thr_sock.ip_addr, ip_address);

            /**  - Copy the port from local variable to global config struct */
            thr_sock.port= port;
        }
#if (PERIODIC_LOG_ENB == 1)
        /** - Compare the configuration name */
        else if(0 == strcmp(name,"OBC<->LOG"))
        {
            /**  - Copy the IP address from local variable to global config struct */
            strcpy(logger_soc_inf.ip_addr, ip_address);

            /**  - Copy the port from local variable to global config struct */
            logger_soc_inf.port= port;
        }
#endif
        else
        {
            /** - Invalid configuration */
        }
    }
    DEBUG_CPRINT(("\n GS2 SOC IP =%s Port =%d\n",obc_gs2_soc_inf.ip_addr,obc_gs2_soc_inf.port));
    DEBUG_CPRINT(("\n PS SOC IP =%s Port =%d\n",icd_soc_inf.ip_addr,icd_soc_inf.port));
    DEBUG_CPRINT(("\n ADCS SOC IP =%s Port =%d\n",adcs_soc_inf.ip_addr,adcs_soc_inf.port));
    DEBUG_CPRINT(("\n EPS SOC IP =%s Port =%d\n",eps_soc_inf.ip_addr,eps_soc_inf.port));
    DEBUG_CPRINT(("\n SENSOR SOC IP =%s Port =%d\n",sns_sock.ip_addr,sns_sock.port));
    DEBUG_CPRINT(("\n GS1 SOC IP =%s Port =%d\n",obc_gs1_soc_inf.ip_addr,obc_gs1_soc_inf.port));
    DEBUG_CPRINT(("\n IQS SOC IP =%s Port =%d\n",obc_iqs_sim_soc_inf.ip_addr,obc_iqs_sim_soc_inf.port));
    DEBUG_CPRINT(("\n IQS RX SOC IP =%s Port =%d\n",obc_iqs_sim_soc1_inf.ip_addr,obc_iqs_sim_soc1_inf.port));
    DEBUG_CPRINT(("\n UHF SOC IP =%s Port =%d\n",obc_uhf_soc_inf.ip_addr,obc_uhf_soc_inf.port));
    DEBUG_CPRINT(("\n THRUSTER SOC IP =%s Port =%d\n",thr_sock.ip_addr,thr_sock.port));
#if (PERIODIC_LOG_ENB == 1)
    DEBUG_CPRINT(("\n LOGGER SOC IP =%s Port =%d\n",logger_soc_inf.ip_addr,logger_soc_inf.port));
#endif
}


/*
 *
 *  This function read the time zone then set time zone as "UTC0"
 *
 */
void set_time_zone_utc0(void);


/*
 *
 *  This function read the time zone then set time zone as "UTC0"
 *
 */
void set_time_zone_utc0(void)
{

        char *tz = getenv("TZ");
        if (tz != 0)
        {
        	printf("time zone read %s\n",tz);
        }
        else
        {
        	printf("time zone not set yet\n");
        }

    // setting UTC0 as time zone
        setenv("TZ", "UTC0", 1);
        tzset();

        tz = getenv("TZ");
        if (tz != 0)
        {
        	printf("time zone set %s\n",tz);
        }
        else
               {
        	printf("time zone set fail\n");
               }

       }

/**
 * @brief Application main for linux.
 */
int main()
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat"
    DEBUG_CPRINT(("Major Version : %llu",VER_u8DATA_SW_MAJOR));
    DEBUG_CPRINT(("Minor Version : %llu",VER_u8DATA_SW_MINOR));
#pragma GCC diagnostic pop
    //Enable Line buffering for stdout
    setvbuf(stdout, NULL, _IOLBF, 0);
    sock_info_init();
    log_init();
    nvm_init();
    rtc_nvm_reload();
    hal_init();
    /** TC/TM Init moved here, since Buffer FW must be initialized first */
    tc_tm_mngr_main();
    health_metric_buffer_main();
    //osal_test();
    adcs_csw_main();
    set_time_zone_utc0();   // setting UTC0 as time zone

    /** Initialisation/Configuration of CSP */
    CSP_Setup();
   
    comms_csw_init();
    comms_uhf_csw_init();

#ifdef SDR_XBAND_XLINK
    xband_init();
#endif

    sdr_app_init();
    eps_init();
    //tc_tm_sample_mfcc_sock_main();
    conops_sch_init();
    obc_main_init();
    sensors_init();
    gps_init();
    thruster_init();
#ifdef	MEM_DEBUG_ENB
	mem_debug_tracker_init();
#endif
#if defined(TEST_LAYER)
        test_task_init();
#endif

    while(1)
    {
        //DEBUG_CPRINT(("\n EXO Idle Task routine running"));
        sleep(3);
    }
    return(0);
}

