/*
 * Copyright (C) 2023, KylinSoft Co., Ltd.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library.  If not, see <https://www.gnu.org/licenses/>.
 *
 * Authors: tianshaoshuai <tianshaoshuai@kylinos.cn>
 *
 */

#include "ksettingsschema.h"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
#include <syslog.h>

/**
 * @brief 嵌套哈希表，
 * 表层 key:AppData* value: 版本号哈希表GHashTable*
 * 版本号哈希表 key VersionData* 项目哈希表GHashTable*
 * 项目哈希表 key 项目id char*    配置详情KSettingsSchema*
 */
GHashTable *schemas_table = NULL;
/*************************hash函数*****************************/

guint data_hash(gconstpointer data)
{
    return g_str_hash(((AppData *)data)->name);
}

gboolean data_equal(gconstpointer a, gconstpointer b)
{
    return 0 == g_strcmp0(((AppData *)a)->name, ((AppData *)b)->name);
}

void app_data_destroy(gpointer data)
{
    if (NULL == data)
        return;
    AppData *tmp = (AppData *)data;
    g_free(tmp->name);
    g_free(tmp->default_version);
    g_free(tmp);
}

void version_data_destroy(gpointer data)
{
    if (NULL == data)
        return;
    VersionData *tmp = (VersionData *)data;
    g_free(tmp->name);
    g_strfreev(tmp->compatible);
    g_free(tmp);
}

void schema_data_destroy(gpointer data)
{
    if (NULL == data)
        return;
    KSettingsSchema *schema = (KSettingsSchema *)data;
    g_free(schema->id);
    g_free(schema->version);
    if (schema->values)
    {
        g_hash_table_destroy(schema->values);
        schema->values = NULL;
    }
    if (schema->children)
    {
        g_array_free(schema->children, TRUE);
        schema->children = NULL;
    }
    g_free(schema);
}

static char **_split_string(const gchar *id, const char seq)
{
    if (NULL == id)
        return NULL;

    const gchar *p, *q;
    p = q = id;
    int count = 1;
    while (*p != '\0')
    {
        if (*p++ == seq)
            count++;
    }

    char **list = calloc(count + 1, sizeof(char *));
    if (NULL == list)
    {
        // TODO: write log
        return NULL;
    }

    int i = 0;
    p = id;
    while (*p != '\0')
    {
        if (*p == seq)
        {
            list[i] = calloc(p - q + 1, sizeof(char));
            if (NULL == list[i])
            {
                g_strfreev(list);
                return NULL;
            }
            strncpy(list[i++], q, p - q);
            q = p + 1;
        }
        p++;
    }
    list[i] = calloc(p - q + 1, sizeof(char));
    if (NULL == list[i])
    {
        g_strfreev(list);
        return NULL;
    }
    strncpy(list[i], q, p - q);

    return list;
}

/**********************************************读取数据库*****************************************************/

/************************************SQL回调函数*********************************************/
// 读取一行
static int _sql_call_get_columns(void *data, int argc, char **argv, char **azColName)
{
    *((char ***)data) = g_strdupv(argv);
    return 0;
}

// 读取多行
static int line_count = 0; // 行计数
static int _sql_call_get_lines(void *data, int argc, char **argv, char **azColName)
{
    char ***lines = *((char ****)data);
    char **columns = calloc(argc + 1, sizeof(char *));
    for (int i = 0; i < argc; i++)
    {
        char *fewf = argv[i];
        columns[i] = g_strdup(argv[i]);
    }
    char ***tmp = realloc(lines, sizeof(char **) * (line_count + 2));
    if (NULL == tmp)
    {
        line_count = 0;
        for (int i = 0; i < line_count; i++)
        {
            for (int j = 0; j < argc; j++)
            {
                free(lines[i][j]);
            }
            free(lines[i]);
        }
        free(lines);
        lines = NULL;
        return 1;
    }
    lines = tmp;
    lines[line_count++] = columns;
    lines[line_count] = NULL;
    *((char ****)data) = lines;
    return 0;
}

