/**
 * @file mfcc_sample_app.c
 *
 * @brief This file contains functions that provides demonstration of
 *        usage of TC TM Library APIs
 *
 * The functions provided in the file provides demonstration on
 * usage of TC TM library 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 "mfcc_sample_app.h"
#include "exo_osal.h"
#include "exo_ral_cmn.h"
#include "mfcc_tc_tm_sample.h"
#include "tc_common.h"
#include "tc_tm_lib_common.h"
#include "tm_common.h"
#include <stdio.h>

#ifdef LINUX_TEMP_PORT
#include <unistd.h>
#else
#include "stm32f7xx_hal.h"
#endif

#define ENBL_DBG_PRNT (1)
/**
 * @brief Global variable to keep track of target pointing response status
 */
uint32 trgt_pt_rsp_sts = 0;

/**
 * @brief Global variable to keep track of location information
 */
static tctm_gnss_loc_info_s gli;

/**
 * @brief Global variables for thread and IPC operations
 */
extern os_thread_handle_ptr obc_tc_thread;
os_thread_handle_ptr mfcc_tm_thread;
os_thread_handle_ptr mfcc_tc_thread;
os_itc_msg_handle_t mfcc_tc_ipc_msg;
os_itc_msg_handle_t mfcc_tm_ipc_msg;

/**
 * @brief Main function that creates threads for sending TC and
 * receiving TM
 */
os_status_t tc_tm_sample_mfcc_main(void)
{
    os_status_t t_sts;

    /* Create thread that sends Telecommand binary data stream via
       IPC interface */
    t_sts = os_thread_create(&mfcc_tc_thread, 1024, 24, "TC sender thread",
                             mfcc_tc_send_thread, NULL, NULL, NULL);

    /* Create thread that receives Telemetry binary data stream via
       IPC interface */
    t_sts = os_thread_create(&mfcc_tm_thread, 1024, 24, "TM receiver thread",
                             mfcc_tm_listener_thread, NULL, NULL, NULL);

    return t_sts;
}

/**
 * @brief Function that prepares Telecommand payload
 * based on user input
 */
