/*
 * 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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
#include <gio/gio.h>
#include <pthread.h>
#include <stdarg.h>
#include <syslog.h>
#include <sys/stat.h>

#include "libkysettings.h"
#include "ksettingsschema.h"

static GMainLoop *dbus_signal_loop = NULL; // glib主循环，用于处理dbus消息事件，主循环不开启无法接收dbus信号
static guint ref_count = 0;                // 记录dbus_signal_loop的引用计数
static volatile int mutex = 0;
/*
 *句柄继承GObject
 *继承GObject，以使用glib框架的信号机制
 */
typedef struct _KSettingsPrivate
{
    GMainContext *main_context;
    KSettingsSchema *schema;
    GDBusConnection *conn;
    guint sub_id[2];
} KSettingsPrivate;

struct _KSettings
{
    GObject parent_instance;
    KSettingsPrivate *priv;
};

typedef struct _KSettingsClass
{
    GObjectClass parent_class;

    /* Signals */
    void (*writable_changed)(KSettings *settings,
                             const gchar *key);
    void (*changed)(KSettings *settings,
                    const gchar *key);
    void (*reload)(KSettings *settings);
    gboolean (*writable_change_event)(KSettings *settings,
                                      GQuark key);
    gboolean (*change_event)(KSettings *settings,
                             const GQuark *keys,
                             gint n_keys);

    gpointer padding[20];
} KSettingsClass;

enum
{
    SIGNAL_CHANGED,
    SIGNAL_RELOAD,
    N_SIGNALS
};

static guint k_settings_signals[N_SIGNALS];

G_DEFINE_TYPE_WITH_PRIVATE(KSettings, k_settings, G_TYPE_OBJECT)

#define K_TYPE_SETTINGS (k_settings_get_type())
#define K_SETTINGS(inst) (G_TYPE_CHECK_INSTANCE_CAST((inst), \
                                                     K_TYPE_SETTINGS, KSettings))
#define K_SETTINGS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST((class), \
                                                         K_TYPE_SETTINGS, KSettingsClass))
#define K_IS_SETTINGS(inst) (G_TYPE_CHECK_INSTANCE_TYPE((inst), K_TYPE_SETTINGS))
#define K_IS_SETTINGS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE((class), K_TYPE_SETTINGS))
#define K_SETTINGS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS((inst), \
                                                              K_TYPE_SETTINGS, KSettingsClass))

static void k_settings_finalize(GObject *object)
{
    KSettings *ksettings = K_SETTINGS(object);

    g_main_context_unref(ksettings->priv->main_context);
    //    kdk_conf2_schema_destroy(ksettings->priv->schema);
    if (ksettings->priv->conn)
    {
        g_dbus_connection_signal_unsubscribe(ksettings->priv->conn, ksettings->priv->sub_id[SIGNAL_CHANGED]);
        g_dbus_connection_signal_unsubscribe(ksettings->priv->conn, ksettings->priv->sub_id[SIGNAL_RELOAD]);
        g_object_unref(ksettings->priv->conn);
    }

    G_OBJECT_CLASS(k_settings_parent_class)->finalize(object);
}

static void k_settings_init(KSettings *settings)
{
    settings->priv = k_settings_get_instance_private(settings);
    settings->priv->main_context = g_main_context_ref_thread_default();
}

static void k_settings_class_init(KSettingsClass *class)
{
    GObjectClass *object_class = G_OBJECT_CLASS(class);
    object_class->finalize = k_settings_finalize;

    // 创建一个新的信号
    k_settings_signals[SIGNAL_CHANGED] =
        g_signal_new("changed",
                     K_TYPE_SETTINGS,
                     G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
                     G_STRUCT_OFFSET(KSettingsClass, changed),
                     NULL,
                     NULL,
                     NULL,
                     G_TYPE_NONE,
                     1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);

    k_settings_signals[SIGNAL_RELOAD] =
        g_signal_new("reload",
                     K_TYPE_SETTINGS,
                     G_SIGNAL_RUN_LAST,
                     G_STRUCT_OFFSET(KSettingsClass, reload),
                     NULL,
                     NULL,
                     NULL,
                     G_TYPE_NONE,
                     0);
}

