/**
 * @file tm_terminate.c
 *
 * @brief This file contains functions to extract info from telemetry frame
 *
 * The functions provided in the file helps to extract the required information
 * from the telemetry frame with crc and encryptions. It also provides the
 * functionalities to unpack buffer and store it in the s_tm_frame structure.
 *
 * @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 <tm_common.h>

/**
 * @brief This API check the frame validity by verifing the telemetry EOF value
 * is same as actual EOF value.
 *
 * @param[in] frame_ptr   : pointer to the input buffer
 * @param[in] tm_orig     : Structure instance of s_tm_frame.
 * @param[in] curr_index  : current index upto tm len.
 * @param[in] len         : frame_ptr length.
 *
 * @return frame validity status if success TC_TM_LIB_OK else error
 *
 * @retval 0 or greater than zero-> success less than zero ->failure
 */
static int32 validate_tm_eof(const uint8 *frame_ptr, s_tm_frame *tm_orig,
                             uint16 curr_index, uint16 len);

/**
 * local instance of s_tm_terminator structure to store tm_id and trigger
 * function pointer
 */

static s_tm_terminator tm_term_local;

/// Utility buffer
uint8_t tm_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_tm_frame type struct to passed in sof1 and 2
 * value. Refer section 3.2.2.1 in TC_TM_GS & Space Communication Protocol
 * Design document for more information
 */