void mfcc_tc_send_thread(void *params)
{
    s_tc_frame *tc_orig_app;
    uint8 *dst;
    uint16 tc_len;
    int32 size = 0;
    uint8 *tc_data = NULL;
    tctm_trgt_pt_req_s tpr;
    uint32 tc_id = 0;
#ifdef ENBL_DBG_PRNT
    uint32 loop = 0;
#endif

    /* Encryption related params */
    s_crypt_params param;
    uint8 key[] = {
        0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03,
        0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03,
        0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03,
        0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03,
        0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03,
        0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f};

    uint8 iv[16] = {0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};

    /* Send buffer and frame memory allocations */
    tc_orig_app = (s_tc_frame *)malloc(sizeof(s_tc_frame));
    dst = (unsigned char *)malloc(MAX_TC_BUFF_SIZE);

    /* The above part need to be built in the Go Language */

    /* From here onwards TC TM library is used */
    /* Initialize telecommand structure instance */
    tc_orig_init(tc_orig_app);
    /* Reset the sequence number */
    tc_orig_reset_seq_no();
    /* Configure Start of Frame */
    tc_orig_cfg_sof(tc_orig_app, TC_FRAME_SOF);
    /* Configure Satellite ID */
    tc_orig_cfg_sat_id(tc_orig_app, RECV_SAT_ID);
    /* Configure Ground station ID */
    tc_orig_cfg_gs_id(tc_orig_app, SOURCE_GROUND_STN_ID);
    /* Enable encryption */
    tc_orig_cfg_tc_ctrl(tc_orig_app, CRYPTO_ENCRYP_ENBL);
    /* Configure Quality of Service */
    tc_orig_cfg_qos(tc_orig_app, TC_QOS_CFG);
    /* Configure source address */
    tc_orig_cfg_sa_id(tc_orig_app, TC_SOURCE_ID);
    /* Configure destination address */
    tc_orig_cfg_da_id(tc_orig_app, TC_DESTINATION_ID);

    while(1)
    {
#ifdef LINUX_TEMP_PORT
        sleep(3);
#else
        HAL_Delay(3000);
#endif
        /* Get Telecommand ID to be sent from Ground station to Satellite */
        DEBUG_CPRINT(("Enter Telecommand ID (Possible TC ID [1:2])\n"));
#ifdef LINUX_TEMP_PORT
        scanf(" %d", &tc_id);
#else
        tc_id = 1;
#endif
        switch(tc_id)
        {
            /* Telecommand prepartion for Target Pointing Request */
            case TC_TRGT_PT:
            {
                /* pack TC data */
                tc_len = sizeof(tpr);
                tc_data = (uint8_t *)malloc(tc_len);
                tpr.lat = 44;
                tpr.longit = 72;
                tpr.alt = 101;
                tpr.pld_hw_id = 3;
                tpr.act_tmr_min = 10;
                tpr.act_tmr_sec = 0;
                tpr.act_tmr_ms = 0;
                tc_trgt_point_req_enc(tc_data, &tpr);

                /* Configure Telecommand ID */
                tc_orig_cfg_tc_id(tc_orig_app, TC_TRGT_PT);
                /* Configure Telecommand Length */
                tc_orig_cfg_tc_len(tc_orig_app, tc_len);
                /* Configure Telecommand payload */
                tc_orig_cfg_tc_data(tc_orig_app, tc_data);

                /* Configure Encryption parameters */
                tc_orig_app->cmac_hmac = (uint8_t *)malloc(SHA256_HASH_SIZE);
                os_memcpy(param.iv, iv, 16);
                param.key_size = 256;
                param.key = (unsigned char *)malloc(param.key_size);
                os_memcpy(param.key, key, param.key_size);
                param.encrypt_mode = CRYPT_AES_CFB8;
                tc_orig_select_encryption(&param);

#ifdef ENBL_DBG_PRNT
                DEBUG_CPRINT(("MFCC APP: TC payload unencrypted : "));
                for(loop = 0; loop < tc_len; loop++)
                    DEBUG_CPRINT(("%x ", tc_data[loop]));
                DEBUG_CPRINT(("\n"));
#endif

                /* Generate Telecommand as binary data stream */
                size = tc_pack(dst, tc_orig_app);
                DEBUG_CPRINT(("MFCC APP: tc_pack size is %d \n", size));
#ifdef ENBL_DBG_PRNT
                DEBUG_CPRINT(("MFCC APP: TC payload encrypted : "));
                for(loop = 0; loop < tc_len; loop++)
                    DEBUG_CPRINT(("%x ", tc_data[loop]));
                DEBUG_CPRINT(("\n"));
#endif

                /* Send generated Telecommand via IPC interface */
                // tcp_send(addr, port, dst, size);
                mfcc_tc_ipc_msg.src_entity = 1;
                mfcc_tc_ipc_msg.pld_info = 10;
                mfcc_tc_ipc_msg.Msg_id = 100;
                mfcc_tc_ipc_msg.Msg_len = size;
                mfcc_tc_ipc_msg.pld.pld_ptr = dst;
                os_itc_msg_send(&mfcc_tc_ipc_msg, obc_tc_thread, 1000);

#ifdef ENBL_DBG_PRNT
                DEBUG_CPRINT(("MFCC APP: TC byte stream: "));
                for(loop = 0; loop < size; loop++)
                    DEBUG_CPRINT(("%x ", dst[loop]));
                DEBUG_CPRINT(("\n"));
#endif
            }
            break;

            case TC_LOC_INFO:
            {
                /* pack TC payload */
                tc_len = 0;
                tc_data = (uint8_t *)malloc(tc_len);

                /* Configure Telecommand ID */
                tc_orig_cfg_tc_id(tc_orig_app, TC_LOC_INFO);
                /* Configure Telecommand Length */
                tc_orig_cfg_tc_len(tc_orig_app, tc_len);
                /* Configure Telecommand payload */
                tc_orig_cfg_tc_data(tc_orig_app, tc_data);

                /* Configure Encryption parameters */
                tc_orig_app->cmac_hmac = (uint8_t *)malloc(SHA256_HASH_SIZE);
                os_memcpy(param.iv, iv, 16);
                param.key_size = 256;
                param.key = (unsigned char *)malloc(param.key_size);
                os_memcpy(param.key, key, param.key_size);
                param.encrypt_mode = CRYPT_AES_CFB8;
                tc_orig_select_encryption(&param);

                /* Generate Telecommand as binary data stream */
                size = tc_pack(dst, tc_orig_app);
                /* Send generated Telecommand via socket interface */
                // tcp_send(addr, port, dst, size);
                mfcc_tc_ipc_msg.src_entity = 1;
                mfcc_tc_ipc_msg.pld_info = 10;
                mfcc_tc_ipc_msg.Msg_id = 100;
                mfcc_tc_ipc_msg.Msg_len = size;
                mfcc_tc_ipc_msg.pld.pld_ptr = dst;
                os_itc_msg_send(&mfcc_tc_ipc_msg, obc_tc_thread, 1000);
            }
            break;

            default:
            {
                DEBUG_CPRINT("Invalid TC ID Received - %d.Please try again.\n",
                       tc_id));
            }
        }
    }
}