// 读取一个组件key和child
static int _sql_call_get_schema(void *data, int argc, char **argv, char **azColName)
{
    KSettingsSchema *schema = (KSettingsSchema *)data;

    if (NULL == argv[3])
    {
        // hash表  s：hash（s:s）
        if (NULL == schema->values)
            schema->values = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_hash_table_destroy);
        // hash表 s:s
        GHashTable *item = g_hash_table_new(g_str_hash, g_str_equal);
        if (NULL == item)
            goto err;
        if (argv[2])
            g_hash_table_insert(item, "property", g_strdup(argv[2])); // 键名
        if (argv[4])
            g_hash_table_insert(item, "user_value", g_strdup(argv[4])); // 键值
        if (argv[5])
            g_hash_table_insert(item, "data_type", g_strdup(argv[5])); // 类型
        if (argv[6])
            g_hash_table_insert(item, "default_value", g_strdup(argv[6])); // 默认值
        if (argv[7])
            g_hash_table_insert(item, "permission", g_strdup(argv[7])); // 读写权限
        if (argv[8])
            g_hash_table_insert(item, "range", g_strdup(argv[8])); // 取值范围
        if (argv[10])
            g_hash_table_insert(item, "description", g_strdup(argv[10])); // 详细描述
        if (argv[11])
            g_hash_table_insert(item, "summary", g_strdup(argv[11])); // 概要
        g_hash_table_insert(schema->values, g_strdup(argv[2]), item);
    }
    else
    {
        if (NULL == schema->children)
        {
            schema->children = g_array_new(FALSE, FALSE, sizeof(char *));
        }
        char *tmp = g_strdup(argv[3]);
        g_array_append_val(schema->children, tmp);
    }
    return 0;
err:
    return 1;
}
/*SQL回调函数*/

/************************************SQL读取函数*********************************************/

// 读取一个组件
static KSettingsSchema *_get_schema(const char *id, const char *version)
{
    char *user_name = getlogin();
    if (user_name == NULL)
        return NULL;

    KSettingsSchema *schema = NULL;

    char db_file[PATH_MAX];
    if (0 == strcmp(user_name, "root"))
        sprintf(db_file, "/etc/kylin-config/kylin-config/user.db");
    else
        sprintf(db_file, "/home/%s/.config/kylin-config/user.db", user_name);

    char **id_list = _split_string(id, '.');
    if (!id_list)
        return NULL;

    // 读取数据库数据
    sqlite3 *db = NULL;
    char *err_msg = NULL;

    // 打开数据库
    int rc = sqlite3_open(db_file, &db);
    if (rc != SQLITE_OK)
    {
        rc = sqlite3_open("/etc/kylin-config/kylin-config/user.db", &db);
        if (rc != SQLITE_OK)
        {
            fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db));
            goto out;
        }
    }

    // 查询数据
    int app_id = 0, version_id = 0, parent_id = 0;
    char sql[100];
    sprintf(sql, "SELECT * FROM app WHERE app_name = '%s'", id_list[0]);
    char **columns = NULL;
    rc = sqlite3_exec(db, sql, _sql_call_get_columns, &columns, &err_msg);
    if (rc != SQLITE_OK || NULL == columns)
    {
        fprintf(stderr, "查询失败: %s\n", err_msg);
        goto out;
    }
    app_id = atoi(columns[0]);
    if (NULL == version)
        version = columns[2];
    g_strfreev(columns);
    sprintf(sql, "SELECT * FROM version WHERE app_id = %d AND version = '%s'", app_id, version);

    rc = sqlite3_exec(db, sql, _sql_call_get_columns, &columns, &err_msg);
    if (rc != SQLITE_OK)
    {
        // TODO: 版本兼容性处理
        fprintf(stderr, "查询失败: %s\n", err_msg);
        goto out;
    }
    version_id = atoi(columns[0]);
    g_strfreev(columns);

    int i = 1;
    while (id_list[i])
    {
        sprintf(sql, "SELECT * FROM configures WHERE version_id = %d AND parent = %d AND group_name = '%s'",
                version_id, parent_id, id_list[i++]);

        rc = sqlite3_exec(db, sql, _sql_call_get_columns, &columns, &err_msg);
        if (rc != SQLITE_OK)
        {
            fprintf(stderr, "查询失败: %s\n", err_msg);
            goto out;
        }
        parent_id = atoi(columns[0]);
        g_strfreev(columns);
    }

    schema = (KSettingsSchema *)calloc(1, sizeof *schema);
    schema->id = strdup(id);
    schema->version = strdup(version);
    // 如果获取数据失败，毁掉函数_sql_call_get_key会释放result的内存并将result置空
    sprintf(sql, "SELECT * FROM configures WHERE version_id = %d AND parent = %d",
            version_id, parent_id);

    rc = sqlite3_exec(db, sql, _sql_call_get_schema, schema, &err_msg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "查询失败: %s\n", err_msg);
        goto out;
    }

