/*
 * Copyright (c) 2017 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Created in Samsung Ukraine R&D Center (SRK) under a contract between
 * LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine)
 * and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
 */

/**
 * @file TigerJson.c
 * @brief Implements JSON functionality.
 * @author Victor Kopp <v.kopp@samsung.com>
 * @date Created Jun 12, 2017
 */
#include <stdio.h>

#include "TigerJson.h"

#include <inttypes.h>
#include <limits.h>

#include "TigerLogging.h"
#include "TigerMacroses.h"
#include "TzwSerialNumber.h"
#include "TzwMemory.h"
#include "TzwString.h"

#define MAX_JSON_DATA_LEN (512)
#define MAX_UINT32_T_LEN (10)
#define MAX_UINT64_T_LEN (20)

struct __JsonObject {
     char* data;
     size_t maxLength;
     uint32_t length;
};

void jsonCreate(JsonObject_t* obj) {
    (*obj) = tzwMalloc(sizeof(struct __JsonObject));
    if (NULL == *obj) {
        LOG_E("Cannot create JSON object.");
        *obj = NULL;
        return;
    }
   (*obj)->data = (char*) tzwMalloc(MAX_JSON_DATA_LEN);
    if (NULL == (*obj)->data) {
        LOG_E("Cannot allocate JSON data.");
        jsonRelease(*obj);
        *obj = NULL;
        return;
    }

    (*obj)->maxLength = MAX_JSON_DATA_LEN;
    (*obj)->length = 0;
}

void jsonRelease(JsonObject_t obj) {
    if (NULL != obj) {
        tzwFree(obj->data);
        obj->data = NULL;
        obj->length = 0;
        obj->maxLength = 0;
        tzwFree(obj);
    }
}

TEE_Result jsonWriteRawString(JsonObject_t obj, const char* str) {
    TIGER_ASSERT(NULL != obj);
    TIGER_ASSERT(NULL != str);

    size_t strLen = strnlen(str, MAX_JSON_DATA_LEN);
    size_t currLen = obj->length + strLen;
    TIGER_CHECK_CONDITION_RETURN(currLen < UINT_MAX);

    if (obj->maxLength < currLen) {
        size_t currMaxLength = obj->maxLength + strLen + MAX_JSON_DATA_LEN;
        obj->data = (char*) tzwRealloc(obj->data, currMaxLength);
        if (NULL == obj->data) {
            LOG_E("Cannot allocate JSON data.");
            jsonRelease(obj);
            return TEE_ERROR_OUT_OF_MEMORY;
        }
        obj->maxLength = currMaxLength;
    }

    obj->data = tzwStrncat(obj->data, str, strLen, currLen);
    obj->length = (uint32_t) currLen;
    return TEE_SUCCESS;
}

TEE_Result jsonWriteKeyUint32(JsonObject_t obj, const char* key, const uint32_t val) {
    TIGER_ASSERT(NULL != obj);
    TIGER_ASSERT(NULL != obj->data);
    TIGER_ASSERT(NULL != key);

    TEE_Result status = jsonWriteRawString(obj, "\"");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, key);
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, "\":");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");

    char valStr[MAX_UINT32_T_LEN];
    int ret = snprintf(valStr, MAX_UINT32_T_LEN, "%u", val);
    if (ret < 0) {
        LOG_E("Error during composing uint32_t integer.");
        LOG_D("snprintf returned %d", ret);
        return TEE_ERROR_GENERIC;
    }

    status = jsonWriteRawString(obj, valStr);
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");

    return status;
}

TEE_Result jsonWriteKeyUint32AsStr(JsonObject_t obj, const char* key, const uint32_t val) {
    TIGER_ASSERT(NULL != obj);
    TIGER_ASSERT(NULL != obj->data);
    TIGER_ASSERT(NULL != key);

    TEE_Result status = jsonWriteRawString(obj, "\"");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, key);
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, "\":\"");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");

    char valStr[MAX_UINT32_T_LEN];
    int ret = snprintf(valStr, MAX_UINT32_T_LEN, "%u", val);
    if (ret < 0) {
        LOG_E("Error during composing uint32_t integer.");
        LOG_D("snprintf returned %d", ret);
        return TEE_ERROR_GENERIC;
    }
    status = jsonWriteRawString(obj, valStr);
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, "\"");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");

    return status;
}