static int verify_file(char *file_name)
{
    return 1;
}

/**
 * @brief _g_main_loop_thread 开启main_loop获取消息循环
 * @param data
 * @return
 */
static void *_g_main_loop_thread(void *data)
{
    // 启动主循环
    g_main_loop_run(dbus_signal_loop);
    return NULL;
}

/**
 * @brief _on_key_changed DBus服务中key_changed信号响应函数
 * @param connection
 * @param sender_name
 * @param object_path
 * @param interface_name
 * @param signal_name
 * @param parameters
 * @param user_data
 */
static void _on_key_changed(GDBusConnection *connection,
                            const gchar *sender_name,
                            const gchar *object_path,
                            const gchar *interface_name,
                            const gchar *signal_name,
                            GVariant *parameters,
                            gpointer user_data)
{
    KSettings *ksettings = (KSettings *)user_data;
    char *id, *version, *key, *user;
    g_variant_get(parameters, "(ssss)", &id, &version, &key, &user);
    char *schema_id = kdk_conf2_schema_get_id(ksettings->priv->schema);
    if ((0 == strcmp(schema_id, id)) && (0 == strcmp(g_get_user_name(), user)))
    {
        ksettings->priv->schema = kdk_conf2_schema_reload(id, version);
        g_signal_emit(ksettings, k_settings_signals[SIGNAL_CHANGED], g_quark_from_string(key), key);
    }
    g_free(schema_id);
    mutex = 0;
}

/**
 * @brief _on_updated   DBus服务中update信号的响应函数
 * @param connection
 * @param sender_name
 * @param object_path
 * @param interface_name
 * @param signal_name
 * @param parameters
 * @param user_data
 */
static void _on_updated(GDBusConnection *connection,
                        const gchar *sender_name,
                        const gchar *object_path,
                        const gchar *interface_name,
                        const gchar *signal_name,
                        GVariant *parameters,
                        gpointer user_data)
{
    if (0 == mutex)
    {
        mutex = 1;
        kdk_conf2_schema_update_schemas_table();
        mutex = 0;
    }
    else
        usleep(1);
    KSettings *ksettings = (KSettings *)user_data;
    g_signal_emit(ksettings, k_settings_signals[SIGNAL_RELOAD], 0);
}

/**
 * @brief _type_check   检查vaule的值是否符合key的数据类型标签
 * @param ksettings 句柄
 * @param key   键名
 * @param value 目标值的字符串。
 * @return 布尔类型 1：True 0：False
 */
static int _type_check(KSettings *ksettings, const char *key, const char *value)
{
    const gchar *endptr = NULL;
    GError *error = NULL;
    GHashTable *key_hash = kdk_conf2_schema_get_key(ksettings->priv->schema, key);
    if (NULL == key_hash)
        return FALSE;

    char *type = kdk_conf2_schema_key_value_type(key_hash);
    if ((0 != strcmp(type, "enum")) && (0 != strcmp(type, "s")))
    {
        GVariant *variant = g_variant_parse(G_VARIANT_TYPE(type), value, NULL, &endptr, &error);
        if (NULL == variant)
        {
            printf("parser error : %s\n", error->message);
            g_error_free(error);
            g_free(type);
            return FALSE;
        }
        g_variant_unref(variant);
    }
    g_free(type);
    return TRUE;
}

/**
 * @brief _call_method 调用dbus接口
 * @param connection   dbus链接
 * @param method    接口名 set/reset/reset_recursively/reload
 * @param id        配置id，可为NULL，根据method传参。
 * @param version   配置版本，可为NULL，根据method传参。
 * @param key       键名，可为NULL，根据method传参。
 * @param value     键值，可为NULL，根据method传参。
 * @return  boolean类型 成功返回1 失败返回0
 */