out:
    g_strfreev(id_list);
    if (err_msg)
        sqlite3_free(err_msg);
    if (db)
        sqlite3_close(db);
    return schema;
}

// 递归读取组件配置
static int _get_component_recursively(char *version, int version_id, int parent_id, char *parent_name, GHashTable *schemas, sqlite3 *db)
{
    char *err_msg = NULL;
    char sql[128];
    char ***components = NULL;
    line_count = 0;
    sprintf(sql, "SELECT * FROM configures WHERE version_id = %d AND parent = %d AND group_name IS NOT NULL", version_id, parent_id);
    int rc = sqlite3_exec(db, sql, _sql_call_get_lines, &components, &err_msg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db));
        return rc;
    }

    KSettingsSchema *schema = g_hash_table_lookup(schemas, parent_name);
    if (NULL == schema)
    {
        schema = calloc(1, sizeof *schema);
        if (NULL == schema)
            return SQLITE_NOMEM;

        schema->id = strdup(parent_name);
        schema->version = strdup(version);
        g_hash_table_insert(schemas, strdup(parent_name), schema);
    }
    else
    {
        if (schema->values)
        {
            g_hash_table_destroy(schema->values);
            schema->values = NULL;
        }
        if (schema->children)
        {
            g_array_free(schema->children, TRUE);
            schema->children = NULL;
        }
    }
    sprintf(sql, "SELECT * FROM configures WHERE version_id = %d AND parent = %d",
            version_id, parent_id);
    rc = sqlite3_exec(db, sql, _sql_call_get_schema, schema, &err_msg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "查询失败: %s\n", err_msg);
        // schema_data_destroy(schema);
        return rc;
    }

    // components为空
    if (NULL == components)
    {
        return rc;
    }

    for (int i = 0; components[i]; i++)
    {
        parent_id = atoi(components[i][0]);
        char *id = g_strdup_printf("%s.%s", parent_name, components[i][3]);
        // 递归读取
        rc = _get_component_recursively(version, version_id, parent_id, id, schemas, db);
        if (rc != SQLITE_OK)
        {
            for (int j = 0; components[j]; j++)
            {
                for (int k = 0; k < 12; k++)
                    free(components[j][k]);
                free(components[j]);
            }
            free(components);
            return rc;
        }
    }

    for (int j = 0; components[j]; j++)
    {
        for (int k = 0; k < 12; k++)
            free(components[j][k]);
        free(components[j]);
    }
    free(components);
    return rc;
}

