/**
 * @file tc_terminate.c
 *
 * @brief This file contains functions to extract info from telecommand frame
 *
 * The functions provided in the file helps to extract the required information
 * from the tele command frame with crc and encryptions. It also provides the
 * functionalities to unpack buffer and store it in the s_tc_frame structure.
 */
/*********************************************************************/
#include "tc_common.h"
static int32 validate_tc_eof(const uint8* frame_ptr, s_tc_frame *tc_orig,\
                                                   uint16 curr_index, uint16 len);
/**
* local instance of s_tc_terminator structure to store tc_id and trigger
* function pointer
*/

static s_tc_terminator tc_term_local;

/// Utility buffer
uint8_t tc_out[SHA256_HASH_SIZE];

/**
 * @brief This API stores the start of frame (sof1 and sof2) from the sof1 and
 * sof2 member of passed in s_tc_frame type struct to passed in sof1 and 2
 * value. Refer section 3.2.1.1 in TC_TM_GS & Space Communication Protocol
 * Design document for more information
 */
int8 tc_term_extract_sof(s_tc_frame *tc_term, uint8 *sof1, uint8 *sof2)
{
    int8 status;

    if((NULL != tc_term) && (NULL != sof1) && (NULL != sof2))
    {
        *sof1 = tc_term->sof1;
        *sof2 = tc_term->sof2;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the start of frame (sof) from the sof member of
 * passed in s_tc_frame type struct to passed in sof value. Refer
 * section 3.2.1.2 in TC_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 tc_term_extract_tc_ctrl(s_tc_frame *tc_term, s_tc_ctrl *tc_ctrl)
{
    int8 status;

    if((NULL != tc_term) && (NULL != tc_ctrl))
    {
        s_tc_ctrl_util info;
        info.byte = tc_term->tc_ctrl;
        tc_ctrl->source_dest = info.ctrl.source_dest;
        tc_ctrl->tx_mode     = info.ctrl.tx_mode;
        tc_ctrl->comm_mode   = info.ctrl.comm_mode;
        tc_ctrl->auth        = info.ctrl.auth;
        tc_ctrl->encrypt     = info.ctrl.encrypt;
        tc_ctrl->reserved_1  = info.ctrl.reserved_1;
        tc_ctrl->reserved_2  = info.ctrl.reserved_2;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the seq_no member of passed
 * in s_tc_frame type struct to passed in seq_no value. Refer
 * section 3.2.1.4 in TC_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 tc_term_extract_seq_no(s_tc_frame *tc_term, uint16 *seq_no)
{
    int8 status;

    if((NULL != tc_term) && (NULL != seq_no))
    {
        *seq_no = tc_term->seq_no;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the sat_id member of passed
 * in s_tc_frame type struct to passed in sat_id value. Refer
 * section 3.2.1.5 in TC_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 tc_term_extract_sat_id(s_tc_frame *tc_term, uint16 *sat_id)
{
    int8 status;

    if((NULL != tc_term) && (NULL != sat_id))
    {
        *sat_id = tc_term->sat_id;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the gs_id of passed
 * in s_tc_frame type struct to passed in gs_id value. Refer
 * section 3.2.1.6 in TC_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 tc_term_extract_gs_id(s_tc_frame *tc_term, uint16 *gs_id)
{
    int8 status;

    if((NULL != tc_term) && (NULL != gs_id))
    {
        *gs_id = tc_term->gs_id;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the qos of passed in s_tc_frame type struct
 *  to passed in qos value. Refer section 3.2.1.7 in TC_TM_GS & Space
 *  Communication Protocol Design document for more information
 */
int8 tc_term_extract_qos(s_tc_frame *tc_term, uint16 *qos)
{
    int8 status;

    if((NULL != tc_term) && (NULL != qos))
    {
        *qos = tc_term->qos;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the sa_id of passed in s_tc_frame type
 * struct to passed in sa_id value. Refer section 3.2.1.8 in TC_TM_GS & Space
 *  Communication Protocol Design document for more information
 */
int8 tc_term_extract_sa_id(s_tc_frame *tc_term, uint16 *sa_id)
{
    int8 status;

    if((NULL != tc_term) && (NULL != sa_id))
    {
        *sa_id = tc_term->sa_id;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the da_id of passed in s_tc_frame type struct
 * to passed in da_id value. Refer section 3.2.1.9 in TC_TM_GS & Space
 * Communication Protocol Design document for more information
 */
int8 tc_term_extract_da_id(s_tc_frame *tc_term, uint16 *da_id)
{
    int8 status;

    if((NULL != tc_term) && (NULL != da_id))
    {
        *da_id = tc_term->da_id;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the rm_id of passed in s_tc_frame type
 * struct to passed in rm_id value. Refer section 3.2.1.10 in
 * TC_TM_GS & Space Communication Protocol Design document for more information
 */
int8 tc_term_extract_rm_id(s_tc_frame *tc_term, uint8 *rm_id)
{
    int8 status;

    if((NULL != tc_term) && (NULL != rm_id))
    {
        *rm_id = tc_term->rm_id;
        status = TC_TM_LIB_OK;
    }
    else
    {
        status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the tc_id of passed in s_tc_frame type struct
 * to passed in tc_id value. Refer section 3.2.1.11 in TC_TM_GS & Space
 * Communication Protocol Design document for more information
 */
int8 tc_term_extract_tc_id(s_tc_frame *tc_term, uint16 *tc_id)
{
    int8 status;

    if((NULL != tc_term) && (NULL != tc_id))
    {
        *tc_id = tc_term->tc_id;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the tc_len of passed in s_tc_frame type struct
 *  to passed in tc_len value. Refer section 3.2.1.12 in TC_TM_GS & Space
 * Communication Protocol Design document for more information
 */
int8 tc_term_extract_tc_len(s_tc_frame *tc_term, uint16 *tc_len)
{
    int8 status;

    if((NULL != tc_term) && (NULL != tc_len))
    {
        *tc_len = tc_term->tc_len;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the end of frame (eof) from the eof member of
 * passed in s_tc_frame type struct to passed in eof value. Refer
 * section 3.2.1.16 in TC_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 tc_term_extract_eof(s_tc_frame *tc_term, uint8 *eof)
{
    int8 status;

    if((NULL != tc_term) && (NULL != eof))
    {
        *eof = tc_term->eof;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API adds the tc_id and tc_func in the corresponding index of
 * tc_id[] and tc_func[] member of type s_tc_terminator
 */
int8 tc_term_add_tc_func(s_tc_terminator* tc_term, uint16 tc_id,\
                                             tc_trgr_ptr tc_func)
{
    int8 status;

    if((NULL != tc_term) && (NULL != tc_func))
    {
        tc_term->tc_id = tc_id;
        tc_term->tc_func[tc_id] = tc_func;
        tc_term_local.tc_id= tc_id;
        tc_term_local.tc_func[tc_id] = tc_func;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API removes the tc_id and tc_func in the corresponding index of
 * type s_tc_terminator
 */
int8 tc_term_remove_tc_func(s_tc_terminator* tc_term, uint16 tc_id)
{
    int8 status;

    if(NULL != tc_term)
    {
        tc_term->tc_id = -1;
        tc_term->tc_func[tc_id] = NULL;
        tc_term_local.tc_id = -1;
        tc_term_local.tc_func[tc_id] = NULL;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API copies the structure data to the input buffer(dst) to the
 *        input structure. It also computes crc and hmac(if enabled) for the
 *         input buffer and compares it with the embedded values.
 */
int32 tc_unpack(const uint8 *dst, uint16 len, s_tc_frame *tc_orig)
{
    int32 size = 0;
    size_t hmac_size = 0;
    int32 rslt = -1;
    uint8 crc_8 = 0;

    if((NULL != dst) && (NULL != tc_orig))
    {
        if( TC_MIN_LEN < len)
        {
            os_memcpy(&tc_orig->sof1, &dst[size], sizeof (tc_orig->sof1));
            size += sizeof (tc_orig->sof1);

            os_memcpy(&tc_orig->sof2, &dst[size], sizeof (tc_orig->sof2));
            size += sizeof (tc_orig->sof2);

            if((TC_TM_LIB_SOF1 == tc_orig->sof1) &&
                                    (TC_TM_LIB_SOF2 == tc_orig->sof2))
            {

                os_memcpy(&tc_orig->tc_ctrl, &dst[size], sizeof tc_orig->tc_ctrl);
                size += sizeof tc_orig->tc_ctrl;

                os_memcpy(&tc_orig->time_stamp,
                          &dst[size],
                          sizeof tc_orig->time_stamp);

                size += sizeof tc_orig->time_stamp;

                os_memcpy(&tc_orig->seq_no, &dst[size], sizeof tc_orig->seq_no);
                size += sizeof tc_orig->seq_no;

                // Populate the value based on 1 or 2 byte encoding
                size += get_extnd_val_16(&dst[size],
                                         &tc_orig->sat_id,
                                         sizeof tc_orig->sat_id);

                // Populate the value based on 1 or 2 byte encoding
                size += get_extnd_val_16(&dst[size],
                                         &tc_orig->gs_id,
                                         sizeof tc_orig->gs_id);

                os_memcpy(&tc_orig->qos, &dst[size], sizeof tc_orig->qos);
                size += sizeof tc_orig->qos;

                // Populate the value based on 1 or 2 byte encoding
                size += get_extnd_val_16(&dst[size],
                                         &tc_orig->sa_id,
                                         sizeof tc_orig->sa_id);

                // Populate the value based on 1 or 2 byte encoding
                size += get_extnd_val_16(&dst[size],
                                         &tc_orig->da_id,
                                         sizeof tc_orig->da_id);

               	os_memcpy(&tc_orig->rm_id, &dst[size], sizeof(tc_orig->rm_id));
                size +=sizeof(tc_orig->rm_id);

                // Populate the value based on 1 or 2 byte encoding
                size += get_extnd_val_16(&dst[size],
                                         &tc_orig->tc_id,
                                         sizeof tc_orig->tc_id);
                //tc_len is fixed as 2bytes, before it is of variable length of 1 or 2 bytes
               	os_memcpy(&tc_orig->tc_len, &dst[size], sizeof(tc_orig->tc_len));
                size +=sizeof(tc_orig->tc_len);


                rslt = validate_tc_eof(dst, tc_orig, size, len);

                if(TC_TM_LIB_OK == rslt)
                {

                    os_memcpy(tc_orig->tc_data, &dst[size], tc_orig->tc_len);
                    size += tc_orig->tc_len;

                    if(((tc_orig->tc_ctrl & TC_CTRL_AUTH_BIT)
                                        >> TC_CTRL_AUTH_POS) & 1)
                    {
                        hmac_size = compute_hmac (tc_orig->tc_data,
                                                 (const uint8*)TC_KEY,
                                                 strlen(TC_KEY),
                                                 tc_orig->tc_len,
                                                 tc_out);

                        os_memcpy(tc_orig->cmac_hmac, &dst[size], hmac_size);
                        size += hmac_size;
                        rslt = memcmp(tc_out, tc_orig->cmac_hmac, hmac_size);

                        if(0 != rslt)
                        {
                            rslt = TC_TM_LIB_HMAC_ERROR;
                        }
                        else
                        {
                            rslt = size;
                        }
                    }
                    else
                    {
                        rslt = size;
                    }
                    if (rslt > 0)
                    {
                        os_memcpy(&tc_orig->crc_8, &dst[size], sizeof (tc_orig->crc_8));
                        tc_orig_compute_crc (dst, &crc_8, size);
                        size += sizeof tc_orig->crc_8;

                        os_memcpy(&tc_orig->eof, &dst[size], sizeof (tc_orig->eof));
                        size += sizeof (tc_orig->eof);

                        rslt = size;

                        if(TC_TM_LIB_EOF != tc_orig->eof)
                        {
                            rslt = TC_TM_LIB_SOF_EOF_ERROR;
                        }
                        else if( crc_8 != tc_orig->crc_8)
                        {
                            rslt = TC_TM_LIB_CRC_ERROR;
                        }
                        else
                        {
                            rslt = size;
                        }
                    }
                    else
                    {
                        /**
                         *authentication failed for some reason. 
                         */
                    }
                }
                else
                {
                    /**
                     * End of frame values not matching with the expected
                     * values.
                     */
                     rslt = TC_TM_LIB_EOF_ERROR;
                }
            }
            else
            {
                /**
                 * Start of frame values not matching with the expected
                 * values.
                 */
                 rslt = TC_TM_LIB_SOF_EOF_ERROR;
            }
        }
        else
        {
            rslt = TC_TM_MIN_LEN_ERROR;
        }
    }
    else
    {
        rslt = TC_TM_LIB_NULL_PTR;
    }

    return rslt;

}

/**
 * @brief This API copies the structure data to the input buffer(dst) to the
 *        input structure. It also computes crc and hmac for the input buffer
 *        and compares it with the embedded values. It also triggers the user
 *        function if the user function is mapped with the received
 *        tc_orig->tc_id with tc_term_add_tc_func() api
 */
int8 tc_unpack_trigger(const uint8 *dst, uint16 len, s_tc_frame *tc_orig)
{
    int32 rslt = -1;
    rslt = tc_unpack(dst, len, tc_orig);

    if(0 < rslt)
    {
        rslt = tc_trgr_func(tc_orig);
    }
    else
    {
        /* error in unpacking. Pass the rslt as return value */
    }

    return rslt;
}

/**
 * @brief This API triggers the user function if the user function is
 *        mapped with the received tc_orig->tc_id with 
 *        tc_term_add_tc_func() api
 */
int8 tc_trgr_func (s_tc_frame *tc_orig)
{
    int8 status;
    if((NULL != tc_orig) && (NULL != tc_term_local.tc_func[tc_orig->tc_id]))
    {
        status = tc_term_local.tc_func[tc_orig->tc_id](tc_orig);
    }
    else
    {
        status = TC_TM_LIB_NULL_PTR;
    }
    return status;
}

static int32 validate_tc_eof(const uint8* frame_ptr, s_tc_frame *tc_orig,\
                                                   uint16 curr_index, uint16 len)
{
    int rslt = -1;
    uint16 eof_index;
    uint8 eof;

    if(curr_index < len)
    {
        if(((tc_orig->tc_ctrl & TC_CTRL_AUTH_BIT) >> TC_CTRL_AUTH_POS) & 1)
        {
            /*
             * Authentication is enabled. Calculate the eof index as per that.
             * eof_index = current index + tc data length + size of hmac(4)\
             *             + size of crc(1)
             */
            eof_index = curr_index + (tc_orig->tc_len) + 4 + 1;
        }
        else
        {
            /*
             * Authentication is not enabled. Calculate the eof index as per that.
             * eof_index = current index + tc data length + size of crc(1)
             */
            eof_index = curr_index + (tc_orig->tc_len) + 1;
        }

        if(eof_index < len)
        {
            eof = frame_ptr[eof_index];

            if(TC_TM_LIB_EOF != eof)
            {
                rslt = TC_TM_LIB_EOF_ERROR;
            }
            else
            {
                rslt = TC_TM_LIB_OK;
            }
        }
        else
        {
            /*
             * No point in checking EOF as the index is exceeding the total len
             */
            rslt = TC_TM_LIB_EOF_ERROR;
        }
    }
    else
    {
        /*
         * No point in checking EOF as the index is exceeding the total len
         */
        rslt = TC_TM_LIB_EOF_ERROR;
    }

    return rslt;
}