static int _call_method(GDBusConnection *connection, const char *method, const char *id, const char *version, const char *key, const char *value)
{
    GError *error = NULL;
    GDBusProxy *proxy;
    GVariant *result;
    int response = FALSE;

    // 获取 DBus 会话总线
    if (NULL == connection)
    {
        connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
        if (error != NULL)
        {
            g_printerr("Connection Error: %s\n", error->message);
            g_error_free(error);
            return response;
        }
    }

    // 创建代理对象
    proxy = g_dbus_proxy_new_sync(connection,
                                  G_DBUS_PROXY_FLAGS_NONE,
                                  NULL,
                                  "com.kylin.kysdk.conf2",
                                  "/com/kylin/kysdk/conf2",
                                  "com.kylin.kysdk.conf2",
                                  NULL,
                                  &error);
    if (error != NULL)
    {
        g_printerr("Proxy Error: %s\n", error->message);
        g_error_free(error);
        return response;
    }

    // 调用代理对象的方法
    GVariant *variant = NULL;
    if (0 == strcmp(method, "set"))
        variant = g_variant_new("(sssss)", getlogin(), id, version, key, value);
    if (0 == strcmp(method, "reset"))
        variant = g_variant_new("(ssss)", getlogin(), id, version, key);
    if (0 == strcmp(method, "reset_recursively"))
        variant = g_variant_new("(sss)", getlogin(), id, version);

    result = g_dbus_proxy_call_sync(proxy,
                                    method,
                                    variant,
                                    G_DBUS_CALL_FLAGS_NONE,
                                    -1,
                                    NULL,
                                    &error);
    if (error != NULL)
    {
        g_printerr("Method Call Error: %s\n", error->message);
        g_error_free(error);
        return response;
    }

    // 处理方法调用的返回值
    g_variant_get(result, "(i)", &response);

    // 释放资源
    g_variant_unref(result);
    g_object_unref(proxy);
    return response;
}

KSettings *kdk_conf2_new(const char *id, const char *version)
{
    if (NULL == id)
    {
        openlog("kysdk-conf2", LOG_PID, LOG_USER);
        syslog(LOG_INFO, "id is nullptr");
        closelog();
        return NULL;
    }

    if (NULL == schemas_table)
        kdk_conf2_schema_update_schemas_table();

    if (NULL == schemas_table)
    {
        openlog("kysdk-conf2", LOG_PID, LOG_USER);
        syslog(LOG_INFO, "load user.db failed");
        closelog();
        return NULL;
    }

    KSettingsSchema *schema = kdk_conf2_schema_table_lookup(id, version);
    if (NULL == schema)
    {
        openlog("kysdk-conf2", LOG_PID, LOG_USER);
        syslog(LOG_INFO, "invalied schema id");
        closelog();
        return NULL;
    }

    KSettings *ksettings = g_object_new(K_TYPE_SETTINGS, NULL);
    if (!K_IS_SETTINGS(ksettings))
    {
        openlog("kysdk-conf2", LOG_PID, LOG_USER);
        syslog(LOG_INFO, "init KSettings failed");
        closelog();
        return NULL;
    }

    ksettings->priv->schema = schema;

    // 创建dbus服务的链接，用于调用接口及接收信号
    GError *error = NULL;
    ksettings->priv->conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
    if (error != NULL)
    {
        openlog("kysdk-conf2", LOG_PID, LOG_USER);
        syslog(LOG_INFO, "Connection Error: %s\n", error->message);
        closelog();
        g_object_unref(ksettings);
        ksettings = NULL;
        g_error_free(error);
        return NULL;
    }

    // 监听key_changed信号
    ksettings->priv->sub_id[0] = g_dbus_connection_signal_subscribe(ksettings->priv->conn,
                                                                    "com.kylin.kysdk.conf2",  // 消息来源 (NULL 表示任意)
                                                                    "com.kylin.kysdk.conf2",  // 接口名称
                                                                    "key_changed",            // 信号名称
                                                                    "/com/kylin/kysdk/conf2", // 对象路径 (NULL 表示任意)
                                                                    NULL,
                                                                    G_DBUS_SIGNAL_FLAGS_NONE,
                                                                    _on_key_changed,
                                                                    ksettings,
                                                                    NULL);
    // 监听重载信号
    ksettings->priv->sub_id[1] = g_dbus_connection_signal_subscribe(ksettings->priv->conn,
                                                                    "com.kylin.kysdk.conf2",  // 消息来源 (NULL 表示任意)
                                                                    "com.kylin.kysdk.conf2",  // 接口名称
                                                                    "updated",                // 信号名称
                                                                    "/com/kylin/kysdk/conf2", // 对象路径 (NULL 表示任意)
                                                                    NULL,
                                                                    G_DBUS_SIGNAL_FLAGS_NONE,
                                                                    _on_updated,
                                                                    ksettings,
                                                                    NULL);

    // 创建glib的事件处理循环，单独起线程。
    // 如果循环已经启动，增加dbus_signal_loop的引用计数
    if (NULL == dbus_signal_loop)
    {
        dbus_signal_loop = g_main_loop_new(NULL, FALSE);
        pthread_t thread;
        pthread_create(&thread, NULL, _g_main_loop_thread, NULL);
    }
    else
        g_main_loop_ref(dbus_signal_loop);
    ref_count++;
    return ksettings;
}