// 读取所有组件配置
static void _get_schemas()
{
    char *user_name = getlogin();
    if (user_name == NULL)
    {
        openlog("kysdk-conf2", LOG_PID, LOG_USER);
        syslog(LOG_INFO, "Get user name failed! use root config");
        closelog();
        user_name = "root";
    }

    char db_file[PATH_MAX];
    if (0 == strcmp(user_name, "root"))
        sprintf(db_file, "/root/.config/kylin-config/user.db");
    else
        sprintf(db_file, "/home/%s/.config/kylin-config/user.db", user_name);

    // 读取数据库数据
    sqlite3 *db = NULL;
    char *err_msg = NULL;

    // 打开数据库
    int rc = sqlite3_open(db_file, &db);
    if (rc != SQLITE_OK)
    {
        openlog("kysdk-conf2", LOG_PID, LOG_USER);
        syslog(LOG_INFO, "open %s failed with error code %d! try to open /etc/kylin-config/user.db.", db_file, rc);
        closelog();
        rc = sqlite3_open("/etc/kylin-config/user.db", &db);
        if (rc != SQLITE_OK)
        {
            openlog("kysdk-conf2", LOG_PID, LOG_USER);
            syslog(LOG_INFO, "open /etc/kylin-config/user.db failed with error code %d", rc);
            closelog();
            goto out;
        }
    }
    // 顶层hash， 元素为appData:hash(versionData:hash(s:KSettingsSchema))
    if (NULL == schemas_table)
        schemas_table = g_hash_table_new_full(data_hash, data_equal, app_data_destroy, (GDestroyNotify)g_hash_table_destroy);
    if (NULL == schemas_table)
    {
        openlog("kysdk-conf2", LOG_PID, LOG_USER);
        syslog(LOG_INFO, "Creat hash  Table failed");
        closelog();
        goto out;
    }

    int i = 0;
    char sql[100];
    char ***apps = NULL;

    strcpy(sql, "SELECT * FROM app");
    line_count = 0;
    rc = sqlite3_exec(db, sql, _sql_call_get_lines, &apps, &err_msg);
    if (rc != SQLITE_OK)
    {
        openlog("kysdk-conf2", LOG_PID, LOG_USER);
        syslog(LOG_INFO, "查询失败: %s\n", err_msg);
        closelog();
        goto out;
    }
    while (apps && apps[i])
    {
        char ***versions = NULL;
        int app_id = atoi(apps[i][0]);
        AppData *app_data = calloc(1, sizeof *app_data);
        app_data->name = strdup(apps[i][1]);
        app_data->default_version = strdup(apps[i][2]);

        line_count = 0;
        sprintf(sql, "SELECT * FROM version WHERE app_id = %d", app_id);
        rc = sqlite3_exec(db, sql, _sql_call_get_lines, &versions, &err_msg);
        if (rc != SQLITE_OK)
        {
            openlog("kysdk-conf2", LOG_PID, LOG_USER);
            syslog(LOG_INFO, "查询失败: %s\n", err_msg);
            closelog();
            for (int j = 0; apps[j]; j++)
            {
                for (int k = 0; k < 3; k++)
                    free(apps[j][k]);
                free(apps[j]);
            }
            free(apps);
            goto out;
        }
        // 第二层hash，元素为versionData:hash(s:KSettingsSchema)
        GHashTable *version = g_hash_table_lookup(schemas_table, app_data);
        if (NULL == version)
        {
            version = g_hash_table_new_full(data_hash, data_equal, version_data_destroy, (GDestroyNotify)g_hash_table_destroy);
            if (NULL == version)
            {
                openlog("kysdk-conf2", LOG_PID, LOG_USER);
                syslog(LOG_INFO, "Creat hash2 Table failed");
                closelog();
                goto out;
            }
            g_hash_table_insert(schemas_table, app_data, version);
        }
        else
            app_data_destroy(app_data);

        int version_i = 0;
        while (versions[version_i])
        {
            int version_id = atoi(versions[version_i][0]);
            VersionData *version_data = calloc(1, sizeof *version_data);
            version_data->name = strdup(versions[version_i][2]);
            version_data->compatible = _split_string(versions[version_i][3], '.');
            // 第三层hash，元素为s:KSettingsSchema
            GHashTable *schemas = g_hash_table_lookup(version, version_data);
            if (NULL == schemas)
            {
                schemas = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)schema_data_destroy);
                g_hash_table_insert(version, version_data, schemas);
                if (NULL == schemas)
                {
                    openlog("kysdk-conf2", LOG_PID, LOG_USER);
                    syslog(LOG_INFO, "Creat schemas hash Table failed");
                    closelog();
                    goto out;
                }
            }
            else
                version_data_destroy(version_data);

            rc = _get_component_recursively(versions[version_i][2], version_id, 0, apps[i][1], schemas, db);
            if (rc != SQLITE_OK)
            {
                openlog("kysdk-conf2", LOG_PID, LOG_USER);
                syslog(LOG_INFO, "Read data failed");
                closelog();
                for (int j = 0; j < i + 1; j++)
                {
                    for (int k = 0; k < 3; k++)
                        free(apps[j][k]);
                    free(apps[j]);
                }
                free(apps);
                for (int j = 0; versions[j]; j++)
                {
                    for (int k = 0; k < 4; k++)
                        free(versions[j][k]);
                    free(versions[j]);
                }
                free(versions);
                goto out;
            }
            version_i++;
        }
        if (version)
        {
            for (int j = 0; versions[j]; j++)
            {
                for (int k = 0; k < 4; k++)
                    free(versions[j][k]);
                free(versions[j]);
            }
            free(versions);
        }
        i++;
    }
    if (apps)
    {
        for (int j = 0; apps[j]; j++)
        {
            for (int k = 0; k < 3; k++)
                free(apps[j][k]);
            free(apps[j]);
        }
        free(apps);
    }
