#ifdef WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include "bmser_log.h"
#include "cJSON.h"
#include "bmser_cjson.h"

int g_item_mem_alloc_count = 0;
int g_file_mem_alloc_count = 0;

/**
 * @brief 根据给定的路径，在 cJSON 对象中找到对应的项。
 *
 * @param[in] root cJSON 根对象指针。
 * @param[in] path 路径字符串。
 * @return cJSON* 成功时返回找到的 cJSON 项, 否则返回NULL。
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-12-08           JHC        创建初始版本          \n
 */
cJSON* get_json_item_by_path(cJSON* root, const char* path) {
    if (!root || !path) return NULL;
#ifdef WIN32
    char* path_copy = _strdup(path); // 创建path的副本
#else
    char* path_copy = strdup(path); // 创建path的副本
#endif
    if (!path_copy) {
        // 处理_strdup失败的情况
        return NULL;
    }
#ifdef WIN32
    char* context = NULL; // strtok_s需要的额外指针，用于在后续调用中维持状态
    char* token = strtok_s(path_copy, ".[", &context); // 初始化标记
#else
    char* rest = path_copy;  // strtok_r的额外指针，用于保持上下文
    char* token = strtok_r(path_copy, ".[", &rest); // 初始化标记
#endif
    cJSON* current_element = root;

    while (token != NULL && current_element != NULL) {
        // 数组访问
        char* endptr = NULL;  // strtol将在这里指向遍历后的下一个字符
        long index = strtol(token, &endptr, 10);
        // 检查是否有数字被解析，并验证是不是遇到了中括号内的情况
        if (endptr != token && *endptr == ']') {
            current_element = cJSON_GetArrayItem(current_element, (int)index);
        }
        else {
            current_element = cJSON_GetObjectItemCaseSensitive(current_element, token);
        }
#ifdef WIN32
        token = strtok_s(NULL, ".[", &context);
#else
        // 为下一个迭代获得新token
        token = strtok_r(NULL, ".[", &rest);
#endif
    }

    free(path_copy);
    return current_element; // 返回找到的项或NULL
}

/**
 * @brief 解析 JSON 文本并获取指定路径上 JSON 数组的长度。
 *
 * @param[in] json_text JSON 文本字符串。
 * @param[in] path 要获取数组长度的 JSON 路径。
 * @return int 成功时返回数组的长度, 如果输入无效或者项不存在返回-1。
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-12-08           JHC        创建初始版本          \n
 */
int json_get_array_length(const char* json_text, const char* path) {
    if (json_text == NULL || path == NULL) {
        // 如果输入参数无效，返回错误。
        return -1;
    }

    cJSON* json = cJSON_Parse(json_text);
    if (json == NULL) {
        // 如果无法解析 JSON，返回错误。
        return -1;
    }

    cJSON* array_item = get_json_item_by_path(json, path);
    if (array_item == NULL || !cJSON_IsArray(array_item)) {
        // 如果找不到路径对应的项或该项不是数组，返回错误。
        cJSON_Delete(json);
        return -1;
    }

    int array_length = cJSON_GetArraySize(array_item);
    cJSON_Delete(json);
    return array_length;
}

/**
 * @brief 解析 JSON 对象并获取指定路径上 JSON 数组的长度。(适用于比较大的配置文件)
 *
 * @param[in] json parse后的JSON对象。
 * @param[in] path 要获取数组长度的 JSON 路径。例:frames[0].regs
 * @return int 成功时返回数组的长度, 如果输入无效或者项不存在返回-1。
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-12-08           JHC        创建初始版本          \n
 */
int json_get_array_length_by_json(void* json, const char* path) {
    if (path == NULL) {
        // 如果输入参数无效，返回错误。
        return -1;
    }

    if (json == NULL) {
        // 如果解析后的JSON对象为空，返回错误。
        return -1;
    }

    cJSON* array_item = get_json_item_by_path((cJSON*)json, path);
    if (array_item == NULL || !cJSON_IsArray(array_item)) {
        // 如果找不到路径对应的项或该项不是数组，返回错误。
        return -1;
    }

    int array_length = cJSON_GetArraySize(array_item);
    return array_length;
}

