/*
 * =====================================================================================
 *
 *       Filename:  hdm_json.h
 *
 *    Description:  HDM Json functions
 *
 *        Version:  1.0
 *        Created:  09/20/2019 13:16:11 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *        Company:  Samsung Electronics
 *        Copyright (c) 2019 by Samsung Electronics, All rights reserved.
 *
 * =====================================================================================
 */

/** Includes */
#include "hdm_json.h"

/**
 * @brief
 * json_append_single_char
 * Append a single char in a json.
 *
 * @param[in|out] buffer        - Pointer to the buffer where the json is being built
 * @param[in]     buffer_size   - Max size of the buffer
 * @param[in|out] buffer_offset - Offset of the buffer it is updated inside this function
 * @param[in]     c             - Char to be written to the buffer
 *
 * @return Status code
 */
hdm_return_code_t json_append_single_char(uint8_t *buffer, uint32_t buffer_size, uint32_t *buffer_offset, uint8_t c)
{
        if (*buffer_offset + 2 > buffer_size) {
                HDM_LOG_DEBUG("Failed to write \'%c\'; buffer_size: %d buffer_offset: %d", c, buffer_size, *buffer_offset);
                return HDM_JSON_FAIL;
        }

        buffer[*buffer_offset] = c;
        buffer[*buffer_offset + 1] = '\0';
        *buffer_offset += 1;

        return HDM_STATUS_SUCCESS;
}

/**
 * @brief
 * json_append_char
 * Append a char field in a json.
 *
 * @param[in|out] buffer        - Pointer to the buffer where the json is being built
 * @param[in]     buffer_size   - Max size of the buffer
 * @param[in|out] buffer_offset - Offset of the buffer it is updated inside this function
 * @param[in]     name          - Name of the json field to be added
 * @param[in]     value         - Char to be written to the buffer
 *
 * @return Status code
 */
hdm_return_code_t json_append_char(uint8_t *buffer, uint32_t buffer_size, uint32_t *buffer_offset, const char *name, char value)
{
        hdm_return_code_t ret = HDM_JSON_FAIL;
        int size = buffer_size - *buffer_offset;
        const char format[] = "\"%s\":\"%c\"";
        char *aux;

        if (size > 0) {
                aux = (char *) &buffer[*buffer_offset];
                ret = snprintf(aux, (size_t) size, format, name, value);

                if (ret > 0 && ret < (size_t) size) {
                        *buffer_offset += ret;
                        ret = HDM_STATUS_SUCCESS;

                } else {
                        HDM_LOG_DEBUG("Failed to write %d bytes to buffer of size %d bytes", ret, size);
                        ret = HDM_JSON_FAIL;
                }

        } else {
                HDM_LOG_DEBUG("Impossible to write to buffer of size %d bytes", size);
                ret = HDM_JSON_FAIL;
        }

        return ret;
}

/**
 * @brief
 * json_append_int
 * Append an integer field in a json.
 *
 * @param[in|out] buffer        - Pointer to the buffer where the json is being built
 * @param[in]     buffer_size   - Max size of the buffer
 * @param[in|out] buffer_offset - Offset of the buffer it is updated inside this function
 * @param[in]     name          - Name of the json field to be added
 * @param[in]     value         - Integer value to be added
 *
 * @return Status code
 */
hdm_return_code_t json_append_int(uint8_t *buffer, uint32_t buffer_size, uint32_t *buffer_offset, const char *name, uint32_t value)
{
        hdm_return_code_t ret = HDM_JSON_FAIL;
        int size = buffer_size - *buffer_offset;
        const char format[] = "\"%s\":%d";
        char *aux;

        if (size > 0) {
                aux = (char *)&buffer[*buffer_offset];
                ret = snprintf(aux, (size_t)size, format, name, value);

                if (ret > 0 && ret < (size_t) size) {
                        *buffer_offset += ret;
                        ret = HDM_STATUS_SUCCESS;

                } else {
                        HDM_LOG_DEBUG("Failed to write %d bytes to buffer of size %d bytes", ret, size);
                        ret = HDM_JSON_FAIL;
                }

        } else {
                HDM_LOG_DEBUG("Impossible to write to buffer of size %d bytes", size);
                ret = HDM_JSON_FAIL;
        }

        return ret;
}

/**
 * @brief
 * json_append_string
 * Append an string field in a json.
 *
 * @param[in|out] buffer        - Pointer to the buffer where the json is being built
 * @param[in]     buffer_size   - Max size of the buffer
 * @param[in|out] buffer_offset - Offset of the buffer it is updated inside this function
 * @param[in]     name          - Name of the json field to be added
 * @param[in]     value         - String value to be added
 * @param[in]     format        - Format string for the field, if it is NULL, default format "\"%s\":\"%s\"" is used
 *
 * @return Status code
 */