void kdk_conf2_ksettings_destroy(KSettings *ksettings)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    if (!K_IS_SETTINGS(ksettings))
        return;

    g_object_unref(ksettings);

    // 减少dbus_signal_loop的引用计数
    // 如果归零则停止事件循环
    g_main_loop_unref(dbus_signal_loop);
    if (0 == (--ref_count))
    {
        g_main_loop_quit(dbus_signal_loop);
        dbus_signal_loop = NULL;
    }
}

char *kdk_conf2_get_id(KSettings *ksettings)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    if (!K_IS_SETTINGS(ksettings))
        return NULL;
    return kdk_conf2_schema_get_id(ksettings->priv->schema);
}

char *kdk_conf2_get_version(KSettings *ksettings)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    if (!K_IS_SETTINGS(ksettings))
        return NULL;
    return strdup(kdk_conf2_schema_get_version(ksettings->priv->schema));
}

KSettings *kdk_conf2_get_child(KSettings *ksettings, const char *name)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != name);

    if (!K_IS_SETTINGS(ksettings) || NULL == name)
        return NULL;

    if (!kdk_conf2_schema_find_child(ksettings->priv->schema, name))
        return NULL;
    char child_id[PATH_MAX];
    char *id = kdk_conf2_schema_get_id(ksettings->priv->schema);
    sprintf(child_id, "%s.%s", id, name);
    g_free(id);
    KSettings *result = kdk_conf2_new(child_id, kdk_conf2_schema_get_version(ksettings->priv->schema));
    return result;
}

char **kdk_conf2_list_children(KSettings *ksettings)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    if (!K_IS_SETTINGS(ksettings))
        return NULL;

    return kdk_conf2_schema_list_children(ksettings->priv->schema);
}

char **kdk_conf2_list_keys(KSettings *ksettings)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    if (!K_IS_SETTINGS(ksettings))
        return NULL;

    return kdk_conf2_schema_list_keys(ksettings->priv->schema);
}

char *kdk_conf2_get_range(KSettings *ksettings, const char *key)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return NULL;

    GHashTable *key_hash = kdk_conf2_schema_get_key(ksettings->priv->schema, key);
    return kdk_conf2_schema_key_get_range(key_hash);
}

int kdk_conf2_range_check(KSettings *ksettings, const char *key, const char *value)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return FALSE;

    GHashTable *key_hash = kdk_conf2_schema_get_key(ksettings->priv->schema, key);
    if (NULL == key_hash)
        return FALSE;
    return kdk_conf2_schema_key_range_check(key_hash, value);
}

int kdk_conf2_set_value(KSettings *ksettings, const char *key, const char *value)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return FALSE;

    if(0 == kdk_conf2_is_writable(ksettings, key))
        return FALSE;

    if (!_type_check(ksettings, key, value))
    {
        return FALSE;
    }
    if (!kdk_conf2_range_check(ksettings, key, value))
    {
        return FALSE;
    }

    int result = FALSE;
    GHashTable *key_hash = kdk_conf2_schema_get_key(ksettings->priv->schema, key);
    // key不存在
    if (NULL == key)
        return FALSE;
    char *curr_value = kdk_conf2_schema_key_get_value(key_hash);

    char *id = kdk_conf2_schema_get_id(ksettings->priv->schema);
    char *version = kdk_conf2_schema_get_version(ksettings->priv->schema);
    if (NULL == curr_value || 0 != strcmp(curr_value, value))
        result = _call_method(ksettings->priv->conn, "set",
                              id,
                              version,
                              key, value);
    else
        result = TRUE;
    g_free(id);
    g_free(version);
    g_free(curr_value);
    return result;
}

