/* Copyright (c) KylinSoft  Co., Ltd. [2023].All rights reserved.
    [ostree-maintain-daemon] licensed under the [Your LICENSE].
    Author: [ZhangTingting]
    Create: [2024]
*/

#include "main.h"

/**shutdown*/
gboolean mm_shutdown_action = FALSE;
gboolean mm_shutdown_sync = FALSE;
/*********************************************gdbus*************************************************/
GDBusConnection *global_connection;
static GDBusNodeInfo *introspection_data = NULL;

/* Introspection data for the service we are exporting */
static const gchar introspection_xml[] =
    "<node>"
    "  <interface name='com.kylin.MaintainMode.interface'>"
    "    <method name='OpenMaintainMode'>"
    "      <annotation name='com.kylin.MaintainMode.Annotation' value='OnMethod'/>"
    "      <arg type='b' name='result' direction='out'/>"
    "    </method>"
    "    <method name='CloseMaintainMode'>"
    "      <annotation name='com.kylin.MaintainMode.Annotation' value='OnArg'/>"
    "      <arg type='s' name='savedatamode' direction='in'/>"
    "      <arg type='b' name='result' direction='out'/>"
    "    </method>"
    "    <method name='GetBootMode'>"
    "      <annotation name='com.kylin.MaintainMode.Annotation' value='OnArg'/>"
    "      <arg type='s' name='bootmode' direction='out'/>"
    "    </method>"
    "    <signal name='BootModeChanged'>"
    "      <annotation name='com.kylin.MaintainMode.Annotation' value='Onsignal'/>"
    "      <arg type='u' name='bootmode'>"
    "        <annotation name='com.kylin.MaintainMode.Annotation' value='OnArg_NonFirst'/>"
    "      </arg>"
    "    </signal>"
    "  </interface>"
    "</node>";

static void
handle_method_call(GDBusConnection *connection,
                   const gchar *sender,
                   const gchar *object_path,
                   const gchar *interface_name,
                   const gchar *method_name,
                   GVariant *parameters,
                   GDBusMethodInvocation *invocation,
                   gpointer user_data)
{
    if (g_strcmp0(method_name, "CloseMaintainMode") == 0) {
        char *save_mode;
        gboolean result;
        g_variant_get(parameters, "(s)", &save_mode);
        result = mm_exit_maintain_mode(save_mode);
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", result));

    } else if (g_strcmp0(method_name, "OpenMaintainMode") == 0) {
        gboolean result;
        result = mm_enter_maintain_mode();
        g_print("come open %s.\n", result ? "true" : "false");
        g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", result));

    } else if (g_strcmp0(method_name, "GetBootMode") == 0) {
        gchar *boot_mode = mm_get_boot_mode();
        g_dbus_method_invocation_return_value(invocation,
                                              g_variant_new("(s)", boot_mode));
        // g_free (boot_mode);
    }
}

void mm_emit_mode_changed_signal(guint mode)
{
    GError *error = NULL;
    g_dbus_connection_emit_signal(global_connection,
                                  NULL,
                                  "/com/kylin/MaintainMode",
                                  "com.kylin.MaintainMode.interface",
                                  "BootModeChanged",
                                  g_variant_new("(u)", mode),
                                  &error);
    g_info("mm: Successfully sent a signal %d on D-Bus", mode);
    g_assert_no_error(error);
}
/* for now */
static const GDBusInterfaceVTable interface_vtable =
    {
        handle_method_call};
/* ---------------------------------------------------------------------------------------------------- */
static void
on_bus_acquired(GDBusConnection *connection,
                const gchar *name,
                gpointer user_data)
{
    GError *error = NULL;
    guint registration_id;

    registration_id = g_dbus_connection_register_object(connection,
                                                        "/com/kylin/MaintainMode/Object",
                                                        introspection_data->interfaces[0],
                                                        &interface_vtable,
                                                        NULL,  /* user_data */
                                                        NULL,  /* user_data_free_func */
                                                        NULL); /* GError** */
    if (registration_id <= 0) {
        g_warning("mm: Failed to register object: %s", error->message);
        // 根据需要处理错误，例如退出函数或尝试恢复
        return;
    }
    g_assert(registration_id > 0);
    global_connection = connection;
}

static void
on_name_acquired(GDBusConnection *connection,
                 const gchar *name,
                 gpointer user_data)
{
    g_info("mm: on_name_acquired: D-Bus name '%s' acquired on connection %p", name, connection);
}

static void
on_name_lost(GDBusConnection *connection,
             const gchar *name,
             gpointer user_data)
{
    g_info("mm: on_name_lost: D-Bus name '%s' lost on connection %p", name, connection);
    exit(1);
}

/********************************关机前执行的操作**************************************************/
static void handle_signal(int signum)
{
    gboolean ret;
    if (mm_shutdown_action) {
        if (mm_shutdown_sync && !mm_move_pkgs_data_to_layer()) {
            g_warning("mm: mm_move_pkgs_data_to_layer: Pre-shutdown data sync failed.");
        }

        ret = mm_delete_tmp_layer_data();
        if (!ret) {
            g_info("mm: mm_delete_tmp_layer_data: Performing mm cleanup tasks failed...");
            return;
        }
        g_info("mm: mm_delete_tmp_layer_data: Performing mm cleanup tasks success...");
    }
    exit(0);
}

int main(int argc, char **argv)
{
    GMainLoop *loop;
    guint16 owner_id;
    GError *error = NULL;

    g_unix_signal_add(SIGTERM, (GSourceFunc)handle_signal, NULL);
    g_unix_signal_add(SIGINT, (GSourceFunc)handle_signal, NULL);

    /*init log*/
    mm_log_init();

    // 判断ostree-ovl.conf和ostree-pkgs.db文件是否存在，如果不存在，则创建该文件
    mm_init_config_file(MM_CONFIG_FILE_PATH);
    g_info("mm: mm_init_config_file: Config file initialized at %s", MM_CONFIG_FILE_PATH);

    /*
    mm_init_db_file(PKGS_DB_PATH);
    g_info("mm: mm_init_db_file: Database file initialized at %s", PKGS_DB_PATH);
    */
    /*init gdbus*/
    introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
    if (introspection_data == NULL) {
        g_warning("mm: g_dbus_node_info_new_for_xml: Failed to parse introspection XML: %s", error->message);
        g_error_free(error); // 释放错误对象
        return -1;
    }
    g_info("mm: D-Bus introspection data initialized");

    owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM,
                              "com.kylin.MaintainMode",
                              G_BUS_NAME_OWNER_FLAGS_NONE,
                              on_bus_acquired,
                              on_name_acquired,
                              on_name_lost,
                              NULL,
                              NULL);
    if (owner_id == 0) {
        g_warning("mm: Failed to own D-Bus name");
        return -1; // 返回非零值表示错误
    }

    /*mainloop*/
    loop = g_main_loop_new(NULL, TRUE);

    g_info("mm: Starting GLib main loop");
    g_main_loop_run(loop);

    g_info("mm: Exiting GLib main loop, unowning D-Bus name");
    g_bus_unown_name(owner_id);

    g_info("mm: Unreferencing D-Bus node info");
    g_dbus_node_info_unref(introspection_data);

    g_info("mm: Unreferencing GLib main loop");
    g_main_loop_unref(loop);

    return 0;
}