/**
 * @brief This API creates socket interface to receive Telemetry data
 */
void mfcc_tm_listener_thread(void *params)
{
    int32 loop = 0;
    uint8_t *buffer;
    uint16_t bytesize = 0;
    while(1)
    {
        // os_queue_receive(itc_queue,&rcv_val,os_wait_forever);
        // DEBUG_CPRINT(("queue data received===%d\n",rcv_val));
        os_queue_receive(mfcc_tm_thread->queue_info_t->itc_queue_id,
                         &mfcc_tm_ipc_msg, os_wait_forever);
        DEBUG_CPRINT(("\nthread_test2\r\n"));
        DEBUG_CPRINT(("Received data=%hu", mfcc_tm_ipc_msg.Msg_id));
        buffer = mfcc_tm_ipc_msg.pld.pld_ptr;
        bytesize = mfcc_tm_ipc_msg.Msg_len;
#ifdef ENBL_DBG_PRNT
        DEBUG_CPRINT(("MFCC APP: Received TM byte stream: "));
        for(loop = 0; loop < bytesize; loop++)
        {
            DEBUG_CPRINT(("%x ", buffer[loop]));
            DEBUG_CPRINT(("\n"));
        }
#endif

        /* Invoke Telemetry parser */
        mfcc_tm_parser(buffer);
#ifdef LINUX_TEMP_PORT
        sleep(1);
#else
        HAL_Delay(1000);
#endif
    }
}

/**
 * @brief Function that parses Telemetry payload
 * and invokes corresponding Telemetry decoder
 */