char *kdk_conf2_get_value(KSettings *ksettings, const char *key)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return NULL;

    GHashTable *key_hash = kdk_conf2_schema_get_key(ksettings->priv->schema, key);
    if (NULL == key_hash)
        return NULL;

    char *value = kdk_conf2_schema_key_get_value(key_hash);
    if ((0 == kdk_conf2_is_writable(ksettings, key)) || NULL == value)
    {
        value = kdk_conf2_schema_key_get_default_value(key_hash);
    }
    return value;
}

char *kdk_conf2_get_default_value(KSettings *ksettings, const char *key)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return NULL;

    GHashTable *node = kdk_conf2_schema_get_key(ksettings->priv->schema, key);
    return node ? kdk_conf2_schema_key_get_default_value(node) : NULL;
}

int kdk_conf2_set_boolean(KSettings *ksettings, const char *key, int value)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return FALSE;

    GVariant *variant = g_variant_new_boolean(value);
    if (NULL == variant)
        return FALSE;

    int result = kdk_conf2_set_value(ksettings, key, g_variant_print(variant, FALSE));
    g_variant_unref(variant);
    return result;
}

int kdk_conf2_get_boolean(KSettings *ksettings, const char *key)
{
    int result = FALSE;

    char *value = kdk_conf2_get_value(ksettings, key);
    if (NULL != value)
    {
        result = (0 == strcmp(value, "true")) ? 1 : 0;
        g_free(value);
    }
    return result;
}

int kdk_conf2_get_default_boolean(KSettings *ksettings, const char *key)
{
    int result = FALSE;

    char *value = kdk_conf2_get_default_value(ksettings, key);
    if (NULL != value)
    {
        result = (0 == strcmp(value, "true")) ? 1 : 0;
        g_free(value);
    }
    return result;
}

int kdk_conf2_set_double(KSettings *ksettings, const char *key, double value)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return 0;

    GVariant *variant = g_variant_new_double(value);
    int result = kdk_conf2_set_value(ksettings, key, g_variant_print(variant, FALSE));
    g_variant_unref(variant);
    return result;
}

double kdk_conf2_get_double(KSettings *ksettings, const char *key)
{
    double result = 0.0;
    char *value = kdk_conf2_get_value(ksettings, key);
    if (NULL != value)
    {
        sscanf(value, "%lf", &result);
        g_free(value);
    }
    return result;
}

double kdk_conf2_get_default_double(KSettings *ksettings, const char *key)
{
    double result = 0.0;
    char *value = kdk_conf2_get_default_value(ksettings, key);
    if (NULL != value)
    {
        sscanf(value, "%lf", &result);
        g_free(value);
    }
    return result;
}

int kdk_conf2_set_enum(KSettings *ksettings, const char *key, int value)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return 0;

    int result = 0;
    char *range = kdk_conf2_get_range(ksettings, key);
    if (NULL == range)
        goto out;

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

    GVariantIter *iter = NULL;
    g_variant_get(variant, "a{si}", &iter);

    char *nick = NULL;
    gint32 tmp = 0;
    while (g_variant_iter_loop(iter, "{si}", &nick, &tmp))
    {
        if (tmp == value)
        {
            char set_value[1024];
            sprintf(set_value, "%d", value);
            result = kdk_conf2_set_value(ksettings, key, set_value);
            break;
        }
    }
    g_variant_iter_free(iter);
    g_variant_unref(variant);
out:
    g_free(range);
    return result;
}

int kdk_conf2_get_enum(KSettings *ksettings, const char *key)
{
    int result = -1;
    char *value = kdk_conf2_get_value(ksettings, key);
    if (NULL == value)
        return result;
    char *range = kdk_conf2_get_range(ksettings, key);
    if (NULL == range)
        return result;

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

    char *nick = NULL;
    gint32 tmp = 0;
    while (g_variant_iter_loop(iter, "{si}", &nick, &tmp))
    {
        if (0 == g_strcmp0(nick, value))
        {
            result = tmp;
            break;
        }
    }
    g_variant_iter_free(iter);
    g_variant_unref(variant);

out:
    g_free(value);
    g_free(range);
    return result;
}