int8 tm_term_extract_sof(s_tm_frame *tm_term, uint8 *sof1, uint8 *sof2)
{
    int8 status;

    if((NULL != tm_term) && (NULL != sof1) && (NULL != sof2))
    {
        *sof1 = tm_term->sof1;
        *sof2 = tm_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_tm_frame type struct to passed in sof value. Refer
 * section 3.2.2.2 in TM_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 tm_term_extract_tm_ctrl(s_tm_frame *tm_term, s_tm_ctrl *tm_ctrl)
{
    int8 status;

    if((NULL != tm_term) && (NULL != tm_ctrl))
    {
        s_tm_ctrl_util info;
        info.byte = tm_term->tm_ctrl;
        tm_ctrl->source_dest = info.ctrl.source_dest;
        tm_ctrl->tx_mode = info.ctrl.tx_mode;
        tm_ctrl->comm_mode = info.ctrl.comm_mode;
        tm_ctrl->auth = info.ctrl.auth;
        tm_ctrl->encrypt = info.ctrl.encrypt;
        tm_ctrl->tm_msg_typ = info.ctrl.tm_msg_typ;
        tm_ctrl->reserved = info.ctrl.reserved;
        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_tm_frame type struct to passed in seq_no value. Refer
 * section 3.2.2.4 in TM_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 tm_term_extract_seq_no(s_tm_frame *tm_term, uint16 *seq_no)
{
    int8 status;

    if((NULL != tm_term) && (NULL != seq_no))
    {
        *seq_no = tm_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_tm_frame type struct to passed in sat_id value. Refer
 * section 3.2.2.5 in TM_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 tm_term_extract_sat_id(s_tm_frame *tm_term, uint16 *sat_id)
{
    int8 status;

    if((NULL != tm_term) && (NULL != sat_id))
    {
        *sat_id = tm_term->sat_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_tm_frame type struct
 *  to passed in qos value. Refer section 3.2.2.6 in TM_TM_GS & Space
 *  Communication Protocol Design document for more information
 */
int8 tm_term_extract_qos(s_tm_frame *tm_term, uint16 *qos)
{
    int8 status;

    if((NULL != tm_term) && (NULL != qos))
    {
        *qos = tm_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_tm_frame type
 * struct to passed in sa_id value. Refer section 3.2.2.7 in TM_TM_GS & Space
 *  Communication Protocol Design document for more information
 */
int8 tm_term_extract_sa_id(s_tm_frame *tm_term, uint16 *sa_id)
{
    int8 status;

    if((NULL != tm_term) && (NULL != sa_id))
    {
        *sa_id = tm_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_tm_frame type struct
 * to passed in da_id value. Refer section 3.2.2.8 in TM_TM_GS & Space
 * Communication Protocol Design document for more information
 */
int8 tm_term_extract_da_id(s_tm_frame *tm_term, uint16 *da_id)
{
    int8 status;

    if((NULL != tm_term) && (NULL != da_id))
    {
        *da_id = tm_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_tm_frame type struct
 * to passed in rm_id value. Refer section 3.2.2.9 in TM_TM_GS & Space
 * Communication Protocol Design document for more information
 */
int8 tm_term_extract_rm_id(s_tm_frame *tm_term, uint8 *rm_id)
{
    int8 status;

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

    return status;
}

/**
 * @brief This API stores the tm_id of passed in s_tm_frame type struct
 * to passed in tm_id value. Refer section 3.2.2.10 in TM_TM_GS & Space
 * Communication Protocol Design document for more information
 */
int8 tm_term_extract_tm_id(s_tm_frame *tm_term, uint16 *tm_id)
{
    int8 status;

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

    return status;
}

/**
 * @brief This API stores the tm_co_id (coorelation id)of passed in
 * s_tm_frame type struct to passed in co_id value. Refer section 3.2.2.15
 * in TM_TM_GS & SpaceCommunication Protocol Design document for
 * more information
 */
int8 tm_term_extract_co_id(s_tm_frame *tm_term, uint16 *co_id)
{
    int8 status;

    if((NULL != tm_term) && (NULL != co_id))
    {
        *co_id = tm_term->tm_co_id;
        status = TC_TM_LIB_OK;
    }
    else
    {
        status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the tm_len of passed in s_tm_frame type struct
 *  to passed in tm_len value. Refer section 3.2.2.11 in TM_TM_GS & Space
 * Communication Protocol Design document for more information
 */
int8 tm_term_extract_tm_len(s_tm_frame *tm_term, uint16 *tm_len)
{
    int8 status;

    if((NULL != tm_term) && (NULL != tm_len))
    {
        *tm_len = tm_term->tm_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_tm_frame type struct to passed in eof value. Refer
 * section 3.2.2.16 in TC_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 tm_term_extract_eof(s_tm_frame *tm_term, uint8 *eof)
{
    int8 status;

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

    return status;
}

/**
 * @brief This API adds the tm_id and tm_func in the corresponding index of
 * tm_id[] and tm_func[] member of type s_tm_terminator
 */
int8 tm_term_add_tm_func(s_tm_terminator *tm_term, uint16 tm_id,
                         tm_trgr_ptr tm_func)
{
    int8 status;

    if((NULL != tm_term) && (NULL != tm_func))
    {
        tm_term->tm_id = tm_id;
        tm_term->tm_func[tm_id] = tm_func;
        tm_term_local.tm_id = tm_id;
        tm_term_local.tm_func[tm_id] = tm_func;
        status = TC_TM_LIB_OK;
    }
    else
    {
        status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API removes the tm_id and tm_func in the corresponding index of
 * type s_tm_terminator
 */
int8 tm_term_remove_tm_func(s_tm_terminator *tm_term, uint16 tm_id)
{
    int8 status;

    if(NULL != tm_term)
    {
        tm_term->tm_id = -1;
        tm_term->tm_func[tm_id] = NULL;
        tm_term_local.tm_id = -1;
        tm_term_local.tm_func[tm_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 tm_unpack(const uint8 *dst, uint16 len, s_tm_frame *tm_orig)
{
    int32 size = 0;
    size_t hmac_size = 0;
    int32 rslt = -1;
    uint8 crc_8 = 0;

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

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

            if((TC_TM_LIB_SOF1 == tm_orig->sof1) &&
               (TC_TM_LIB_SOF2 == tm_orig->sof2))
            {
                os_memcpy(&tm_orig->tm_ctrl, &dst[size],
                            sizeof(tm_orig->tm_ctrl));
                size += sizeof(tm_orig->tm_ctrl);

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

                size += sizeof(tm_orig->time_stamp);

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

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

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

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

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

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

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

                if(((tm_orig->tm_ctrl & TM_CTRL_CO_ID_BIT)\
                    >> TM_CTRL_CO_ID_POS) & 1)
                {
                    os_memcpy(&tm_orig->tm_co_id, &dst[size],
                              sizeof(tm_orig->tm_co_id));
                    size += sizeof(tm_orig->tm_co_id);
                }
                
                //tm_len is fixed as 2 bytes, before it is of variable lenght 1 or 2 bytes
                os_memcpy(&tm_orig->tm_len, &dst[size], sizeof(tm_orig->tm_len));
                size += sizeof(tm_orig->tm_len);

                rslt = validate_tm_eof(dst, tm_orig, size, len);
                if(TC_TM_LIB_OK == rslt)
                {
                    os_memcpy(tm_orig->tm_data, &dst[size], tm_orig->tm_len);
                    size += tm_orig->tm_len;

                    if(((tm_orig->tm_ctrl & TM_CTRL_AUTH_BIT)\
                        >> TM_CTRL_AUTH_POS) & 1)
                    {
                        hmac_size =
                            compute_hmac(tm_orig->tm_data,
                                         (const uint8 *)TM_KEY, strlen(TM_KEY),
                                         tm_orig->tm_len, tm_out);

                        os_memcpy(tm_orig->cmac_hmac, &dst[size], hmac_size);
                        size += hmac_size;
                        rslt = memcmp(tm_out, tm_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(&tm_orig->crc_8, &dst[size],
                                    sizeof(tm_orig->crc_8));
                        tm_orig_compute_crc(dst, &crc_8, size);
                        size += sizeof(tm_orig->crc_8);

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

                        rslt = size;
                        if(TC_TM_LIB_EOF != tm_orig->eof)
                        {
                            rslt = TC_TM_LIB_SOF_EOF_ERROR;
                        }
                        else if(crc_8 != tm_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
 *        tm_orig->tm_id with tm_term_add_tm_func() api
 */
int8 tm_unpack_trigger(const uint8 *dst, uint16 len, s_tm_frame *tm_orig)
{
    int32 rslt = -1;
    rslt = tm_unpack(dst, len, tm_orig);

    if(0 < rslt)
    {
        rslt = tm_trgr_func(tm_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 tm_orig->tm_id with
 *        tm_term_add_tm_func() api
 */
int8 tm_trgr_func(s_tm_frame *tm_orig)
{
    int8 status;
    if((NULL != tm_orig) && (NULL != tm_term_local.tm_func[tm_orig->tm_id]))
    {
        status = tm_term_local.tm_func[tm_orig->tm_id](tm_orig);
    }
    else
    {
        status = TC_TM_LIB_NULL_PTR;
    }
    return status;
}

/*
 * @brief This API check the frame validity by verifing the telemetry EOF value
 * is same as actual EOF value.
 */
static int32 validate_tm_eof(const uint8 *frame_ptr, s_tm_frame *tm_orig,
                             uint16 curr_index, uint16 len)
{
    int rslt = -1;
    uint16 eof_index;
    uint8 eof;

    if(curr_index < len)
    {
        if(((tm_orig->tm_ctrl & TM_CTRL_AUTH_BIT) >> TM_CTRL_AUTH_POS) & 1)
        {
            /*
             * Authentication is enabled. Calculate the eof index as per that.
             * eof_index = current index + tm data length + size of hmac(32)\
             *             + size of crc(1)
             */
            eof_index = curr_index + (tm_orig->tm_len) + 32 + 1;
        }
        else
        {
            /*
             * Authentication is not enabled. Calculate the eof index as per
             * that.
             * eof_index = current index + tm data length + size of crc(1)
             */
            eof_index = curr_index + (tm_orig->tm_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;
}