out:
    if (rc != SQLITE_OK && NULL != schemas_table)
    {
        g_hash_table_destroy(schemas_table);
        schemas_table = NULL;
    }
    if (err_msg)
        sqlite3_free(err_msg);
    if (db)
        sqlite3_close(db);
}
/*读取数据库*/

// 在schemas_table查找指定schema的配置句柄KSettingsSchema
KSettingsSchema *kdk_conf2_schema_table_lookup(const char *id, const char *version)
{
    KSettingsSchema *result = NULL;

    AppData *app_data = NULL;
    GHashTable *version_hash = NULL;
    char **list = _split_string(id, '.');

    GHashTableIter iter;
    g_hash_table_iter_init(&iter, schemas_table);
    while (g_hash_table_iter_next(&iter, &app_data, &version_hash))
    {
        if (0 == strcmp(app_data->name, list[0]))
        {
            VersionData *version_data = NULL;
            GHashTable *schemas = NULL;
            version = version ? version : app_data->default_version;

            GHashTableIter iter_version;
            g_hash_table_iter_init(&iter_version, version_hash);
            while (g_hash_table_iter_next(&iter_version, &version_data, &schemas))
            {
                if (0 == strcmp(version_data->name, version))
                {
                    result = g_hash_table_lookup(schemas, id);
                    break;
                }
            }
        }
    }
    g_strfreev(list);
    return result;
}

void kdk_conf2_schema_destroy(KSettingsSchema *schema)
{
    schema_data_destroy(schema);
}

char *kdk_conf2_schema_get_id(KSettingsSchema *schema)
{
    return strdup(schema->id);
}

char *kdk_conf2_schema_get_version(KSettingsSchema *schema)
{
    return strdup(schema->version);
}

GHashTable *kdk_conf2_schema_get_key(KSettingsSchema *schema, const char *key)
{
    if (NULL == schema->values)
        return NULL;
    return (GHashTable *)g_hash_table_lookup(schema->values, key);
}

int kdk_conf2_schema_has_key(KSettingsSchema *schema)
{
    return schema->values ? TRUE : FALSE;
}