int kdk_conf2_get_default_enum(KSettings *ksettings, const char *key)
{
    int result = 0;
    char *value = kdk_conf2_get_default_value(ksettings, key);
    if (NULL == value)
        return result;
    char *range = kdk_conf2_get_range(ksettings, key);
    if (NULL == range)
        return result;

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

    char *nick = NULL;
    gint32 tmp = 0;
    while (g_variant_iter_loop(iter, "{si}", &nick, &tmp))
    {
        if (0 == g_strcmp0(nick, value))
        {
            result = tmp;
            break;
        }
    }
    g_variant_iter_free(iter);
    g_variant_unref(variant);

out:
    g_free(value);
    g_free(range);
    return result;
}

int kdk_conf2_set_int(KSettings *ksettings, const char *key, int value)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return 0;

    GVariant *variant = g_variant_new_int32(value);
    int result = kdk_conf2_set_value(ksettings, key, g_variant_print(variant, FALSE));
    g_variant_unref(variant);

    return result;
}

int kdk_conf2_get_int(KSettings *ksettings, const char *key)
{
    int result = 0;
    char *value = kdk_conf2_get_value(ksettings, key);
    if (NULL != value)
    {
        sscanf(value, "%d", &result);
        g_free(value);
    }
    return result;
}

int kdk_conf2_get_default_int(KSettings *ksettings, const char *key)
{
    int result = 0;
    char *value = kdk_conf2_get_default_value(ksettings, key);
    if (NULL != value)
    {
        sscanf(value, "%d", &result);
        g_free(value);
    }
    return result;
}

int kdk_conf2_set_int64(KSettings *ksettings, const char *key, long value)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return 0;

    GVariant *variant = g_variant_new_int64(value);
    int result = kdk_conf2_set_value(ksettings, key, g_variant_print(variant, FALSE));
    g_variant_unref(variant);

    return result;
}

long kdk_conf2_get_int64(KSettings *ksettings, const char *key)
{
    long result = 0;
    char *value = kdk_conf2_get_value(ksettings, key);
    if (NULL != value)
    {
        sscanf(value, "%ld", &result);
        g_free(value);
    }
    return result;
}

long kdk_conf2_get_default_int64(KSettings *ksettings,
                                 const char *key)
{
    long result = 0;
    char *value = kdk_conf2_get_default_value(ksettings, key);
    if (NULL != value)
    {
        sscanf(value, "%ld", &result);
        g_free(value);
    }
    return result;
}

int kdk_conf2_set_uint(KSettings *ksettings, const char *key, unsigned int value)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return 0;

    GVariant *variant = g_variant_new_uint32(value);
    int result = kdk_conf2_set_value(ksettings, key, g_variant_print(variant, FALSE));
    g_variant_unref(variant);

    return result;
}

unsigned int kdk_conf2_get_uint(KSettings *ksettings, const char *key)
{
    unsigned int result = 0;
    char *value = kdk_conf2_get_value(ksettings, key);
    if (NULL != value)
    {
        sscanf(value, "%u", &result);
        g_free(value);
    }
    return result;
}

unsigned int kdk_conf2_get_default_uint(KSettings *ksettings, const char *key)
{
    unsigned int result = 0;
    char *value = kdk_conf2_get_default_value(ksettings, key);
    if (NULL != value)
    {
        sscanf(value, "%u", &result);
        g_free(value);
    }
    return result;
}

int kdk_conf2_set_uint64(KSettings *ksettings, const char *key, unsigned long value)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return 0;

    GVariant *variant = g_variant_new_uint64(value);
    int result = kdk_conf2_set_value(ksettings, key, g_variant_print(variant, FALSE));
    g_variant_unref(variant);

    return result;
}

unsigned long kdk_conf2_get_uint64(KSettings *ksettings, const char *key)
{
    unsigned long result = 0;
    char *value = kdk_conf2_get_value(ksettings, key);
    if (NULL != value)
    {
        sscanf(value, "%lu", &result);
        g_free(value);
    }
    return result;
}

