/*
 * Copyright (c) 2024-2024 Huawei Device Co., Ltd.
 * 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 "utils_json.h"
#include "comm_def.h"
#include "iotc_log.h"
#include "utils_assert.h"
#include "securec.h"
#include "utils_common.h"
#include "iotc_conf.h"
#include "iotc_errcode.h"

IotcJson *UtilsJsonCreateErrcode(int32_t errcode)
{
    IotcJson *obj = IotcJsonCreate();
    if (obj == NULL) {
        return NULL;
    }

    int32_t ret = IotcJsonAddNum2Obj(obj, STR_ERRCODE, errcode);
    if (ret != IOTC_OK) {
        IOTC_LOGW("add num to obj err %d", ret);
        IotcJsonDelete(obj);
        return NULL;
    }
    return obj;
}

int32_t UtilsJsonGetNum(const IotcJson *json, const char *name, int32_t *num)
{
    CHECK_RETURN(json != NULL && name != NULL && num != NULL, IOTC_ERR_PARAM_INVALID);

    int64_t err;
    int32_t ret = IotcJsonGetNum(IotcJsonGetObj(json, name), &err);
    if (ret != IOTC_OK) {
        return ret;
    }
    if (err > INT32_MAX) {
        IOTC_LOGW("invalid num");
        return IOTC_CORE_COMM_UTILS_ERR_JSON_INVALID_ERRCODE;
    }
    *num = err;
    return IOTC_OK;
}

int32_t UtilsJsonGetUint(const IotcJson *json, const char *name, uint32_t *num)
{
    CHECK_RETURN(json != NULL && name != NULL && num != NULL, IOTC_ERR_PARAM_INVALID);

    int64_t jsonNum;
    int32_t ret = IotcJsonGetNum(IotcJsonGetObj(json, name), &jsonNum);
    if (ret != IOTC_OK) {
        return ret;
    }
    if (jsonNum > UINT32_MAX || jsonNum < 0) {
        IOTC_LOGW("invalid num %lld", jsonNum);
        return IOTC_CORE_COMM_UTILS_ERR_JSON_INVALID_ERRCODE;
    }
    *num = jsonNum;
    return IOTC_OK;
}

int32_t UtilsJsonGetString(const IotcJson *json, const char *key, char *buf, uint32_t len)
{
    CHECK_RETURN(json != NULL && key != NULL && buf != NULL && len != 0, IOTC_ERR_PARAM_INVALID);

    const char *str = IotcJsonGetStr(IotcJsonGetObj(json, key));
    if (str == NULL) {
        IOTC_LOGW("get json str error %s", key);
        return IOTC_ADAPTER_JSON_ERR_GET_STRING;
    }
    int32_t ret = strcpy_s(buf, len, str);
    if (ret != EOK) {
        IOTC_LOGW("strcpy error %u/%u", len, strlen(str));
        return IOTC_ERR_SECUREC_STRCPY;
    }
    return IOTC_OK;
}

int32_t UtilsGenErrcodeJsonStr(int32_t errcode, char **out, uint32_t *outLen)
{
    CHECK_RETURN_LOGW((out != NULL) && (outLen != NULL), IOTC_ERR_PARAM_INVALID, "invalid param");

    IotcJson *obj = UtilsJsonCreateErrcode(errcode);
    if (obj == NULL) {
        IOTC_LOGW("create json error");
        return IOTC_ADAPTER_JSON_ERR_CREATE;
    }

    *out = UtilsJsonPrintByMalloc(obj);
    IotcJsonDelete(obj);
    if (*out == NULL) {
        IOTC_LOGW("json print err");
        return IOTC_CORE_COMM_UTILS_ERR_JSON_MALLOC_PRINT;
    }
    *outLen = strlen(*out);

    return IOTC_OK;
}

int32_t UtilsJsonAddHexify(IotcJson *json, const char *name, const uint8_t *input, uint32_t inputLen)
{
    uint32_t dataLen = HEXIFY_LEN(inputLen) + 1;
    char *data = (char *)IotcCalloc(dataLen, sizeof(char));
    if (data == NULL) {
        return IOTC_ADAPTER_MEM_ERR_MALLOC;
    }

    if (!UtilsHexify(input, inputLen, data, dataLen)) {
        IotcFree(data);
        return IOTC_CORE_COMM_UTILS_ERR_HEXIFY;
    }

    int32_t ret = IotcJsonAddStr2Obj(json, name, data);
    IotcFree(data);
    return ret;
}

int32_t UtilsJsonAddStrTable(IotcJson *json, const UtilsJsonStrItem *tbl, uint32_t size)
{
    CHECK_RETURN_LOGW((json != NULL) && (tbl != NULL) && (size != 0), IOTC_ERR_PARAM_INVALID, "invalid param");

    int32_t ret;
    for (uint32_t i = 0; i < size; ++i) {
        ret = IotcJsonAddStr2Obj(json, tbl[i].name, tbl[i].value);
        if (ret != IOTC_OK) {
            IOTC_LOGW("json add %s error", NON_NULL_STR(tbl[i].name));
            return ret;
        }
    }
    return IOTC_OK;
}

int32_t UtilsJsonParseStrTable(IotcJson *json, UtilsJsonStrBufItem *tbl, uint32_t size)
{
    CHECK_RETURN_LOGW((json != NULL) && (tbl != NULL) && (size != 0), IOTC_ERR_PARAM_INVALID, "invalid param");

    int32_t ret;
    for (uint32_t i = 0; i < size; ++i) {
        ret = UtilsJsonGetString(json, tbl[i].name, tbl[i].buf, tbl[i].size);
        if (ret != IOTC_OK) {
            IOTC_LOGW("json get string %s error", NON_NULL_STR(tbl[i].name));
            return ret;
        }
    }
    return IOTC_OK;
}

char *UtilsJsonPrintByMalloc(const IotcJson *json)
{
    char *ret = IotcJsonPrint(json);
    if (ret == NULL) {
        return NULL;
    }

#if IOTC_CONF_JSON_FREE_EQUAL_MALLOC_FREE
    return ret;
#else
    char *copy = UtilsStrDup(ret);
    IotcJsonFreePrint(ret);
    return copy;
#endif
}

IotcJson *UtilsJsonCreateKeyIntValue(const char *key, int32_t value)
{
    IotcJson *obj = IotcJsonCreate();
    if (obj == NULL) {
        return NULL;
    }

    int32_t ret = IotcJsonAddNum2Obj(obj, key, value);
    if (ret != IOTC_OK) {
        IOTC_LOGW("add num to obj err %d", ret);
        IotcJsonDelete(obj);
        return NULL;
    }
    return obj;
}

int32_t UtilsGenKeyIntValueJsonStr(const char *key, int32_t value, char **out, uint32_t *outLen)
{
    CHECK_RETURN_LOGW((key != NULL) && (out != NULL) && (outLen != NULL), IOTC_ERR_PARAM_INVALID, "invalid param");

    IotcJson *obj = UtilsJsonCreateKeyIntValue(key, value);
    if (obj == NULL) {
        IOTC_LOGW("create json error");
        return IOTC_ADAPTER_JSON_ERR_CREATE;
    }

    *out = UtilsJsonPrintByMalloc(obj);
    IotcJsonDelete(obj);
    if (*out == NULL) {
        IOTC_LOGW("json print err");
        return IOTC_CORE_COMM_UTILS_ERR_JSON_MALLOC_PRINT;
    }
    *outLen = strlen(*out);

    return IOTC_OK;
}