TEE_Result jsonWriteKeyUint64(JsonObject_t obj, const char* key, const uint64_t val) {
    TIGER_ASSERT(NULL != obj);
    TIGER_ASSERT(NULL != obj->data);
    TIGER_ASSERT(NULL != key);

    TEE_Result status = jsonWriteRawString(obj, "\"");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, key);
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, "\":");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");

    char valStr[MAX_UINT64_T_LEN];
    int ret = snprintf(valStr, MAX_UINT64_T_LEN, "%"PRIu64, val);
    if (ret < 0) {
        LOG_E("Error during composing uint64_t integer.");
        LOG_D("snprintf returned %d", ret);
        return TEE_ERROR_GENERIC;
    }
    status = jsonWriteRawString(obj, valStr);
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");

    return status;
}

TEE_Result jsonWriteKeyUint64AsStr(JsonObject_t obj, const char* key, const uint64_t val) {
    TIGER_ASSERT(NULL != obj);
    TIGER_ASSERT(NULL != obj->data);
    TIGER_ASSERT(NULL != key);

    TEE_Result status = jsonWriteRawString(obj, "\"");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, key);
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, "\":\"");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");

    char valStr[MAX_UINT64_T_LEN];
    int ret = snprintf(valStr, MAX_UINT64_T_LEN, "%"PRIu64, val);
    if (ret < 0) {
        LOG_E("Error during composing uint64_t integer.");
        LOG_D("snprintf returned %d", ret);
        return TEE_ERROR_GENERIC;
    }
    status = jsonWriteRawString(obj, valStr);
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, "\"");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");

    return status;
}

TEE_Result jsonWriteKeyString(JsonObject_t obj, const char* key, const char* str) {
    TIGER_ASSERT(NULL != obj);
    TIGER_ASSERT(NULL != obj->data);
    TIGER_ASSERT(NULL != key);
    TIGER_ASSERT(NULL != str);

    TEE_Result status = jsonWriteRawString(obj, "\"");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, key);
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, "\":\"");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, str);
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");
    status = jsonWriteRawString(obj, "\"");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");

    return status;
}

TEE_Result jsonWriteComma(JsonObject_t obj) {
    TIGER_ASSERT(NULL != obj);
    TIGER_ASSERT(NULL != obj->data);

    TEE_Result status = jsonWriteRawString(obj, ",");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");

    return status;
}

TEE_Result jsonOpenBrace(JsonObject_t obj) {
    TIGER_ASSERT(NULL != obj);
    TIGER_ASSERT(NULL != obj->data);

    TEE_Result status = jsonWriteRawString(obj, "{");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");

    return status;
}

TEE_Result jsonCloseBrace(JsonObject_t obj) {
    TIGER_ASSERT(NULL != obj);
    TIGER_ASSERT(NULL != obj->data);

    TEE_Result status = jsonWriteRawString(obj, "}");
    TIGER_CHECK_TEE_STATUS_SUCCESS_RETURN("jsonWriteRawString");

    return status;
}

TEE_Result jsonWriteKeyBuffer(JsonObject_t obj, const char* key,
                              const uint8_t* buffer, const uint32_t bufferSize) {
    TIGER_ASSERT(NULL != obj);
    TIGER_ASSERT(NULL != obj->data);
    TIGER_ASSERT(NULL != key);
    TIGER_ASSERT(NULL != buffer);

    char* tempBuffer = tzwMalloc(bufferSize + 1); // plus '0' for C-String
    TIGER_CHECK_BUFFER_ALLOCATED_RETURN(tempBuffer);

    tzwMemMove(tempBuffer, buffer, bufferSize);
    TEE_Result status = jsonWriteKeyString(obj, key, tempBuffer);

    tzwFree(tempBuffer);

    return status;
}

const char* jsonGetPlain(const JsonObject_t obj) {
    TIGER_ASSERT(NULL != obj);
    TIGER_ASSERT(NULL != obj->data);

    return obj->data;
}

uint32_t jsonGetLength(const JsonObject_t obj) {
    TIGER_ASSERT(NULL != obj);
    TIGER_ASSERT(NULL != obj->data);

    return obj->length;
}