unsigned long kdk_conf2_get_default_uint64(KSettings *ksettings, const char *key)
{
    unsigned long result = 0;
    char *value = kdk_conf2_get_default_value(ksettings, key);
    if (NULL != value)
    {
        sscanf(value, "%lu", &result);
        g_free(value);
    }
    return result;
}

int kdk_conf2_set_string(KSettings *ksettings, const char *key, const char *value)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return 0;

    GVariant *variant = g_variant_new_string(value);
    int result = kdk_conf2_set_value(ksettings, key, g_variant_print(variant, FALSE));
    g_variant_unref(variant);

    return result;
}

char *kdk_conf2_get_string(KSettings *ksettings, const char *key)
{
    return kdk_conf2_get_value(ksettings, key);
}

char *kdk_conf2_get_default_string(KSettings *ksettings, const char *key)
{
    return kdk_conf2_get_default_value(ksettings, key);
}

int kdk_conf2_set_strv(KSettings *ksettings, const char *key, const char *const *value)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return 0;

    GVariant *variant;
    if (value != NULL)
        variant = g_variant_new_strv(value, -1);
    else
        variant = g_variant_new_strv(NULL, 0);
    int result = kdk_conf2_set_value(ksettings, key, g_variant_print(variant, FALSE));
    g_variant_unref(variant);

    return result;
}

char **kdk_conf2_get_strv(KSettings *ksettings, const char *key)
{
    char **result = NULL;

    char *value = kdk_conf2_get_value(ksettings, key);
    if (NULL == value)
        return result;

    const gchar *endptr = NULL;
    GError *error = NULL;
    GVariant *variant = g_variant_parse(G_VARIANT_TYPE("as"), value, NULL, &endptr, &error);
    if (NULL == variant)
    {
        g_print("解析失败: %s\n", error->message);
        g_free(value);
        g_error_free(error);
        return NULL;
    }
    result = g_variant_dup_strv(variant, NULL);
    g_free(value);
    g_variant_unref(variant);

    return result;
}

char **kdk_conf2_get_default_strv(KSettings *ksettings, const char *key)
{
    char **result = NULL;

    char *value = kdk_conf2_get_default_value(ksettings, key);
    if (NULL == value)
        return result;

    const gchar *endptr = NULL;
    GError *error = NULL;
    GVariant *variant = g_variant_parse(G_VARIANT_TYPE("as"), value, NULL, &endptr, &error);
    if (NULL == variant)
    {
        g_print("解析失败: %s\n", error->message);
        g_free(value);
        g_error_free(error);
        return NULL;
    }
    result = g_variant_dup_strv(variant, NULL);
    g_free(value);
    g_variant_unref(variant);

    return result;
}

char *kdk_conf2_get_descrition(KSettings *ksettings, const char *key)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return NULL;

    GHashTable *key_hash = kdk_conf2_schema_get_key(ksettings->priv->schema, key);
    return kdk_conf2_schema_key_get_description(key_hash);
}

char *kdk_conf2_get_summary(KSettings *ksettings, const char *key)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return NULL;

    GHashTable *key_hash = kdk_conf2_schema_get_key(ksettings->priv->schema, key);
    return kdk_conf2_schema_key_get_summary(key_hash);
}

char *kdk_conf2_get_type(KSettings *ksettings, const char *key)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return NULL;

    GHashTable *key_hash = kdk_conf2_schema_get_key(ksettings->priv->schema, key);
    return kdk_conf2_schema_key_value_type(key_hash);
}

void kdk_conf2_reset(KSettings *ksettings, const char *key)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return;

    char *id = kdk_conf2_schema_get_id(ksettings->priv->schema);
    char *version = kdk_conf2_schema_get_version(ksettings->priv->schema);
    _call_method(ksettings->priv->conn, "reset",
                 id,
                 version,
                 key, NULL);
    g_free(id);
    g_free(version);
}

int kdk_conf2_is_writable(KSettings *ksettings, const char *key)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    // g_assert(NULL != key);
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return FALSE;

    GHashTable *key_hash = kdk_conf2_schema_get_key(ksettings->priv->schema, key);
    char *permission = kdk_conf2_schema_key_get_permission(key_hash);
    if ((NULL == permission) || (0 == strcmp(permission, "public")))
        return TRUE;
    return FALSE;
}