hdm_return_code_t json_append_string(uint8_t *buffer, uint32_t buffer_size, uint32_t *buffer_offset, const char *name, uint8_t *value, char *format)
{
        hdm_return_code_t ret = HDM_JSON_FAIL;
        int size = buffer_size - *buffer_offset;
        char *aux;

        if (format == NULL) {
                format = "\"%s\":\"%s\"";
        }

        if (size > 0) {
                aux = (char *) &buffer[*buffer_offset];
                ret = snprintf(aux, (size_t) size, format, name, value);

                if (ret > 0 && ret < (size_t) size) {
                        *buffer_offset += ret;
                        ret = HDM_STATUS_SUCCESS;

                } else {
                        HDM_LOG_DEBUG("Failed to write %d bytes to buffer of size %d bytes", ret, size);
                        ret = HDM_JSON_FAIL;
                }

        } else {
                HDM_LOG_DEBUG("Impossible to write to buffer of size %d bytes", size);
                ret = HDM_JSON_FAIL;
        }

        return ret;
}

/**
 * @brief
 * json_append_certs
 * Append a certificate array in a json.
 *
 * @param[in|out] buffer        - Pointer to the buffer where the json is being built
 * @param[in]     buffer_size   - Max size of the buffer
 * @param[in|out] buffer_offset - Offset of the buffer it is updated inside this function
 * @param[in]     name          - Name of the json field to be added
 * @param[in]     array         - Array of certificates to be added
 * @param[in]     array_size    - Number of elements of the array
 *
 * @return Status code
 */
hdm_return_code_t json_append_certs(uint8_t *buffer, uint32_t buffer_size, uint32_t *buffer_offset,
                                    const char *name, drk_cert_chain_t array[], uint8_t array_size)
{
        hdm_return_code_t ret = HDM_JSON_FAIL;
        int size = buffer_size - *buffer_offset;
        int i;
        char *aux;

        if (array_size <= 0) {
                HDM_LOG_DEBUG("Empty or invalid array size\n");
                goto error;
        }

        if (size <= 0) {
                HDM_LOG_DEBUG("Impossible to write to buffer of size %d bytes", size);
                ret = HDM_JSON_FAIL;
                goto error;
        }

        aux = (char *) &buffer[*buffer_offset];
        ret = snprintf(aux, size, "\"%s\":[", name);

        if (ret <= 0 || ret >= (size_t) size) {
                HDM_LOG_DEBUG("Failure writing certificate. Return: %d Size: %d\n", ret, size);
                ret = HDM_JSON_FAIL;
                goto error;
        }

        *buffer_offset += ret;

        /**
         * The x5c field must be in order ["HDM_DRK_CERT", "DEVICE_DRK_CERT"]
         * drk_cert_chain_t[] is in the order ["DEVICE_DRK_CERT", "HDM_DRK_CERT"]
         * So it is needed the start appending from last position of drk_cert_chain_t[]
         */
        for (i = array_size - 1; i >= 0; i--) {
                size = buffer_size - *buffer_offset;

                if (size <= 0) {
                        HDM_LOG_DEBUG("Impossible to write to buffer of size %d bytes", size);
                        ret = HDM_JSON_FAIL;
                        goto error;
                }

                if (i < (array_size - 1)) {
                        ret = json_append_single_char(buffer, buffer_size, buffer_offset, ',');
                        if (ret != HDM_STATUS_SUCCESS) {
                                HDM_LOG_DEBUG("json_append_single_char FAIL");
                                goto error;
                        }
                }

                if ((size_t) size <= array[i].certificate_len) {
                        HDM_LOG_DEBUG("Buffer is not big enough to write DRK certificates.\n");
                        ret = HDM_JSON_FAIL;
                        goto error;
                }

                aux = (char *) &buffer[*buffer_offset];
                ret = snprintf(aux, size, "\"%s\"", array[i].certificate);

                if (ret <= 0 || ret >= (size_t) size) {
                        HDM_LOG_DEBUG("Failure II writing certificate to payload. Return: %d Size: %d\n", ret, size);
                        ret = HDM_JSON_FAIL;
                        goto error;
                }

                *buffer_offset += ret;
                buffer[*buffer_offset] = '\0';
        }

        ret = json_append_single_char(buffer, buffer_size, buffer_offset, ']');

        if (ret != HDM_STATUS_SUCCESS) {
                goto error;
        }

        ret = HDM_STATUS_SUCCESS;

error:
        return ret;
}