char **kdk_conf2_schema_list_keys(KSettingsSchema *schema)
{
    if (NULL == schema->values)
        return NULL;

    GList *keys = g_hash_table_get_keys(schema->values);
    guint len = g_list_length(keys);
    char **result = (char **)calloc(len + 1, sizeof(char *));
    if (NULL == result)
        return NULL;

    GList *iter = keys;
    for (guint i = 0; i < len; i++)
    {
        char *tmp = (char *)(iter->data);
        result[i] = (char *)calloc(strlen(tmp) + 1, sizeof(char));
        if (NULL == result[i])
        {
            // 无符号整形，-1为0xffffffff
            while (result[i] && i != 0xffffffff)
            {
                free(result[i--]);
            }
            free(result);
            break;
        }
        strcpy(result[i], tmp);
        iter = iter->next;
    }
    return result;
}

char **kdk_conf2_schema_list_children(KSettingsSchema *schema)
{
    if (NULL == schema->children)
        return NULL;

    char **result = (char **)calloc(schema->children->len + 1, sizeof(char *));
    if (NULL == result)
        return NULL;

    for (guint i = 0; i < schema->children->len; i++)
    {
        result[i] = (char *)calloc(strlen(g_array_index(schema->children, char *, i)) + 1, sizeof(char));
        if (NULL == result[i])
        {
            while (result[i] && i != 0xffffffff)
            {
                free(result[i--]);
            }
            free(result);
            break;
        }
        strcpy(result[i], g_array_index(schema->children, char *, i));
    }
    return result;
}

int kdk_conf2_schema_find_child(KSettingsSchema *schema, const char *name)
{
    if (NULL == schema->children)
        return FALSE;

    for (guint i = 0; i < schema->children->len; i++)
    {
        char *tmp = g_array_index(schema->children, char *, i);
        if (0 == g_strcmp0(name, tmp))
        {
            return TRUE;
        }
    }
    return FALSE;
}

