/**
 * @file ipcc_cmd_common.h
 *
 * @brief This file contains function prototypes and data definitions that helps
 * to create a IPCC frame
 */

#ifndef IPCC_ORIG_LIB_H_
#define IPCC_ORIG_LIB_H_

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ipcc_lib_common.h>

#ifdef __cplusplus
extern "C" {
#endif
/**@}*/
/**\name C standard macros */
#ifndef NULL
#ifdef __cplusplus
#define NULL                                      0
#else
#define NULL                            ((void *) 0)
#endif
#endif

/// Error code definitions
#define IPCC_OK                   INT8_C(0)      ///< Success return value
#define IPCC_NULL_PTR             INT8_C(-1)     ///< Null pointer return value
#define IPCC_CRC_ERROR            INT8_C(-2)     ///< CRC value mismatch
#define IPCC_HMAC_ERROR           INT8_C(-3)     ///< hmac value mismatch
#define IPCC_SEARCH_ERROR         INT8_C(-4)     ///< search failure
#define IPCC_SOF_MISMATCH_ERROR   INT8_C(-5)     ///< SOF value Mismatch
#define IPCC_LENGTH_OF_ERROR      INT8_C(-6)     ///< Length Overflow error
#define IPCC_FULL_FRAME_NOT_RCVD  INT8_C(-7)     ///< Full Frame not received

/// IPCC SOF value
#define IPCC_SOF                  UINT8_C(0xa5)

/// Limits definitions
#define IPCC_ID_MAX               INT8_C(255)    ///< IPCC id maximum limit
#define IPCC_LENGTH_MAX           (2000)        ///< IPCC Maximum length

/// encrypt/decrypt related definitions
#define IPCC_CTRL_CRYPT_BIT       UINT8_C(0x20) ///< encrypt/decrypt bit
#define IPCC_CTRL_CRYPT_POS       UINT8_C(0x5)  ///< encrypt/decrypt bit position
#define KEY                     "ipcc_command_secret_key" ///< key used for hmac 
                                                          /// calculation

/*************************** Data structures *********************************/
/**
 * @brief ipcc command frame structure definition
 *
 */
typedef struct ipcc_frame
{
    uint8   sof;                   /*!< Start of Frame indicator */
    uint8   ipcc_ctrl;               /*!< ipcc-Command control */
    uint32  time_stamp;            /*!< Time stamp value */
    uint16  seq_no;                /*!< Sequence number */
    uint8   qos;                   /*!< Quality Of Service */
    uint16  sa_id;                 /*!< Souce Application id */
    uint16  da_id;                 /*!< Destination Application id */
    uint8   ipcc_id;                 /*!< ipcc-Command id */
    uint16  ipcc_len;                /*!< ipcc-Command data length */
    uint8*  ipcc_data;               /*!< ipcc-Command data */
    uint8*  keys;                  /*!< Encryption-decryption keys */
    uint16  sec_cfg;               /*!< Sec configuration */
    uint16  mac_cfg;               /*!< Mac configuration */
    uint32  crc_32;                /*!< CRC value */
    uint8*  cmac_hmac;             /*!< CMAC/HMAC value */
}s_ipcc_frame;

/**
 * @brief function pointer to trigger function
 */

typedef   int (*Operation)(s_ipcc_frame *);


/**
 * @brief structure that holds the ipcc id and it's corresponding trigger
 *        function pointer
 */
typedef struct ipcc_terminator
{
    uint8  ipcc_id;                   /*!< ipcc-Command id  */
    Operation  ipcc_func[IPCC_ID_MAX];  /*!< Array of pointer to ipcc id function handlers */
}s_ipcc_terminator;

/**
 * @brief structure that holds routing table information. The values stored in
 *        this structure varies based on the comm protocol used.
 */
typedef struct rout_table
{
    uint32 port;                   /*!< Port number or slave register address*/
    uint32 addr;                   /*!< IP address or master address */
}s_rout_table;

/**
 * @brief structure that holds routing table information.
 */
typedef struct ipcc_fwd
{
    s_rout_table rout_table;       /*!< Routing table of type s_rout_table */
}s_ipcc_fwd;

/**
 * @brief This API initializes the s_ipcc_frame by setting all the values of it's
 *        memebers to zero.
 *
 * @param[in] ipcc_orig     : Structure instance of s_ipcc_frame.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_orig_init(s_ipcc_frame *ipcc_orig);

/**
 * @brief This API stores the passed in sof value to the sof member of the
 *        passed in s_ipcc_frame type struct
 *
 * @param[in] ipcc_orig     : Structure instance of s_ipcc_frame.
 * @param[in] sof         : Start Of Frame (sof) value.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_orig_cfg_sof (s_ipcc_frame *ipcc_orig, uint8 sof);

/**
 * @brief This API stores the passed in ipcc_ctrl value to the ipcc_ctrl member of
 *         the passed in s_ipcc_frame type struct
 *
 * @param[in] ipcc_orig     : Structure instance of s_ipcc_frame.
 * @param[in] ipcc_ctrl     : ipcc_ctrl value.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_orig_cfg_ipcc_ctrl (s_ipcc_frame *ipcc_orig, uint8 ipcc_ctrl);

/**
 * @brief This API stores the passed in priority value to the qos member of
 *         the passed in s_ipcc_frame type struct
 *
 * @param[in] ipcc_orig    : Structure instance of s_ipcc_frame.
 * @param[in] priority   : Quality Of Service value.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_orig_cfg_qos (s_ipcc_frame* ipcc_orig, uint8 priority);

/**
 * @brief This API stores the passed in sa_id value to the sa_id member of
 *         the passed in s_ipcc_frame type struct
 *
 * @param[in] ipcc_orig    : Structure instance of s_ipcc_frame.
 * @param[in] sa_id      : Source Application Id.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_orig_cfg_sa_id (s_ipcc_frame* ipcc_orig, uint16 sa_id);

/**
 * @brief This API stores the passed in da_id value to the da_id member of
 *         the passed in s_ipcc_frame type struct
 *
 * @param[in] ipcc_orig    : Structure instance of s_ipcc_frame.
 * @param[in] da_id      : Destination Application Id.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_orig_cfg_da_id (s_ipcc_frame* ipcc_orig, uint16 da_id);

/**
 * @brief This API stores the passed in ipcc_id value to the ipcc_id member of
 *         the passed in s_ipcc_frame type struct
 *
 * @param[in] ipcc_orig    : Structure instance of s_ipcc_frame.
 * @param[in] ipcc_id      : ipcc Command Id.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_orig_cfg_ipcc_id (s_ipcc_frame* ipcc_orig, uint8 ipcc_id);

/**
 * @brief This API stores the passed in ipcc_len value to the ipcc_len member of
 *         the passed in s_ipcc_frame type struct
 *
 * @param[in] ipcc_orig    : Structure instance of s_ipcc_frame.
 * @param[in] ipcc_len      : ipcc Command data Length.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_orig_cfg_ipcc_len (s_ipcc_frame* ipcc_orig, uint16 ipcc_len);

/**
 * @brief This API stores the passed in data to the ipcc_data member of
 *         the passed in s_ipcc_frame type struct. The size of data copied is
 *         equal to ipcc_len. So ipcc_orig_cfg_ipcc_len api needs to be called
 *         first before calling this api.
 *
 * @param[in] ipcc_orig    : Structure instance of s_ipcc_frame.
 * @param[in] data       : Pointer to the data that needs to be copied.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_orig_cfg_ipcc_data (s_ipcc_frame* ipcc_orig, uint8 *data);

/**
 * @brief This API stores the passed in crc_32 value to the crc_32 member of
 *         the passed in s_ipcc_frame type struct
 *
 * @param[in] ipcc_orig    : Structure instance of s_ipcc_frame.
 * @param[in] crc_32      : crc32 value.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_orig_cfg_crc_32 (s_ipcc_frame* ipcc_orig, uint32 crc_32);

/**
 * @brief This API stores the passed in hmac to the hmac member of
 *         the passed in s_ipcc_frame type struct. The size of data copied is
 *         equal to size input value.
 *
 * @param[in] ipcc_orig    : Structure instance of s_ipcc_frame.
 * @param[in] hmac       : Pointer to the computed hmac.
 * @param[in] size       : size of hmac that needs to be copied
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR or other values -> Error.
 */
int8 ipcc_orig_cfg_hmac (s_ipcc_frame* ipcc_orig, uint8 *hmac, size_t size);

/**
 * @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.
 *
 * @param[in] input_str : Input buffer for which the crc needs to be calculated
 * @param[out] val      : Pointer to the computed crc.
 * @param[in] size      : size of the input buffer
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR or other values -> Error.
 */
int8 ipcc_orig_compute_crc (const uint8 *input_str, uint32* val, size_t size);

/**
 * @brief This API calculates the hmac with the key and key size
 *         for the passed in buffer of size "datasize" and store
 *         the computed hmac value to the out parameter
 *
 * @param[in] ipcc_orig : Input buffer for which the hmac needs to be calculated
 * @param[in] key     : Key used for the calculation of the hmac
 * @param[in] keysize : size of the input key
 * @param[in] datasize : size of the input buffer
 * @param[out] out    : Pointer to the computed hmac.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR or other values -> Error.
 */
size_t ipcc_orig_compute_hmac (s_ipcc_frame* ipcc_orig, const uint8* key, const uint32 keysize, const uint32 datasize, void* out);

/**
 * @brief This API resets the sequence number to one
 *
 * @return No return value as it is a void api
 */
void ipcc_orig_reset_seq_no (void);

/**
 * @brief This API resets the timestamp to zero
 *
 * @param[in] ipcc_orig    : Structure instance of s_ipcc_frame.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_orig_reset_time_stamp (s_ipcc_frame* ipcc_orig);

/**
 * @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 ipcc_pack() api
 *
 * @param[in] param    : Structure instance of s_ipcc_crypt_params.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_orig_select_encryption(s_ipcc_crypt_params *param);

/**
 * @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
 *
 * @param[in] param   : Structure instance of s_ipcc_crypt_params.
 * @param[in] buf     : Input buffer to be encrypted
 * @param[in] length  : size of the input buffer
 * @param[out] output : Pointer to the encrypted buffer.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR or any other value -> Error.
 */
int32 ipcc_orig_encrypt_data (s_ipcc_crypt_params *param, const uint8 *buf, int32 length, uint8 *output);

/**
 * @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
 *
 * @param[in] param   : Structure instance of s_ipcc_crypt_params.
 * @param[in] buf     : Input buffer to be decrypted
 * @param[in] length  : size of the input buffer
 * @param[out] output : Pointer to the decrypted buffer.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR or any other value -> Error.
 */
int32 ipcc_orig_decrypt_data (s_ipcc_crypt_params *param, const uint8 *buf, size_t length, uint8 *output);

/**
 * @brief This API copies the da_id, routing parameters (route_param) of size
 *        equals to "size" to the local structure. It is used to add to
 *        route the data if required based on the da_id.
 *
 * @param[in] da_id   : destination application id
 * @param[out] route_param  : Pointer to the routing parameters
 * @param[in] size  : size of the routing table
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_fwd_add_rout_table (uint16 da_id, void* route_param, size_t size);

/**
 * @brief This API copies the routing parameters to the route_param
 *        based on the ipcc_orig->da_id. ipcc_fwd_add_rout_table ()
 *        need to invoked first to configure the routing table with da_id.
 *
 * @param[in] ipcc_orig   : Structure instance of s_ipcc_frame.
 * @param[out] route_param  : Pointer to the routing parameters
 * @param[in] size  : size of the routing table
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / ipcc_SEARCH_ERROR if No routing parameter found for
 *          the da_id or IPCC_NULL_PTR -> Error.
 */
int8 ipcc_route_pkt_hdrs (s_ipcc_frame *ipcc_orig, void* route_param, size_t size);

/**
 * @brief This API copies the routing parameters to the route_param
 *        based on the ipcc_orig->da_id and ipcc_orig->data to the data param
 *        of size ipcc_orig->ipcc_len. ipcc_fwd_add_rout_table () need to invoked
 *        first to configure the routing table with da_id.
 *
 * @param[in] ipcc_orig   : Structure instance of s_ipcc_frame.
 * @param[out] route_param  : Pointer to the routing parameters
 * @param[in] size  : size of the routing table
 * @param[out] data  : Pointer to the copied data
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_SEARCH_ERROR if No routing parameter found for
 *          the da_id or IPCC_NULL_PTR -> Error.
 */
int8 ipcc_route_pkt (s_ipcc_frame *ipcc_orig, void* route_param, size_t size, uint8* data);

/**
 * @brief This API stores the start of frame (sof) from the sof member of passed
 *        in s_ipcc_frame type struct to passed in sof pointer
 *
 * @param[in] ipcc_term    : Structure instance of s_ipcc_frame.
 * @param[out] sof       : Pointer to sof data.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_term_extract_sof(s_ipcc_frame *ipcc_term, uint8 *sof);

/**
 * @brief This API populates the members of ipcc_ctrl input structure based on
 *         the ipcc_term->ipcc_ctrl value.
 *
 * @param[in] ipcc_term    : Structure instance of s_ipcc_frame.
 * @param[out] ipcc_ctrl   : Structure instance of s_ipcc_ctrl.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_term_extract_ipcc_ctrl(s_ipcc_frame *ipcc_term, s_ipcc_ctrl *ipcc_ctrl);

/**
 * @brief This API stores the sequence number (seq_no) from the seq_no
 *        member of passed in s_ipcc_frame type struct to passed in seq_no
 *        pointer
 *
 * @param[in] ipcc_term    : Structure instance of s_ipcc_frame.
 * @param[out] seq_no       : Pointer to seq_no data.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_term_extract_seq_no(s_ipcc_frame *ipcc_term, uint16 *seq_no);

/**
 * @brief This API stores the satellite id (sat_id) from the sat_id
 *        member of passed in s_ipcc_frame type struct to passed in sat_id
 *        pointer
 *
 * @param[in] ipcc_term    : Structure instance of s_ipcc_frame.
 * @param[out] sat_id       : Pointer to sat_id data.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_term_extract_sat_id(s_ipcc_frame *ipcc_term, uint16 *sat_id);

/**
 * @brief This API stores the ground station id (gs_id) from the gs_id
 *        member of passed in s_ipcc_frame type struct to passed in gs_id
 *        pointer
 *
 * @param[in] ipcc_term    : Structure instance of s_ipcc_frame.
 * @param[out] gs_id       : Pointer to gs_id data.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_term_extract_gs_id(s_ipcc_frame *ipcc_term, uint16 *value);

/**
 * @brief This API stores the quality of service (qos) from the qos
 *        member of passed in s_ipcc_frame type struct to passed in qos
 *        pointer
 *
 * @param[in] ipcc_term    : Structure instance of s_ipcc_frame.
 * @param[out] qos       : Pointer to qos data.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_term_extract_qos(s_ipcc_frame *ipcc_term, uint16 *qos);

/**
 * @brief This API stores the source application id (sa_id) from the sa_id
 *        member of passed in s_ipcc_frame type struct to passed in sa_id
 *        pointer
 *
 * @param[in] ipcc_term    : Structure instance of s_ipcc_frame.
 * @param[out] sa_id       : Pointer to sa_id data.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_term_extract_sa_id(s_ipcc_frame *ipcc_term, uint16 *sa_id);

/**
 * @brief This API stores the destination application id (da_id) from the da_id
 *        member of passed in s_ipcc_frame type struct to passed in da_id
 *        pointer
 *
 * @param[in] ipcc_term    : Structure instance of s_ipcc_frame.
 * @param[out] da_id       : Pointer to da_id data.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_term_extract_da_id(s_ipcc_frame *ipcc_term, uint16 *da_id);

/**
 * @brief This API stores the ipcc command id (ipcc_id) from the ipcc_id
 *        member of passed in s_ipcc_frame type struct to passed in ipcc_id
 *        pointer
 *
 * @param[in] ipcc_term    : Structure instance of s_ipcc_frame.
 * @param[out] ipcc_id       : Pointer to ipcc_id data.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_term_extract_ipcc_id(s_ipcc_frame *ipcc_term, uint8 *ipcc_id);

/**
 * @brief This API stores the ipcc command data length (ipcc_len) from the ipcc_len
 *        member of passed in s_ipcc_frame type struct to passed in ipcc_len
 *        pointer
 *
 * @param[in] ipcc_term    : Structure instance of s_ipcc_frame.
 * @param[out] ipcc_len       : Pointer to ipcc_len data.
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_term_extract_ipcc_len(s_ipcc_frame *ipcc_term, uint16 *ipcc_len);

/**
 * @brief This API stores the ipcc command id (ipcc_id) and the ipcc_func function
 *        pointer to the ipcc_term and keeps a local copy of it. The
 *        ipcc_unpack_trigger api utilizes this to trigger ipcc_func when ever it
 *        receives the corresponding ipcc_id
 *
 * @param[in] ipcc_term    : Structure instance of s_ipcc_terminator.
 * @param[in] ipcc_id      : ipcc command id
 * @param[in] ipcc_func    : function pointer for the corresponding ipcc_id
 *
 * @return Result of API execution status
 * @retval IPCC_OK -> Success / IPCC_NULL_PTR -> Error.
 */
int8 ipcc_term_add_ipcc_func(s_ipcc_terminator* ipcc_term, uint8 ipcc_id, Operation ipcc_func);

/**
 * @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.
 * Finally it computes CRC ,hmac and embeds those value to the final 
 * output buffer(dst).
 *
 * @param[out] dst     : pointer to the output buffer
 * @param[in] ipcc_orig  : Structure instance of s_ipcc_frame.
 *
 * @return Size of the output buffer if all operations are 
 *         success else failure code
 * @retval 0 or greater than zero-> success less than zero ->failure
 */
int32 ipcc_pack(uint8 *dst, s_ipcc_frame *ipcc_orig);

/**
 * @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.
 *
 * @param[in] dst     : pointer to the input buffer
 * @param[out] ipcc_orig  : Structure instance of s_ipcc_frame.
 * @param[in] length : length of dst
 *
 * @return Size of the unpacked data if the crc and hmac checks are success 
 *         else failure code
 * @retval 0 or greater than zero-> success less than zero ->failure
 */
int32 ipcc_unpack(const uint8 *dst, s_ipcc_frame *ipcc_orig, uint32 length);

/**
 * @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
 *        ipcc_orig->ipcc_id with ipcc_term_add_ipcc_func() api
 *
 * @param[in] dst     : pointer to the input buffer
 * @param[out] ipcc_orig  : Structure instance of s_ipcc_frame.
 * @param[in] length : length of dst
 *
 * @return return status of the triggered function if the crc and hmac checks
 *          are success else failure code
 * @retval return status of the triggered function
 */
int32 ipcc_unpack_trigger(const uint8 *dst, s_ipcc_frame *ipcc_orig, uint32 length);

/**
 * @brief This API triggers the user function if the user function is
 *        mapped with the received ipcc_orig->ipcc_id with 
 *        ipcc_term_add_ipcc_func() api
 *
 * @param[in] ipcc_orig    : Structure instance of s_ipcc_frame.
 *
 * @return return status of the triggered function. Returns null poiner If no
 *         trigger function is found returns or if the input is null pointer
 * @retval return status of the triggered function else IPCC_NULL_PTR -> Error.
 */
int8 ipcc_trgr_func (s_ipcc_frame *ipcc_orig);

#ifdef __cplusplus
}
#endif
#endif /*IPCC_ORIG_LIB_H_ */
