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

/**
 * local instance of s_td_terminator structure to store td_id and trigger
 * function pointer
 */

static s_td_terminator td_term_local;

/// Utility buffer
uint8_t td_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_td_frame type struct to passed in sof1 and 2
 * value. Refer section 3.2.3.1 in TC_TM_GS & Space Communication Protocol
 * Design document for more information
 */
int8 td_term_extract_sof(s_td_frame *td_term, uint8 *sof1, uint8 *sof2)
{
    int8 status;

    if((NULL != td_term) && (NULL != sof1) && (NULL != sof2))
    {
        *sof1 = td_term->sof1;
        *sof2 = td_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_td_frame type struct to passed in sof value. Refer
 * section 3.2.3.2 in TC_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 td_term_extract_td_ctrl(s_td_frame *td_term, s_td_ctrl *td_ctrl)
{
    int8 status;

    if((NULL != td_term) && (NULL != td_ctrl))
    {
        s_td_ctrl_util info;
        info.byte = td_term->td_ctrl;
        td_ctrl->source_dest = info.ctrl.source_dest;
        td_ctrl->tx_mode = info.ctrl.tx_mode;
        td_ctrl->comm_mode = info.ctrl.comm_mode;
        td_ctrl->auth = info.ctrl.auth;
        td_ctrl->encrypt = info.ctrl.encrypt;
        td_ctrl->td_msg_typ = info.ctrl.td_msg_typ;
        td_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_td_frame type struct to passed in seq_no value. Refer
 * section 3.2.3.4 in TC_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 td_term_extract_seq_no(s_td_frame *td_term, uint16 *seq_no)
{
    int8 status;

    if((NULL != td_term) && (NULL != seq_no))
    {
        *seq_no = td_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_td_frame type struct to passed in sat_id value. Refer
 * section 3.2.3.5 in TC_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 td_term_extract_sat_id(s_td_frame *td_term, uint16 *sat_id)
{
    int8 status;

    if((NULL != td_term) && (NULL != sat_id))
    {
        *sat_id = td_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_td_frame type struct
 *  to passed in qos value. Refer section 3.2.3.6 in TC_TM_GS & Space
 *  Communication Protocol Design document for more information
 */
int8 td_term_extract_qos(s_td_frame *td_term, uint16 *qos)
{
    int8 status;

    if((NULL != td_term) && (NULL != qos))
    {
        *qos = td_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_td_frame type
 * struct to passed in sa_id value. Refer section 3.2.3.7 in TC_TM_GS & Space
 *  Communication Protocol Design document for more information
 */
int8 td_term_extract_sa_id(s_td_frame *td_term, uint16 *sa_id)
{
    int8 status;

    if((NULL != td_term) && (NULL != sa_id))
    {
        *sa_id = td_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_td_frame type struct
 * to passed in da_id value. Refer section 3.2.3.8 in TC_TM_GS & Space
 * Communication Protocol Design document for more information
 */
int8 td_term_extract_da_id(s_td_frame *td_term, uint16 *da_id)
{
    int8 status;

    if((NULL != td_term) && (NULL != da_id))
    {
        *da_id = td_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_td_frame type struct
 * to passed in rm_id value. Refer section 3.2.3.9 in TC_TM_GS & Space
 * Communication Protocol Design document for more information
 */
int8 td_term_extract_rm_id(s_td_frame *td_term, uint8 *rm_id)
{
    int8 status;

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

    return status;
}

/**
 * @brief This API stores the td_id of passed in s_td_frame type struct
 * to passed in td_id value. Refer section 3.2.3.10 in TC_TM_GS & Space
 * Communication Protocol Design document for more information
 */
int8 td_term_extract_td_id(s_td_frame *td_term, uint8 *td_id)
{
    int8 status;

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

    return status;
}

/**
 * @brief This API stores the td_co_id (coorelation id)of passed in
 * s_td_frame type struct to passed in co_id value. Refer section 3.2.3.15
 * in TM_TM_GS & SpaceCommunication Protocol Design document for
 * more information
 */
int8 td_term_extract_co_id(s_td_frame *td_term, uint16 *co_id)
{
    int8 status;

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

    return status;
}

/**
 * @brief This API stores the td_len of passed in s_td_frame type struct
 *  to passed in td_len value. Refer section 3.2.3.11 in TC_TM_GS & Space
 * Communication Protocol Design document for more information
 */
int8 td_term_extract_td_len(s_td_frame *td_term, uint16 *td_len)
{
    int8 status;

    if((NULL != td_term) && (NULL != td_len))
    {
        *td_len = td_term->td_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_td_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 td_term_extract_eof(s_td_frame *td_term, uint8 *eof)
{
    int8 status;

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

    return status;
}

/**
 * @brief This API adds the td_id and td_func in the corresponding index of
 * td_id[] and td_func[] member of type s_td_terminator
 */
int8 td_term_add_td_func(s_td_terminator *td_term, uint8 td_id,
                         td_trgr_ptr td_func)
{
    int8 status;

    if((NULL != td_term) && (NULL != td_func))
    {
        td_term->td_id = td_id;
        td_term->td_func[td_id] = td_func;
        td_term_local.td_id = td_id;
        td_term_local.td_func[td_id] = td_func;
        status = TC_TM_LIB_OK;
    }
    else
    {
        status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API removes the td_id and td_func in the corresponding index of
 * type s_td_terminator
 */
int8 td_term_remove_td_func(s_td_terminator *td_term, uint8 td_id)
{
    int8 status;

    if(NULL != td_term)
    {
        td_term->td_id = -1;
        td_term->td_func[td_id] = NULL;
        td_term_local.td_id = -1;
        td_term_local.td_func[td_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 td_unpack(const uint8 *dst, s_td_frame *td_orig)
{
    int32 size = 0;
    size_t hmac_size = 0;
    int32 rslt = -1;
    uint8 crc_8 = 0;

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

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

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

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

            size += sizeof(td_orig->time_stamp);

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

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

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

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

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

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

            os_memcpy(&td_orig->td_id, &dst[size], sizeof(td_orig->td_id));
            size += sizeof(td_orig->td_id);

            if(((td_orig->td_ctrl & TD_CTRL_CO_ID_BIT) >> TD_CTRL_CO_ID_POS) &
               1)
            {
                os_memcpy(&td_orig->td_co_id, &dst[size],
                          sizeof(td_orig->td_co_id));
                size += sizeof(td_orig->td_co_id);
            }
 
            //td_len is fixed as 2bytes, before it is of vaiable length 1 or 2 bytes
            os_memcpy(&td_orig->td_len, &dst[size], sizeof(td_orig->td_len));
            size += sizeof(td_orig->td_len);

            os_memcpy(td_orig->td_data, &dst[size], td_orig->td_len);
            size += td_orig->td_len;

            if(((td_orig->td_ctrl & TD_CTRL_AUTH_BIT) >> TD_CTRL_AUTH_POS) & 1)
            {
                hmac_size =
                    compute_hmac(td_orig->td_data, (const uint8 *)TD_KEY,
                                 strlen(TD_KEY), td_orig->td_len, td_out);

                os_memcpy(td_orig->cmac_hmac, &dst[size], hmac_size);
                size += hmac_size;
                rslt = memcmp(td_out, td_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(&td_orig->crc_8, &dst[size], sizeof(td_orig->crc_8));
                td_orig_compute_crc(dst, &crc_8, size);
                size += sizeof(td_orig->crc_8);

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

                if(TC_TM_LIB_EOF != td_orig->eof)
                {
                    rslt = TC_TM_LIB_SOF_EOF_ERROR;
                }
                else if(crc_8 != td_orig->crc_8)
                {
                    rslt = TC_TM_LIB_CRC_ERROR;
                }
                else
                {
                    rslt = size;
                }
            }
            else
            {
                /**
                 *authentication failed for some reason.
                 */
            }
        }
        else
        {
            /**
             * Start of frame values not matching with the expected
             * values.
             */
            rslt = TC_TM_LIB_SOF_EOF_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
 *        td_orig->td_id with td_term_add_td_func() api
 */
int8 td_unpack_trigger(const uint8 *dst, s_td_frame *td_orig)
{
    int32 rslt = -1;
    rslt = td_unpack(dst, td_orig);

    if(0 < rslt)
    {
        rslt = td_trgr_func(td_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 td_orig->td_id with
 *        td_term_add_td_func() api
 */
int8 td_trgr_func(s_td_frame *td_orig)
{
    int8 status;
    if((NULL != td_orig) && (NULL != td_term_local.td_func[td_orig->td_id]))
    {
        status = td_term_local.td_func[td_orig->td_id](td_orig);
    }
    else
    {
        status = TC_TM_LIB_NULL_PTR;
    }
    return status;
}