#define GET_KEY_ATTR(x)                       \
    if (NULL == key)                          \
        return NULL;                          \
    char *tmp = g_hash_table_lookup(key, #x); \
    if (NULL == tmp)                          \
        return NULL;                          \
    char *result = strdup(tmp);               \
    return result;

char *kdk_conf2_schema_key_value_type(GHashTable *key)
{
    GET_KEY_ATTR(data_type)
}

char *kdk_conf2_schema_key_get_value(GHashTable *key)
{
    GET_KEY_ATTR(user_value)
}

char *kdk_conf2_schema_key_get_default_value(GHashTable *key)
{
    GET_KEY_ATTR(default_value)
}

char *kdk_conf2_schema_key_get_range(GHashTable *key)
{
    GET_KEY_ATTR(range)
}

int kdk_conf2_schema_key_range_check(GHashTable *key, const char *value)
{
    int result = FALSE;
    char *type = kdk_conf2_schema_key_value_type(key);
    char *range = kdk_conf2_schema_key_get_range(key);

    // 无取值范围则数值合法
    if (NULL == range)
    {
        result = TRUE;
        goto out;
    }

    const gchar *endptr = NULL;
    GError *error = NULL;
    if (0 == strcmp(type, "enum"))
    {
        GVariant *input_value = g_variant_parse(G_VARIANT_TYPE("a{si}"), range, NULL, &endptr, &error);
        if (NULL == input_value)
        {
            g_print("解析失败: %s\n", error->message);
            g_error_free(error);
            goto out;
        }

        // GVariant *tmp = g_variant_parse(G_VARIANT_TYPE("i"), value, NULL, &endptr, &error);
        // if (NULL == tmp)
        // {
        //     g_error_free(error);
        //     error = NULL;
        // }

        char *nick = NULL;
        gint nick_value = 0;
        GVariantIter *iter = NULL;
        g_variant_get(input_value, "a{si}", &iter);
        while (g_variant_iter_loop(iter, "{si}", &nick, &nick_value))
        {

            // if (NULL == tmp)
            // {
                if (0 == g_strcmp0(value, nick))
                    result = TRUE;
            // }
            // else
            // {
            //     if (atoi(value) == nick_value)
            //     {
            //         result = TRUE;
            //         g_variant_unref(tmp);
            //     }
            // }
        }
        g_variant_iter_free(iter);
        g_variant_unref(input_value);
    }
    else
    {
        GVariant *input_value = g_variant_parse(G_VARIANT_TYPE(type), value, NULL, &endptr, &error);
        if (NULL == input_value)
        {
            g_print("解析失败: %s\n", error->message);
            g_error_free(error);
            goto out;
        }
        char *p = range;
        while (*p != ',')
            p++;
        *p = '\0';
        GVariant *min_value = g_variant_parse(G_VARIANT_TYPE(type), range, NULL, &endptr, &error);
        if (NULL == min_value)
        {
            g_print("解析失败: %s\n", error->message);
            g_error_free(error);
            g_variant_unref(input_value);
            goto out;
        }
        GVariant *max_value = g_variant_parse(G_VARIANT_TYPE(type), p + 1, NULL, &endptr, &error);
        if (NULL == max_value)
        {
            g_print("解析失败: %s\n", error->message);
            g_error_free(error);
            g_variant_unref(input_value);
            g_variant_unref(min_value);
            goto out;
        }

        if ((g_variant_compare(min_value, input_value) <= 0) &&
            (g_variant_compare(max_value, input_value) >= 0))
            result = TRUE;
        g_variant_unref(input_value);
        g_variant_unref(min_value);
        g_variant_unref(max_value);
    }
out:
    g_free(type);
    g_free(range);
    return result;
}

char *kdk_conf2_schema_key_get_name(GHashTable *key)
{
    GET_KEY_ATTR(property)
}

char *kdk_conf2_schema_key_get_summary(GHashTable *key)
{
    GET_KEY_ATTR(summary)
}

char *kdk_conf2_schema_key_get_description(GHashTable *key)
{
    GET_KEY_ATTR(description)
}

char *kdk_conf2_schema_key_get_permission(GHashTable *key)
{
    GET_KEY_ATTR(permission);
}

void kdk_conf2_schema_update_schemas_table()
{
    //    if (schemas_table)
    //    {
    //        g_hash_table_destroy(schemas_table);
    //        schemas_table = NULL;
    //    }
    _get_schemas();
}

// 重新读取某个schema的配置
KSettingsSchema *kdk_conf2_schema_reload(const char *id, const char *version)
{
    KSettingsSchema *result = NULL;

    char **list = _split_string(id, '.');
    AppData *app_data = NULL;
    GHashTable *version_hash = NULL;

    GHashTableIter iter;
    g_hash_table_iter_init(&iter, schemas_table);
    while (g_hash_table_iter_next(&iter, &app_data, &version_hash))
    {
        if (0 == strcmp(app_data->name, list[0]))
        {
            VersionData *version_data = NULL;
            GHashTable *schemas = NULL;

            GHashTableIter iter_version;
            g_hash_table_iter_init(&iter_version, version_hash);
            while (g_hash_table_iter_next(&iter_version, &version_data, &schemas))
            {
                if (0 == strcmp(version_data->name, version))
                {
                    // 用新读取的配置替换掉schemas_table中的老配置
                    KSettingsSchema *schema = _get_schema(id, version);
                    result = g_hash_table_lookup(schemas, id);

                    GHashTable *tmp = result->values;
                    result->values = schema->values;
                    schema->values = tmp;

                    GArray *tmp_array = result->children;
                    result->children = schema->children;
                    schema->children = tmp_array;
                    schema_data_destroy(schema);
                    break;
                }
            }
        }
    }
    g_strfreev(list);
    return result;
}
