/**
 * @file td_orig.c
 *
 * @brief This file contains functions that helps to create
 *        the teledata frame
 *
 * The functions provided in the file helps to create teledata
 * frame with crc and encryptions.
 */
/*********************************************************************/
#include "td_common.h"

///Sequence number static global variable
static uint16 seq_no = 1;

///s_crypt_params local instance to keep track of cryption parameters
static s_crypt_params td_param_local;

/**
 * @brief This API initializes the s_td_frame by setting all the values of it's
 *        members to zero.
 */
int8 td_orig_init(s_td_frame *td_orig)
{
    int8 status;

    if(NULL != td_orig)
    {
        memset(td_orig, 0, sizeof (s_td_frame));
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the start of frame passed in sof1 and sof2 value to
 * to the sof1 and sof2 members of passed in s_td_frame type struct. Refer
 * section 3.2.3.1 in TC_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 td_orig_cfg_sof(s_td_frame *td_orig, uint8 sof1, uint8 sof2)
{
    int8 status;

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

    return status;
}

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

    if(NULL != td_orig)
    {
        td_orig->td_ctrl = td_ctrl;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the passed in seq_no_in value to the seq_no member
 * of passed in s_td_frame type struct and also updates the global seq_no.
 * Refer section 3.2.2.4 in TC_TM_GS & Space Communication Protocol Design
 * document for more information
 */
int8 td_orig_cfg_seq_no(s_td_frame* td_orig, uint16 seq_no_in)
{
    int8 status;

    if (NULL != td_orig)
    {
        td_orig->seq_no = seq_no_in;
        seq_no = seq_no_in;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

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

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

    return status;
}

/**
 * @brief This API stores the passed in priority value to the qos member
 * of passed in s_td_frame type struct. Refer section 3.2.3.6 in
 * TC_TM_GS & Space Communication Protocol Design document for more information
 */
int8 td_orig_cfg_qos(s_td_frame* td_orig, uint8 priority)
{
    int8 status;

    if(NULL != td_orig)
    {
        td_orig->qos = priority;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

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

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

    return status;
}

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

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

    return status;
}

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

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

    return status;
}

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

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

    return status;
}

/**
 * @brief This API stores the passed in co_id (corelation id) value to
 * the td_id member of passed in s_td_frame type struct. Refer section
 * 3.2.3.15 in TC_TM_GS & Space Communication Protocol Design document
 * for more information
 */
int8 td_orig_cfg_co_id(s_td_frame* td_orig, uint16 co_id)
{
    int8 status;

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

    return status;
}

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

    if(NULL != td_orig)
    {
        td_orig->td_len = td_len;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API stores the passed in value to the td_len member
 * of passed in s_td_frame type struct. Refer section 3.2.3.12 in
 * TC_TM_GS & Space Communication Protocol Design document for more information
 */
int8 td_orig_cfg_td_data(s_td_frame* td_orig, uint8 *data)
{
    int8 status;

    if((NULL != td_orig) && (NULL != data))
    {
        td_orig->td_data = data;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

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

    if(NULL != td_orig)
    {
        td_orig->crc_8 = crc_8;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

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

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

    return status;
}

/**
 * @brief This API calculates hmac through the 3rd party library
 */
size_t td_orig_compute_hmac(s_td_frame* td_orig, const uint8* key,\
                            const uint32 keysize, const uint32 datasize,\
                                                              void* out)
{
    size_t status;

    if((NULL != td_orig) && (NULL != key) && (NULL != out))
    {
        status = compute_hmac ((const uint8*) td_orig, key, keysize, datasize,
                                out);
    }
    else
    {
        status = TC_TM_LIB_NULL_PTR;
    }
    return status;
}

/**
 * @brief This API stores the passed in hmac to the cmac_hmac member
 * of passed in s_td_frame type struct
 */
int8 td_orig_cfg_hmac(s_td_frame* td_orig, uint8 *hmac, size_t size)
{
    int8 status;

    if((NULL != td_orig) && (NULL != hmac))
    {
        os_memcpy(td_orig->cmac_hmac, hmac, size);
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API resets the sequence number to 1
 */
void td_orig_reset_seq_no(void)
{
   seq_no = 1;

}

/**
 * @brief This API resets the timestamp to zero
 */
int8 td_orig_reset_time_stamp(s_td_frame* td_orig)
{
    int8 status;

    if(NULL != td_orig)
    {
        td_orig->time_stamp = 0;
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API calculates the crc for the passed in input_str
 *         for the input size and stores the crc value to the val parameter
 *         equal to size input value.
 */
int8 td_orig_compute_crc(const uint8 *input_str, uint8* val, size_t size)
{

    int8 status;

    if((NULL != input_str) && (NULL != val))
    {
        *val = crc_8(input_str, size);
        status = TC_TM_LIB_OK;
    }
    else
    {
       status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API copies the structure data to the output buffer(dst). While
 * copying it takes care of the extension bit for the required members.
 * (Refer Table 3 in TC_TM_GS & Space Communication Protocol Design document
 * for more information) Finally it computes CRC ,hmac and embeds those value
 * to the final output buffer(dst).
 */
int32 td_pack(uint8 *dst, s_td_frame *td_orig)
{
    int32 size = 0;
    int8 status = TC_TM_LIB_OK;
    size_t hmac_size = 0;

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

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

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

        td_orig->time_stamp = get_utc_epoch();

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

        size += sizeof td_orig->time_stamp;

        // populate the sequence number to the structure
        td_orig->seq_no = seq_no;

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

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

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

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

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

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

        os_memcpy(&dst[size], &td_orig->td_id, 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(&dst[size], &td_orig->td_co_id, sizeof td_orig->td_co_id);
            size += sizeof td_orig->td_co_id;
        }
        //td_len is fixed as 2bytes, before it is of variable length of 1 or 2 bytes
        os_memcpy(&dst[size], &td_orig->td_len, sizeof(td_orig->td_len));
        size += sizeof(td_orig->td_len);

        if(((td_orig->td_ctrl & TD_CTRL_CRYPT_BIT) >> TD_CTRL_CRYPT_POS) & 1)
        {
            /* encrypt bit is set in td_ctrl. encrypt the data */
            status = td_orig_encrypt_data (&td_param_local,
                                           (const uint8 *)td_orig->td_data,
                                            td_orig->td_len,
                                            td_orig->td_data);
        }

        if(TC_TM_LIB_OK == status)
        {
            os_memcpy(&dst[size], td_orig->td_data, td_orig->td_len);
            size += td_orig->td_len;
            /**
             *Either encryption is success or encryption bit is
             *not set in td_ctrl
             */
            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_orig->cmac_hmac);
                if(0 < hmac_size)
                {
                    os_memcpy(&dst[size], td_orig->cmac_hmac, hmac_size);
                    size += hmac_size;
                }
                else
                {
                    /**
                    *compute_hmac failed for some reason. Set the size to
                    *compute_hmac status and return
                    */
                    size = hmac_size;
                    status = TC_TM_LIB_HMAC_ERROR;
                }
            }

            if(TC_TM_LIB_OK == status)
            {
                td_orig_compute_crc(dst, &td_orig->crc_8, size);

                os_memcpy(&dst[size], &td_orig->crc_8, sizeof td_orig->crc_8);
                size += sizeof td_orig->crc_8;
                os_memcpy(&dst[size], &td_orig->eof, sizeof (td_orig->eof));
                size += sizeof (td_orig->eof);
                seq_no++;
            }
            else
            {
                /**
                 * HMAC bit is set but the calculation failed.
                 * Size will already have the status of compute_hmac
                 */
            }
        }
        else
        {
            /**
             *encryption failed for some reason. Set the size to encryption
             *status and return
             */
            size = status;
        }
    }
    else
    {
        size =  TC_TM_LIB_NULL_PTR;
    }

    return size;
}

/**
 * @brief This API copies the passed in encryption related parameters to the
 *        local structure. The parameters are utilised from the local structure
 *        during the encryption operation in td_pack() api
 */
int8 td_orig_select_encryption(s_crypt_params *param)
{
    int8 status = TC_TM_LIB_OK;

    if((NULL != param) && (NULL != param->iv) && (NULL != param->key))
    {
        os_memcpy(td_param_local.iv, param->iv, 16);
        td_param_local.key = param->key;
        td_param_local.key_size = param->key_size;
        td_param_local.encrypt_mode = param->encrypt_mode;
    }
    else
    {
        status = TC_TM_LIB_NULL_PTR;
    }
    return status;
}

/**
 * @brief This API encrypts the passed in buffer of size "length" based on the
 *        input encryption "param" and stores the encrypted buffer to the
 *        "output" parameter
 */
int32 td_orig_encrypt_data(s_crypt_params *param, const uint8 *buf,\
                                        int32 length, uint8 *output)
{
    int32 status;

    if((NULL != param) && (NULL != buf) &&
       (NULL != param->key) && (NULL != output))
    {
        status = aes_encrypt_data(param, buf, length, output);
    }
    else
    {
        status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}

/**
 * @brief This API decrypts the passed in buffer of size "length" based on the
 *        input decryption "param" and stores the decrypted buffer to the
 *        "output" parameter
 */
int32 td_orig_decrypt_data(s_crypt_params *param, const uint8 *buf,\
                                       size_t length, uint8 *output)
{
    int32 status;

    if((NULL != param) && (NULL != buf) &&
       (NULL != param->key) && (NULL != output))
    {
        status = aes_decrypt_data(param, buf, length, output);

    }
    else
    {
        status = TC_TM_LIB_NULL_PTR;
    }

    return status;
}