int kdk_conf2_has_key(KSettings *ksettings, const char *key)
{
    if (!K_IS_SETTINGS(ksettings) || NULL == key)
        return FALSE;
        
    GHashTable *key_hash = kdk_conf2_schema_get_key(ksettings->priv->schema, key);
    if(NULL == key_hash)
        return 0;
    else
        return 1;
}

unsigned long kdk_conf2_connect_signal(KSettings *ksettings, const char *signal_name, KCallBack handler, void *user_data)
{
    // g_assert(K_IS_SETTINGS(ksettings));
    if (!K_IS_SETTINGS(ksettings))
        return 0;

    return g_signal_connect(ksettings, signal_name, G_CALLBACK(handler), user_data);
}

void kdk_conf2_reload()
{
    GError *error = NULL;
    GDBusConnection *connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
    if (error != NULL)
    {
        g_printerr("Connection Error: %s\n", error->message);
        g_error_free(error);
        return;
    }
    _call_method(connection, "reload", NULL, NULL, NULL, NULL);
}

char **kdk_conf2_list_schemas(const char *app, const char *version)
{
    if (NULL == app)
        return NULL;

    char *default_version = NULL;
    if (NULL == schemas_table)
        kdk_conf2_schema_update_schemas_table();

    GHashTableIter iter;
    AppData *app_data = NULL;
    GHashTable *app_hash = NULL;
    g_hash_table_iter_init(&iter, schemas_table);
    while (g_hash_table_iter_next(&iter, (gpointer)&app_data, (gpointer)&app_hash))
    {
        if (0 == strcmp(app_data->name, app))
        {
            default_version = app_data->default_version;
            break;
        }
        app_hash = NULL;
    }
    if (NULL == app_hash)
        return NULL;

    VersionData version_data;
    version_data.name = version ? version : default_version;
    GHashTable *version_hash = g_hash_table_lookup(app_hash, &version_data);
    if (NULL == version_hash)
        return NULL;

    GList *schemas = g_hash_table_get_keys(version_hash);
    guint len = g_list_length(schemas);

    GList *tmp = schemas;
    char **result = calloc(len + 1, sizeof(char *));
    for (guint i = 0; i < len; i++)
    {
        result[i] = strdup(tmp->data);
        tmp = tmp->next;
    }

    return result;
}

int kdk_conf2_save_user_configure(const char *path)
{
    if(0 != access(path, F_OK))
    {
        if(!verify_file(path))
            return FALSE;
        if (mkdir(path, 0777) < 0)
            return FALSE;
    }

    GDBusConnection *connection;
    GError *error = NULL;
    GDBusProxy *proxy;
    GVariant *result;
    int response = FALSE;

    // 获取 DBus 会话总线
    connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
    if (NULL == connection)
    {
        connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
        if (error != NULL)
        {
            g_printerr("Connection Error: %s\n", error->message);
            g_error_free(error);
            return response;
        }
    }

    // 创建代理对象
    proxy = g_dbus_proxy_new_sync(connection,
                                  G_DBUS_PROXY_FLAGS_NONE,
                                  NULL,
                                  "com.kylin.kysdk.conf2",
                                  "/com/kylin/kysdk/conf2",
                                  "com.kylin.kysdk.conf2",
                                  NULL,
                                  &error);
    if (error != NULL)
    {
        g_printerr("Proxy Error: %s\n", error->message);
        g_error_free(error);
        return response;
    }
    GVariant *variant = g_variant_new("(ss)", getlogin(), path);
    result = g_dbus_proxy_call_sync(proxy,
                                    "save",
                                    variant,
                                    G_DBUS_CALL_FLAGS_NONE,
                                    -1,
                                    NULL,
                                    &error);
    if (error != NULL)
    {
        g_printerr("Method Call Error: %s\n", error->message);
        g_error_free(error);
        return response;
    }

    // 处理方法调用的返回值
    g_variant_get(result, "(b)", &response);

    // 释放资源
    g_variant_unref(result);
    g_object_unref(proxy);
    return response;
}