/**
 * @brief 根据给定的 JSON 文本和路径获取数据，并填充到结果结构体中。
 *
 * @param[in] json_text JSON 文本字符串。
 * @param[in] path 路径字符串，指定要获取数据的路径。
 * @param[out] result 指向 JsonValue 结构体的指针，用于存放获取到的数据。
 * @return int 成功时返回0, 失败时返回-1。
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-12-08           JHC        创建初始版本          \n
 */
int json_value_get_by_json_path(const char* json_text, const char* path, JsonValue* result) {

    int ret = 0;
    // 参数有效性检查
    if (json_text == NULL || path == NULL || result == NULL) {
        return -1; // 失败
    }

    // 解析整个JSON文档
    cJSON* json = cJSON_Parse(json_text);
    if (json == NULL) {
        result->type = JSON_VALUE_NONE;
        return -1; // 失败
    }

    // 获取由点分隔的路径指定的项
    cJSON* item = get_json_item_by_path(json, path);

    // 根据项的类型设置结果结构体
    if (item != NULL) {
        if (cJSON_IsNumber(item)) {
            result->type = JSON_VALUE_NUMBER;
            result->doubleVal = item->valuedouble;
            result->int64Val = (long long)item->valuedouble;
        }
        else if (cJSON_IsString(item)) {
            result->type = JSON_VALUE_STRING;
            strncpy(result->stringVal, item->valuestring, MAX_JSON_STRING_SIZE - 1); // 安全的复制函数
            result->stringVal[MAX_JSON_STRING_SIZE - 1] = '\0'; // 确保字符串以 null 终止
        }
        else if (cJSON_IsBool(item)) {
            // bool类型的归类为number类型, true为1, false为0
            result->type = JSON_VALUE_NUMBER;
            result->int64Val = cJSON_IsTrue(item);
        }
        else {
            result->type = JSON_VALUE_NONE;
        }
    }
    else {
        // 没有找到对应的项
        result->type = JSON_VALUE_NONE;
        ret = -1;
    }

    cJSON_Delete(json); // 清理 cJSON 对象
    return ret; // 成功
}

/**
 * @brief 根据给定的json对象和路径获取数据，并填充到结果结构体中。(适用于比较大的配置文件)
 * @param[in] json cJSON parse后的json对象
 * @param[in] path 路径字符串，指定要获取数据的路径。例:frames[0].regs[0].key
 * @param[out] result 指向 JsonValue 结构体的指针，用于存放获取到的数据。
 * @return int 成功时返回0, 失败时返回-1。
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-12-08           JHC        创建初始版本          \n
 */
int json_value_get_by_json(void* json, const char* path, JsonValue* result)
{
    int ret = 0;
    // 参数有效性检查
    if (path == NULL || result == NULL) {
        return -1; // 失败
    }

    if (json == NULL) {
        result->type = JSON_VALUE_NONE;
        return -1; // 失败
    }

    // 获取由点分隔的路径指定的项
    cJSON* item = get_json_item_by_path((cJSON*)json, path);

    // 根据项的类型设置结果结构体
    if (item != NULL) {
        if (cJSON_IsNumber(item)) {
            result->type = JSON_VALUE_NUMBER;
            result->doubleVal = item->valuedouble;
            result->int64Val = (long long)item->valuedouble;
        }
        else if (cJSON_IsString(item)) {
            result->type = JSON_VALUE_STRING;
            strncpy(result->stringVal, item->valuestring, MAX_JSON_STRING_SIZE - 1); // 安全的复制函数
            result->stringVal[MAX_JSON_STRING_SIZE - 1] = '\0'; // 确保字符串以 null 终止
        }
        else if (cJSON_IsBool(item)) {
            // bool类型的归类为number类型, true为1, false为0
            result->type = JSON_VALUE_NUMBER;
            result->int64Val = cJSON_IsTrue(item);
        }
        else {
            result->type = JSON_VALUE_NONE;
        }
    }
    else {
        // 没有找到对应的项
        result->type = JSON_VALUE_NONE;
        ret = -1;
    }

    return ret; // 成功
}

 /**
  * @brief 读取文件内容并返回文件的全部文本内容。
  * @param[i] fileName 需要读取的文件名。
  * @return 返回指向文件内容的指针。需要由调用者负责释放这段内存。若出错，则返回NULL。
  * @par         修改日志:
  * Date                Author     Description           \n
  * 2023-12-08           JHC        创建初始版本          \n
  */
