#include "common.h"

static void bluez_property_value(const gchar *key, GVariant *value)
{
    const gchar *type = g_variant_get_type_string(value);

    g_print("\t%s : ", key);
    switch (*type)
    {
    case 'o':
    case 's':
        g_print("%s\n", g_variant_get_string(value, NULL));
        break;
    case 'b':
        g_print("%d\n", g_variant_get_boolean(value));
        break;
    case 'u':
        g_print("%d\n", g_variant_get_uint32(value));
        break;
    case 'a':
        /* TODO Handling only 'as', but not array of dicts */
        if (g_strcmp0(type, "as"))
            break;
        g_print("\n");
        const gchar *uuid;
        GVariantIter i;
        g_variant_iter_init(&i, value);
        while (g_variant_iter_next(&i, "s", &uuid))
            g_print("\t\t%s\n", uuid);
        break;
    default:
        g_print("Other\n");
        break;
    }
}

void get_device_path_from_mac(const char *adapter_name, const char *mac_address, char *object_path, size_t object_path_len)
{
    char device_address_str[20 + 1];
    const char *adapter;

    if (adapter_name)
    {
        adapter = adapter_name;
    }
    else
    {
        adapter = "hci0";
    }

    // Transform string from 'DA:94:40:95:E0:87' to 'dev_DA_94_40_95_E0_87'
    strncpy(device_address_str, mac_address, sizeof(device_address_str));
    for (int i = 0; i < strlen(device_address_str); i++)
    {
        if (device_address_str[i] == ':')
        {
            device_address_str[i] = '_';
        }
    }

    // Force a null-terminated character
    device_address_str[20] = '\0';

    // Generate object path like: /org/bluez/hci0/dev_DA_94_40_95_E0_87
    snprintf(object_path, object_path_len, "/org/bluez/%s/dev_%s", adapter, device_address_str);
}

static void bluez_result_connect_async_cb(GObject *con,
                                          GAsyncResult *res,
                                          gpointer data)
{
    ble_dev_t *dev = (ble_dev_t *)data;
    GVariant *result = NULL;
    GError *error = NULL;

    result = g_dbus_connection_call_finish((GDBusConnection *)con, res, &error);
    if (error != NULL)
    {
        g_print("Unable to get result: %s\n", error->message);
        return;
    }
    dev->state = BLE_DEV_STATE_CONNECTING;
    if (result)
    {
        result = g_variant_get_child_value(result, 0);
        bluez_property_value("conenct", result);
    }
    g_variant_unref(result);
}

static void bluez_device_properties_changed(GDBusConnection *conn,
                                            const gchar *sender,
                                            const gchar *path,
                                            const gchar *interface,
                                            const gchar *signal,
                                            GVariant *params,
                                            void *userdata)
{
    (void)conn;
    (void)sender;
    (void)path;
    (void)interface;
    (void)userdata;

    ble_dev_t *dev = (ble_dev_t *)userdata;

    GVariantIter *properties = NULL;
    GVariantIter *unknown = NULL;
    const char *iface;
    const char *key;
    GVariant *value = NULL;
    const gchar *signature = g_variant_get_type_string(params);

    if (g_strcmp0(signature, "(sa{sv}as)") != 0)
    {
        g_print("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)");
        goto done;
    }

    dev->state = BLE_DEV_STATE_CONNECTED;
    g_variant_get(params, "(&sa{sv}as)", &iface, &properties, &unknown);
    while (g_variant_iter_next(properties, "{&sv}", &key, &value))
    {
        bluez_property_value(key, value);
    }
done:
    if (properties != NULL)
        g_variant_iter_free(properties);
    if (value != NULL)
        g_variant_unref(value);
}

int bluez_adapter_connect_dev(ble_dev_t *dev, const char *dst_mac)
{
    char object_path[100];
    GDBusConnection *con = dev->con;

    get_device_path_from_mac("hci0", dst_mac, object_path, sizeof(object_path));
    guint prop_changed;

    printf("send connect reqeust\n");
    g_dbus_connection_call(con,
                           "org.bluez",
                           /* TODO Find the adapter path runtime */
                           object_path,
                           "org.bluez.Device1",
                           "Connect",
                           NULL,
                           NULL,
                           G_DBUS_CALL_FLAGS_NONE,
                           -1,
                           NULL,
                           bluez_result_connect_async_cb,
                           (void *)dev);

    prop_changed = g_dbus_connection_signal_subscribe(con,
                                                      "org.bluez",
                                                      "org.freedesktop.DBus.Properties",
                                                      "PropertiesChanged",
                                                      object_path,
                                                      NULL,
                                                      G_DBUS_SIGNAL_FLAGS_NONE,
                                                      bluez_device_properties_changed,
                                                      (void *)dev,
                                                      NULL);
}