int32 mfcc_tm_parser(uint8 *tm_stream_data)
{
    s_tm_frame tm_frame;
    s_crypt_params param;
    s_tm_ctrl ctrl_param;
    uint16 tm_len = 0;
    uint16 seq_no = 0;
    uint16 tm_id = 0;
#ifdef ENBL_DBG_PRNT
    int32 loop = 0;
    int32 un_pck_sts = -1;
#endif

    /* Define decryption key */
    uint8 key[] = {
        0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03,
        0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03,
        0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03,
        0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03,
        0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x01, 0x02, 0x03,
        0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c, 0x0d, 0x0e, 0x0f};

    /* Define IV parameter for AES decryption */
    uint8 iv[16] = {0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};

    /* Allocation of memory for TM data */
    tm_frame.cmac_hmac = (uint8 *)malloc(SHA256_HASH_SIZE);
    tm_frame.tm_data = (uint8 *)malloc(MAX_TM_BUFF_SIZE);

    /* Unpack TM from binary stream of bytes */
#ifdef ENBL_DBG_PRNT
    un_pck_sts = tm_unpack(tm_stream_data, &tm_frame);
    DEBUG_CPRINT(("MFCC APP: Unpack status is  %d\n", un_pck_sts));
#else
    tm_unpack(tm_stream_data, &tm_frame);
#endif

    /* Decode TM header and payload information */
    if(NULL != tm_frame.tm_data)
    {
        /* Extract TM sequence number */
        tm_term_extract_seq_no(&tm_frame, &seq_no);
        /* Validate sequence number */
        validate_seq_num(seq_no);
        /* Extract Telemetry ID */
        tm_term_extract_tm_id(&tm_frame, &tm_id);
        /* Extract Encryption configuration */
        tm_term_extract_tm_ctrl(&tm_frame, &ctrl_param);
        /* When encryption is enabled in received TM */
        if(ctrl_param.encrypt)
        {
            tm_term_extract_tm_len(&tm_frame, &tm_len);
#ifdef ENBL_DBG_PRNT
            DEBUG_CPRINT(("MFCC APP: Received Data is encrypted \n"));
            DEBUG_CPRINT(("MFCC APP: Received Data length is %d\n", tm_len));
#endif
            /* Configure decryption parameters */
            os_memcpy(param.iv, iv, 16);
            param.key_size = 256;
            param.key = (unsigned char *)malloc(param.key_size);
            os_memcpy(param.key, key, param.key_size);
            param.encrypt_mode = CRYPT_AES_CFB8;
            /* Extract TM payload length */
            tm_term_extract_tm_len(&tm_frame, &tm_len);

#ifdef ENBL_DBG_PRNT
            DEBUG_CPRINT(("MFCC APP: Undecrypted TM data is "));
            for(loop = 0; loop < tm_len; loop++)
                DEBUG_CPRINT(("%x ", tm_frame.tm_data[loop]));
            DEBUG_CPRINT(("\n"));
#endif
            /* Decrypt Telemetry data */
            tm_orig_decrypt_data(&param,
                                 (const unsigned char *)tm_frame.tm_data,
                                 tm_len, (unsigned char *)tm_frame.tm_data);

#ifdef ENBL_DBG_PRNT
            DEBUG_CPRINT(("MFCC APP: Decrypted TM data is "));
            for(loop = 0; loop < tm_len; loop++)
                DEBUG_CPRINT(("%x ", tm_frame.tm_data[loop]));
            DEBUG_CPRINT(("\n"));
#endif
        }
        else
        {
#ifdef ENBL_DBG_PRNT
            /* Extract TM payload length */
            tm_term_extract_tm_len(&tm_frame, &tm_len);
            DEBUG_CPRINT(("MFCC APP: Received Data is unencrypted \n"));
            DEBUG_CPRINT(("MFCC APP: Received Data length is %d\n", tm_len));
            DEBUG_CPRINT(("MFCC APP: Data is "));
            for(loop = 0; loop < tm_len; loop++)
                DEBUG_CPRINT(("%x ", tm_frame.tm_data[loop]));
            DEBUG_CPRINT(("\n"));
#endif
        }
    }

    /* Decode TM payload information */
    switch(tm_id)
    {
        case TM_TRGT_PT:
        {
            os_memcpy(&trgt_pt_rsp_sts, tm_frame.tm_data, 4);
            /* Check target pointing configuration response status */
            if(!trgt_pt_rsp_sts)
                DEBUG_CPRINT(("Target point configuration success\n"));
            else
                DEBUG_CPRINT(("Target point configuration failure\n"));
        }
        break;

        case TM_LOC_INFO:
        {
            /* Decode Location information */
            tm_gnss_loc_info_dec(tm_frame.tm_data, &gli);
        }
        break;

        default:
        {
            DEBUG_CPRINT(("Invalid TM ID received %d\n", tm_id));
        }
    }
    /* Free allocated memories */
    free(tm_frame.tm_data);
    if(ctrl_param.encrypt)
    {
        free(param.key);
        free(tm_frame.cmac_hmac);
    }

    return 0;
}

/**
 * @brief Function that validates sequence number
 */
void validate_seq_num(uint16 actual_seq_no)
{
    static uint16 expected_seq_no = 1;
    uint32 missing_frames, i = 0;
    if(expected_seq_no != actual_seq_no)
    {
        if(actual_seq_no > expected_seq_no)
        {
            missing_frames = actual_seq_no - expected_seq_no;
            for(i = 0; i < missing_frames; i++)
            {
                DEBUG_CPRINT(Warning !!! Missing frame. Sequence number : %d \n",
                       expected_seq_no));
                expected_seq_no++;
            }
        }
        else
        {
            DEBUG_CPRINT( Warning !!! Received the same packet again : %d \n",
                   actual_seq_no));
        }
    }
    expected_seq_no++;
}