char* jsonFileContent_read(const char* fileName) 
{
    if (fileName == NULL) {
        dy_syslog(LOG_ERR,"Input file name is NULL.\n");
        return NULL;
    }

    // 尝试打开文件
    FILE* f = fopen(fileName, "rb");
    if (f == NULL) {
        dy_syslog(LOG_ERR,"Failed to open %s\nPlease check if the file exists and the path is correct.\n", fileName);
        return NULL;
    }

    // 跳到文件末尾获取文件大小
    fseek(f, 0, SEEK_END);
    long len = ftell(f);
    // 返回文件的起始位置
    rewind(f);

    // 为文件内容分配内存
    char* fileContent = (char*)malloc(len + 1);
    if (fileContent == NULL) {
        dy_syslog(LOG_ERR,"Memory allocation failed for file content.\n");
        fclose(f); // 务必关闭文件
        return NULL;
    }

    // 从文件中读取内容
    size_t readLength = fread(fileContent, 1, len, f);
    if (readLength != len) {
        dy_syslog(LOG_ERR,"Could not read the full file content.\n");
        free(fileContent); // 释放内存
        fclose(f);  // 关闭文件
        return NULL;
    }

    // 确保字符串以 null 终止
    fileContent[len] = '\0';

    // 关闭文件
    fclose(f);
    g_file_mem_alloc_count++; // 记录内存申请次数

    return fileContent;
}

/**
 * @brief 释放由jsonFileContent_read分配的内存。
 * @param[i] fileContent 指向要释放的内存的指针
 * @return
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2024-01-05           QZY        创建初始版本          \n
 */
void free_jsonFileContent(char* fileContent)
{
    if (fileContent != NULL) {
        free(fileContent);
        g_file_mem_alloc_count--; // 记录内存释放次数
    }
}

/**
 * @brief 读取json文件内容并返回解析后的root对象
 * @param[i] fileName 需要读取的文件名。
 * @return 返回指向root对象的void*指针。需要由调用者负责释放这段内存。若出错，则返回NULL。
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2024-01-05           QZY        创建初始版本          \n
 */
void* get_jsonItem_by_fileContent(const char* fileName)
{
    char *json_text = jsonFileContent_read(fileName);
    if (json_text == NULL) {
        return NULL;
    }

    cJSON* root = cJSON_Parse(json_text);
    free_jsonFileContent(json_text);

    if (root == NULL) {
        dy_syslog(LOG_ERR, "JSON parsing failed.\n");
        return NULL;
    }
    g_item_mem_alloc_count++; //记录内存申请次数
    return (void*)root;
}

/**
 * @brief 根据消息内容并返回解析后的root对象
 * @param[i] msg 消息内容。
 * @return 返回指向root对象的void*指针。需要由调用者负责释放这段内存。若出错，则返回NULL。
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2024-02-07           JHC        创建初始版本          \n
 */
void* get_jsonItem_by_msgContent(const char* msg)
{
    if (msg == NULL) {
        return NULL;
    }
    cJSON* root = cJSON_Parse(msg);

    if (root == NULL) {
        dy_syslog(LOG_ERR, "JSON parsing failed.\n");
        return NULL;
    }
    g_item_mem_alloc_count++; //记录内存申请次数
    return (void*)root;
}

/**
 * @brief 释放JSON对象的资源
 * @param[in] json 通过JSON对象
 * @par         修改日志:
 * Date                Author     Description
 * 2024-01-05          QZY        创建初始版本
 */
void free_jsonItem(void* json) 
{
    if (json != NULL) {
        cJSON_Delete((cJSON*)json);
        g_item_mem_alloc_count--; // 记录内存释放次数
    }
}