#include "common.h"

static DBusConnection *dbus_conn = NULL;
static char *auto_register_agent = NULL;
static GDBusProxy *agent_manager;

ble_var_t *var = NULL;

static void print_adapter(GDBusProxy *proxy, const char *description)
{
    DBusMessageIter iter;
    const char *address, *name;

    if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
        return;

    dbus_message_iter_get_basic(&iter, &address);

    if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == TRUE)
        dbus_message_iter_get_basic(&iter, &name);
    else
        name = "<unknown>";

    dy_syslog(LOG_DEBUG, "%s%s%sController %s %s\n",
              description ? "[" : "",
              description ?: "",
              description ? "] " : "",
              address, name);
}

static void print_device(GDBusProxy *proxy, const char *description)
{
    DBusMessageIter iter;
    const char *address, *name;

    if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
        return;

    dbus_message_iter_get_basic(&iter, &address);

    if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == TRUE)
        dbus_message_iter_get_basic(&iter, &name);
    else
        name = "<unknown>";

    dy_syslog(LOG_DEBUG, "%s%s%sDevice %s %s\n",
              description ? "[" : "",
              description ?: "",
              description ? "] " : "",
              address, name);
}

static void print_fixed_iter(const char *label, const char *name,
                             DBusMessageIter *iter)
{
    dbus_bool_t *valbool;
    dbus_uint32_t *valu32;
    dbus_uint16_t *valu16;
    dbus_int16_t *vals16;
    unsigned char *byte;
    int len;

    switch (dbus_message_iter_get_arg_type(iter))
    {
    case DBUS_TYPE_BOOLEAN:
        dbus_message_iter_get_fixed_array(iter, &valbool, &len);

        if (len <= 0)
            return;

        dy_syslog(LOG_DEBUG, "%s%s:\n", label, name);
        bt_shell_hexdump((void *)valbool, len * sizeof(*valbool));

        break;
    case DBUS_TYPE_UINT32:
        dbus_message_iter_get_fixed_array(iter, &valu32, &len);

        if (len <= 0)
            return;

        dy_syslog(LOG_DEBUG, "%s%s:\n", label, name);
        bt_shell_hexdump((void *)valu32, len * sizeof(*valu32));

        break;
    case DBUS_TYPE_UINT16:
        dbus_message_iter_get_fixed_array(iter, &valu16, &len);

        if (len <= 0)
            return;

        dy_syslog(LOG_DEBUG, "%s%s:\n", label, name);
        bt_shell_hexdump((void *)valu16, len * sizeof(*valu16));

        break;
    case DBUS_TYPE_INT16:
        dbus_message_iter_get_fixed_array(iter, &vals16, &len);

        if (len <= 0)
            return;

        dy_syslog(LOG_DEBUG, "%s%s:\n", label, name);
        bt_shell_hexdump((void *)vals16, len * sizeof(*vals16));

        break;
    case DBUS_TYPE_BYTE:
        dbus_message_iter_get_fixed_array(iter, &byte, &len);

        if (len <= 0)
            return;

        dy_syslog_hex(LOG_DEBUG, byte, len, "%s%s:\n", label, name);
        bt_shell_hexdump((void *)byte, len * sizeof(*byte));

        break;
    default:
        return;
    };
}

static void print_iter(const char *label, const char *name,
                       DBusMessageIter *iter)
{
    dbus_bool_t valbool;
    dbus_uint32_t valu32;
    dbus_uint16_t valu16;
    dbus_int16_t vals16;
    unsigned char byte;
    const char *valstr;
    DBusMessageIter subiter;
    char *entry;

    if (iter == NULL)
    {
        dy_syslog(LOG_DEBUG, "%s%s is nil\n", label, name);
        return;
    }

    switch (dbus_message_iter_get_arg_type(iter))
    {
    case DBUS_TYPE_INVALID:
        dy_syslog(LOG_DEBUG, "%s%s is invalid\n", label, name);
        break;
    case DBUS_TYPE_STRING:
    case DBUS_TYPE_OBJECT_PATH:
        dbus_message_iter_get_basic(iter, &valstr);
        dy_syslog(LOG_DEBUG, "%s%s: %s\n", label, name, valstr);
        break;
    case DBUS_TYPE_BOOLEAN:
        dbus_message_iter_get_basic(iter, &valbool);
        dy_syslog(LOG_DEBUG, "%s%s: %s\n", label, name,
                  valbool == TRUE ? "yes" : "no");
        break;
    case DBUS_TYPE_UINT32:
        dbus_message_iter_get_basic(iter, &valu32);
        dy_syslog(LOG_DEBUG, "%s%s: 0x%08x\n", label, name, valu32);
        break;
    case DBUS_TYPE_UINT16:
        dbus_message_iter_get_basic(iter, &valu16);
        dy_syslog(LOG_DEBUG, "%s%s: 0x%04x\n", label, name, valu16);
        break;
    case DBUS_TYPE_INT16:
        dbus_message_iter_get_basic(iter, &vals16);
        dy_syslog(LOG_DEBUG, "%s%s: %d\n", label, name, vals16);
        break;
    case DBUS_TYPE_BYTE:
        dbus_message_iter_get_basic(iter, &byte);
        dy_syslog(LOG_DEBUG, "%s%s: 0x%02x\n", label, name, byte);
        break;
    case DBUS_TYPE_VARIANT:
        dbus_message_iter_recurse(iter, &subiter);
        print_iter(label, name, &subiter);
        break;
    case DBUS_TYPE_ARRAY:
        dbus_message_iter_recurse(iter, &subiter);

        if (dbus_type_is_fixed(
                dbus_message_iter_get_arg_type(&subiter)))
        {
            print_fixed_iter(label, name, &subiter);
            break;
        }

        while (dbus_message_iter_get_arg_type(&subiter) !=
               DBUS_TYPE_INVALID)
        {
            print_iter(label, name, &subiter);
            dbus_message_iter_next(&subiter);
        }
        break;
    case DBUS_TYPE_DICT_ENTRY:
        dbus_message_iter_recurse(iter, &subiter);
        entry = g_strconcat(name, " Key", NULL);
        print_iter(label, entry, &subiter);
        g_free(entry);

        entry = g_strconcat(name, " Value", NULL);
        dbus_message_iter_next(&subiter);
        print_iter(label, entry, &subiter);
        g_free(entry);
        break;
    default:
        dy_syslog(LOG_DEBUG, "%s%s has unsupported type\n", label, name);
        break;
    }
}

static int get_fixed_iter_data(unsigned char *buf,
                               DBusMessageIter *iter)
{
    dbus_bool_t *valbool;
    dbus_uint32_t *valu32;
    dbus_uint16_t *valu16;
    dbus_int16_t *vals16;
    unsigned char *byte;
    int len = 0;
    unsigned char *data;

    switch (dbus_message_iter_get_arg_type(iter))
    {
    case DBUS_TYPE_BOOLEAN:
        dbus_message_iter_get_fixed_array(iter, &data, &len);

        if (len <= 0)
            return 0;

        break;
    case DBUS_TYPE_UINT32:
        dbus_message_iter_get_fixed_array(iter, &data, &len);

        if (len <= 0)
            return 0;

        break;
    case DBUS_TYPE_UINT16:
        dbus_message_iter_get_fixed_array(iter, &data, &len);

        if (len <= 0)
            return 0;

        break;
    case DBUS_TYPE_INT16:
        dbus_message_iter_get_fixed_array(iter, &data, &len);

        if (len <= 0)
            return 0;

        break;
    case DBUS_TYPE_BYTE:
        dbus_message_iter_get_fixed_array(iter, &data, &len);

        if (len <= 0)
            return 0;

        break;
    default:
        return 0;
    };

    memcpy(buf, data, len);
    return len;
}

static int get_iter_data(unsigned char *buf,
    DBusMessageIter *iter, int isadv)
{
    dbus_bool_t valbool;
    dbus_uint32_t valu32;
    dbus_uint16_t valu16;
    dbus_int16_t vals16;
    unsigned char byte;
    const char *valstr;
    DBusMessageIter subiter;
    char *entry;
    int len = 0;
    int t;

    if (iter == NULL)
    {
        return 0;
    }

    switch (dbus_message_iter_get_arg_type(iter))
    {
    case DBUS_TYPE_INVALID:
        break;
    case DBUS_TYPE_STRING:
    case DBUS_TYPE_OBJECT_PATH:
        dbus_message_iter_get_basic(iter, &valstr);
        len = strlen(valstr);
        memcpy(buf, valstr, len);
        break;
    case DBUS_TYPE_BOOLEAN:
        dbus_message_iter_get_basic(iter, &valbool);
        len = 1;
        memcpy(buf, &valbool, len);
        break;
    case DBUS_TYPE_UINT32:
        dbus_message_iter_get_basic(iter, &valu32);
        len = 4;
        memcpy(buf, &valu32, len);
        break;
    case DBUS_TYPE_UINT16:
        dbus_message_iter_get_basic(iter, &valu16);
        len = 2;
        memcpy(buf, &valu16, len);
        break;
    case DBUS_TYPE_INT16:
        dbus_message_iter_get_basic(iter, &vals16);
        len = 2;
        memcpy(buf, &vals16, len);
        break;
    case DBUS_TYPE_BYTE:
        dbus_message_iter_get_basic(iter, &byte);
        if (isadv == 0) {
            /*
             * Avoid add byte 38 (0x26) to the advertising data
            dict entry(
               string "AdvertisingData"
               variant                   array [
                     dict entry(
                        byte 38
                        variant                            array of bytes [
                              06 ff b0 01 57 12 04 03 fc 5c 00
                           ]
                     )
                  ]
            )
             */
            len = 1;
            memcpy(buf, &byte, len);
        }
        break;
    case DBUS_TYPE_VARIANT:
        dbus_message_iter_recurse(iter, &subiter);
        len = get_iter_data(buf, &subiter, isadv);
        break;
    case DBUS_TYPE_ARRAY:
        dbus_message_iter_recurse(iter, &subiter);

        if (dbus_type_is_fixed(
                dbus_message_iter_get_arg_type(&subiter)))
        {
            t = get_fixed_iter_data(buf, &subiter);
            buf = buf + t;
            len += t;

            break;
        }

        while (dbus_message_iter_get_arg_type(&subiter) !=
               DBUS_TYPE_INVALID)
        {
            t = get_iter_data(buf, &subiter, isadv);
            buf = buf + t;
            len += t;
            dbus_message_iter_next(&subiter);
        }
        break;
    case DBUS_TYPE_DICT_ENTRY:
        dbus_message_iter_recurse(iter, &subiter);
        t = get_iter_data(buf, &subiter, isadv);
        buf += t;
        len += t;

        dbus_message_iter_next(&subiter);
        t = get_iter_data(buf, &subiter, isadv);
        buf += t;
        len += t;
        break;
    default:
        break;
    }
    return len;
}

static void print_property(GDBusProxy *proxy, const char *name)
{
    DBusMessageIter iter;

    if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE)
        return;

    print_iter("\t", name, &iter);
}

static void print_uuid(const char *uuid)
{
    const char *text;

    text = bt_uuidstr_to_str(uuid);
    if (text)
    {
        char str[26];
        unsigned int n;

        str[sizeof(str) - 1] = '\0';

        n = snprintf(str, sizeof(str), "%s", text);
        if (n > sizeof(str) - 1)
        {
            str[sizeof(str) - 2] = '.';
            str[sizeof(str) - 3] = '.';
            if (str[sizeof(str) - 4] == ' ')
                str[sizeof(str) - 4] = '.';

            n = sizeof(str) - 1;
        }

        dy_syslog(LOG_DEBUG, "\tUUID: %s%*c(%s)\n", str, 26 - n, ' ', uuid);
    }
    else
        dy_syslog(LOG_DEBUG, "\tUUID: %*c(%s)\n", 26, ' ', uuid);
}

static void print_uuids(GDBusProxy *proxy)
{
    DBusMessageIter iter, value;

    if (g_dbus_proxy_get_property(proxy, "UUIDs", &iter) == FALSE)
        return;

    dbus_message_iter_recurse(&iter, &value);

    while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING)
    {
        const char *uuid;

        dbus_message_iter_get_basic(&value, &uuid);

        print_uuid(uuid);

        dbus_message_iter_next(&value);
    }
}

static gboolean device_is_child(GDBusProxy *device, GDBusProxy *master)
{
    DBusMessageIter iter;
    const char *adapter, *path;

    if (!master)
        return FALSE;

    if (g_dbus_proxy_get_property(device, "Adapter", &iter) == FALSE)
        return FALSE;

    dbus_message_iter_get_basic(&iter, &adapter);
    path = g_dbus_proxy_get_path(master);

    if (!strcmp(path, adapter))
        return TRUE;

    return FALSE;
}

static struct adapter *find_ctrl(GList *source, const char *path)
{
    struct adapter *adapter = var->adapter;
    if (adapter != NULL)
    {
        if (!strcasecmp(g_dbus_proxy_get_path(adapter->proxy), path))
            return adapter;
    }

    return NULL;
}

struct adapter *find_parent(GDBusProxy *device)
{

    struct adapter *adapter = var->adapter;

    if (adapter != NULL)
    {
        if (device_is_child(device, adapter->proxy) == TRUE)
            return adapter;
    }
    return NULL;
}

static GDBusProxy *find_parent_device(struct adapter *adapter, GDBusProxy *proxy)
{
    GDBusProxy *device;
    GList *list;

    for (list = g_list_first(adapter->devices); list; list = g_list_next(list))
    {
        GDBusProxy *device = list->data;
        if (!strcasecmp(g_dbus_proxy_get_path(device), g_dbus_proxy_get_path(proxy)))
            return device;
    }

    return NULL;
}

static void device_added(GDBusProxy *proxy)
{
    DBusMessageIter iter;
    struct adapter *adapter = find_parent(proxy);

    if (!adapter)
    {
        dy_syslog(LOG_DEBUG, "no parent adapter found\n");
        return;
    }

    adapter->devices = g_list_append(adapter->devices, proxy);

    adapter->dev_changed_ts = time(NULL);
    DBusMessageIter addr_iter;

    if (g_dbus_proxy_get_property(proxy, "Address",
                                  &addr_iter) == TRUE)
    {
        const char *address = NULL;

        dbus_message_iter_get_basic(&addr_iter,
                                    &address);
        ble_slave_dev_t *slave = find_slave_dev_by_address(adapter, address);
        if (slave != NULL)
        {
            slave->proxy = proxy;
            dy_syslog(LOG_DEBUG, "set %s proxy:%p\n", address, proxy);
            DBusMessageIter conn_iter;
            if (g_dbus_proxy_get_property(proxy, "Connected", &conn_iter) == TRUE)
            {
                dbus_bool_t connected;
                dbus_message_iter_get_basic(&conn_iter, &connected);
                if (connected)
                {
                    long long ms = get_time_stamp_ms();
                    slave->last_ms = ms;
                    __sync_synchronize();
                    slave->state = SLAVE_DEV_CONNECTED;
                }
            }
        }

        ble_scan_dev_t *scan_dev = find_scan_dev_by_address(adapter, address);
        if (scan_dev != NULL) {
            int len, idx;
            unsigned char buf[256];
            const char * fields[] = { "AdvertisingData", "ManufacturerData", "Name", "Alias", NULL, };

            scan_dev->proxy = proxy;
            DBusMessageIter mfd_iter;
            for (idx = 0; fields[idx]; ++idx) {
                const char * field;
                field = fields[idx];
                memset(&mfd_iter, 0, sizeof(mfd_iter));
                if (g_dbus_proxy_get_property(proxy, field, &mfd_iter) == TRUE) {
                    memset(buf, 0, sizeof(buf));
                    len = get_iter_data(buf, &mfd_iter, idx == 0);
                    if (len > 0) {
                        ble_send_adv_data_to_pp(var, adapter->dev_id, scan_dev,
                            field, buf, len);
                        break;
                    }
                }
            }
        }
    }

    print_device(proxy, COLORED_NEW);
    bt_shell_set_env(g_dbus_proxy_get_path(proxy), proxy);
}

static void adapter_added(GDBusProxy *proxy)
{
    struct adapter *adapter = var->adapter;

    if (adapter == NULL)
    {
        struct adapter *adapter = g_malloc0(sizeof(struct adapter));
        adapter->var = var;
        adapter->proxy = proxy;
        var->adapter = adapter;
        ble_load_cfg(var, adapter, proxy);
    }
    else
    {
        adapter->var = var;
        adapter->proxy = proxy;
    }

    print_adapter(proxy, COLORED_NEW);
    bt_shell_set_env(g_dbus_proxy_get_path(proxy), proxy);
}

static void ad_manager_added(GDBusProxy *proxy)
{
    struct adapter *adapter = var->adapter;

    if (adapter != NULL)
    {
        if (!strcasecmp(g_dbus_proxy_get_path(adapter->proxy), g_dbus_proxy_get_path(proxy)))
        {
            adapter->ad_proxy = proxy;
            return;
        }
    }
    dy_syslog(LOG_ERR, "ad manager add failed");
}

static void proxy_added(GDBusProxy *proxy, void *user_data)
{
    const char *interface;

    interface = g_dbus_proxy_get_interface(proxy);
    dy_syslog(LOG_DEBUG, "=====interface:%s, path:%s proxy:%p\n", interface, g_dbus_proxy_get_path(proxy), proxy);

    if (!strcmp(interface, "org.bluez.Device1"))
    {
        device_added(proxy);
    }
    else if (!strcmp(interface, "org.bluez.Adapter1"))
    {
        if (strcmp(g_dbus_proxy_get_path(proxy), var->proxy_path) == 0)
        {
            adapter_added(proxy);
        }
    }
    else if (!strcmp(interface, "org.bluez.AgentManager1"))
    {
        if (!agent_manager)
        {
            agent_manager = proxy;
            if (auto_register_agent)
            {
                agent_register(dbus_conn, agent_manager, auto_register_agent);
            }
            var->agent_manager = proxy;
        }
    }
    else if (!strcmp(interface, "org.bluez.GattService1"))
    {
        gatt_add_service(proxy);
    }
    else if (!strcmp(interface, "org.bluez.GattCharacteristic1"))
    {
        gatt_add_characteristic(proxy);
    }
    else if (!strcmp(interface, "org.bluez.GattDescriptor1"))
    {
        gatt_add_descriptor(proxy);
    }
    else if (!strcmp(interface, "org.bluez.GattManager1"))
    {
        my_gatt_add_manager(proxy);
    }
    else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1"))
    {
        ad_manager_added(proxy);
    }
}

static void device_removed(GDBusProxy *proxy)
{
    struct adapter *adapter = find_parent(proxy);
    if (!adapter)
    {
        dy_syslog(LOG_DEBUG, "cannot find parent adapter\n");
        return;
    }

    adapter->devices = g_list_remove(adapter->devices, proxy);

    DBusMessageIter addr_iter;

    if (g_dbus_proxy_get_property(proxy, "Address",
                                  &addr_iter) == TRUE)
    {
        const char *address = NULL;

        dbus_message_iter_get_basic(&addr_iter,
                                    &address);
        ble_slave_dev_t *slave = find_slave_dev_by_address(adapter, address);
        if (slave != NULL)
        {
            slave->proxy = NULL;
            slave->state = SLAVE_DEV_NONE;
        }
    }

    print_device(proxy, COLORED_DEL);
    bt_shell_set_env(g_dbus_proxy_get_path(proxy), NULL);
}

static void adapter_removed(GDBusProxy *proxy)
{
    struct adapter *adapter = var->adapter;

    if (adapter && adapter->proxy == proxy) {
#if 0
        var->adapter = NULL;
        ___sync_synchronize();
#else
        struct timespec tspec;
        dy_syslog(LOG_ERR, "Removing adapter, exit now to avoid core!\n");

        tspec.tv_sec = 3;
        tspec.tv_nsec = 0;
        nanosleep(&tspec, NULL);
        _exit(1);
#endif

        print_adapter(proxy, COLORED_DEL);
        bt_shell_set_env(g_dbus_proxy_get_path(proxy), NULL);
        g_list_free(adapter->devices);
        g_free(adapter);
        return;
    }
}

static void proxy_removed(GDBusProxy *proxy, void *user_data)
{
    const char *interface;

    interface = g_dbus_proxy_get_interface(proxy);

    if (!strcmp(interface, "org.bluez.Device1"))
    {
        device_removed(proxy);
    }
    else if (!strcmp(interface, "org.bluez.Adapter1"))
    {
        adapter_removed(proxy);
    }
    else if (!strcmp(interface, "org.bluez.AgentManager1"))
    {
        if (agent_manager == proxy)
        {
            agent_manager = NULL;
            if (auto_register_agent)
            {
                agent_unregister(dbus_conn, NULL);
            }
            var->agent_manager = NULL;
        }
    }
    else if (!strcmp(interface, "org.bluez.GattService1"))
    {
        gatt_remove_service(proxy);
    }
    else if (!strcmp(interface, "org.bluez.GattCharacteristic1"))
    {
        gatt_remove_characteristic(proxy);
    }
    else if (!strcmp(interface, "org.bluez.GattDescriptor1"))
    {
        gatt_remove_descriptor(proxy);
    }
    else if (!strcmp(interface, "org.bluez.GattManager1"))
    {
        my_gatt_remove_manager(proxy);
    }
    else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1"))
    {
        struct adapter *adapter = find_ctrl(NULL, g_dbus_proxy_get_path(proxy));

        if (adapter != NULL)
        {
            ad_unregister(adapter, NULL);
            return;
        }
    }
}

static void handle_ChrcChange(GDBusProxy *proxy, const char *name, DBusMessageIter *iter)
{
    DBusMessageIter subiter;
    char *entry;
    char *device_path = NULL;

    unsigned char *byte;
    int len = -1;

    if (iter == NULL)
    {
        dy_syslog(LOG_DEBUG, "%s is nil\n", name);
        return;
    }

    if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY)
    {
        dbus_message_iter_recurse(iter, &subiter);
        if (dbus_type_is_fixed(dbus_message_iter_get_arg_type(&subiter)))
        {
            if (dbus_message_iter_get_arg_type(&subiter) == DBUS_TYPE_BYTE)
            {
                dbus_message_iter_get_fixed_array(&subiter, &byte, &len);
            }
        }
    }

    if (len < 0)
    {
        byte = NULL;
        len = 0;
        goto done;
    }

    char *addr;
    char *uuid;
    DBusMessageIter iter_n;
    const char *path = g_dbus_proxy_get_path(proxy);
    if (path == NULL)
    {
        dy_syslog(LOG_DEBUG, "path is empty\n");
        goto done;
    }
    device_path = strndup(path, strlen("/org/bluez/hci0/dev_01_02_03_FF_6D_16"));

    GDBusProxy *device = bt_shell_get_env(device_path);
    if (device == NULL)
    {
        goto done;
    }

    struct adapter *adapter = find_parent(device);
    if (adapter == NULL)
    {
        dy_syslog(LOG_DEBUG, "%s:%d\n", __FUNCTION__, __LINE__);
        goto done;
    }

    if (g_dbus_proxy_get_property(device, "Address", &iter_n))
        dbus_message_iter_get_basic(&iter_n, &addr);

    if (g_dbus_proxy_get_property(proxy, "UUID", &iter_n))
        dbus_message_iter_get_basic(&iter_n, &uuid);

    ble_slave_dev_t *slave = find_slave_dev_by_address(adapter, addr);
    ble_send_raw_data_to_pp(var, adapter->dev_id, slave, uuid, "write", byte, len);

done:
    if (device_path != NULL)
    {
        free(device_path);
    }
    return;
}

static void handle_AdapterChange(GDBusProxy *proxy, const char *name, DBusMessageIter *iter)
{
    if (iter == NULL)
    {
        dy_syslog(LOG_DEBUG, "%s is nil\n", name);
        return;
    }

#if 1
    if (strcmp(name, "Powered") == 0)
    {
        dbus_bool_t Powered;
        char cmd[32] = {0};

        dbus_message_iter_get_basic(iter, &Powered);
        if (!Powered)
        {
            dy_syslog(LOG_DEBUG, "The adapter stoped\n");
            sprintf(cmd, "hciconfig %s up", var->dev_name);
            system(cmd);
        }
    }
#endif
    return;
}

static void property_changed(GDBusProxy *proxy, const char *name,
                             DBusMessageIter *iter, void *user_data)
{
    const char *interface;
    struct adapter *ctrl;

    if (iter == NULL)
        return;
    interface = g_dbus_proxy_get_interface(proxy);

    if (!strcmp(interface, "org.bluez.Device1"))
    {
        DBusMessageIter addr_iter;
        char *str;
        struct adapter *adapter = find_parent(proxy);

        if (adapter == NULL)
        {
            dy_syslog(LOG_DEBUG, "no parent adapter found\n");
            return;
        }
        adapter->dev_changed_ts = time(NULL);

        if (g_dbus_proxy_get_property(proxy, "Address",
                                      &addr_iter) == TRUE)
        {
            const char *address = NULL;

            dbus_message_iter_get_basic(&addr_iter,
                                        &address);
            str = g_strdup_printf("[" COLORED_CHG
                                  "] Device %s ",
                                  address);
            ble_slave_dev_t *slave = find_slave_dev_by_address(adapter, address);
            if (slave != NULL)
            {
                if (strcmp(name, "Connected") == 0)
                {
                    dbus_bool_t connected;

                    dbus_message_iter_get_basic(iter, &connected);

                    if (connected)
                    {
                        long long ms = get_time_stamp_ms();
                        slave->last_ms = ms;
                        __sync_synchronize();
                        slave->state = SLAVE_DEV_CONNECTED;
                        dy_syslog(LOG_DEBUG, "connectED");
                    }
                    else
                    {
                        slave->state = SLAVE_DEV_NONE;
                    }
                    slave->state_changed = true;
                }
                else if (strcmp(name, "ServicesResolved") == 0)
                {
                    dbus_bool_t connected;

                    dbus_message_iter_get_basic(iter, &connected);

                    if (connected)
                    {
                        if (slave->last_data.uuid != NULL && slave->last_data.data_str != NULL && slave->last_data.opcode != NULL)
                        {
                            dy_syslog(LOG_DEBUG, "resend the uuid:%s slave->last_data.data_str:%s", slave->last_data.uuid, slave->last_data.data_str);
                            ble_send_data_to_sn(var, slave->SN, slave->last_data.uuid, slave->last_data.data_str, slave->last_data.opcode, false);
                        }
                        dy_syslog(LOG_DEBUG, "connect");
                    }
                }
            }

            ble_scan_dev_t *scan_dev = find_scan_dev_by_address(adapter, address);
            if (scan_dev != NULL) {
                int len, idx;
                unsigned char buf[256];
                const char * fields[] = { "AdvertisingData", "ManufacturerData", "Name", "Alias", NULL, };

                scan_dev->proxy = proxy;
                DBusMessageIter mfd_iter;
                for (idx = 0; fields[idx]; ++idx) {
                    const char * field;
                    field = fields[idx];
                    memset(&mfd_iter, 0, sizeof(mfd_iter));

                    if (g_dbus_proxy_get_property(proxy, field, &mfd_iter) == TRUE) {
                        memset(buf, 0, sizeof(buf));
                        len = get_iter_data(buf, &mfd_iter, idx == 0);
                        if (len > 0) {
                            ble_send_adv_data_to_pp(var, adapter->dev_id, scan_dev,
                                field, buf, len);
                            break;
                        }
                    }
                }
            }
        }
        else
            str = g_strdup("");

        print_iter(str, name, iter);
        g_free(str);
    }
    else if (!strcmp(interface, "org.bluez.Adapter1"))
    {
        DBusMessageIter addr_iter;
        char *str;

        if (g_dbus_proxy_get_property(proxy, "Address",
                                      &addr_iter) == TRUE)
        {
            const char *address;

            dbus_message_iter_get_basic(&addr_iter, &address);
            str = g_strdup_printf("[" COLORED_CHG
                                  "] Controller %s ",
                                  address);
        }
        else
            str = g_strdup("");

        handle_AdapterChange(proxy, name, iter);
        print_iter(str, name, iter);
        g_free(str);
    }
    else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1"))
    {
        DBusMessageIter addr_iter;
        char *str;

        ctrl = find_ctrl(NULL, g_dbus_proxy_get_path(proxy));
        if (!ctrl)
            return;

        if (g_dbus_proxy_get_property(ctrl->proxy, "Address",
                                      &addr_iter) == TRUE)
        {
            const char *address;

            dbus_message_iter_get_basic(&addr_iter, &address);
            str = g_strdup_printf("[" COLORED_CHG
                                  "] Controller %s ",
                                  address);
        }
        else
            str = g_strdup("");

        print_iter(str, name, iter);
        g_free(str);
    }
    else if (!strcmp(interface, "org.bluez.GattCharacteristic1"))
    {
        handle_ChrcChange(proxy, name, iter);
    }
    else
    {
        char *str;

        str = g_strdup_printf("[" COLORED_CHG "] Attribute %s ",
                              g_dbus_proxy_get_path(proxy));

        print_iter(str, name, iter);
        g_free(str);
    }
}

static void client_ready(GDBusClient *client, void *user_data)
{
    dy_syslog(LOG_DEBUG, "%s:%d\n", __FUNCTION__, __LINE__);
    start_work(var);
}

static void connect_handler(DBusConnection *connection, void *user_data)
{
    dy_syslog(LOG_DEBUG, "%s:%d\n", __FUNCTION__, __LINE__);
}

static void disconnect_handler(DBusConnection *connection, void *user_data)
{
    dy_syslog(LOG_DEBUG, "%s:%d\n", __FUNCTION__, __LINE__);
}

static void message_handler(DBusConnection *connection,
                            DBusMessage *message, void *user_data)
{
    dy_syslog(LOG_DEBUG, "[SIGNAL] %s.%s\n", dbus_message_get_interface(message),
              dbus_message_get_member(message));
}

static int get_slot_info(ble_var_t *var, int slot)
{
    char key[8];
    char *data = NULL;
    cJSON *root = NULL;
    cJSON *node = NULL;
    int ret = 0;
    char cmd[32];

    data = read_file_data(BLE_SLOT_INFO);
    if (data == NULL)
    {
        dy_syslog(LOG_ERR, "read slot info file %s error", BLE_SLOT_INFO);
        ret = -1;
        goto out;
    }

    root = cJSON_Parse(data);
    if (!root)
    {
        dy_syslog(LOG_ERR, "parse file %s error", BLE_SLOT_INFO);
        ret = -1;
        goto out;
    }

    sprintf(key, "slot_%d", slot);

    node = cJSON_GetObjectItem(root, key);
    if (node == NULL)
    {
        dy_syslog(LOG_ERR, "not slot info:%d", slot);
        ret = -1;
        goto out;
    }

    GET_JSON_VALUE_STRING(node, "ifname", var->dev_name);
    if (strlen(var->dev_name) == 0)
    {
        dy_syslog(LOG_ERR, "ifname not right:%d", slot);
        ret = -1;
        goto out;
    }

    sprintf(var->proxy_path, "/org/bluez/%s", var->dev_name);
    sprintf(cmd, "hciconfig %s up", var->dev_name);
    system(cmd);
    do {
        char timepath[72];
        const char * timep;
        int timeo = 200, bfd;

        timep = getenv("BLE_TIMEOUT");
        if (timep != NULL) {
            int timo;
            errno = 0;
            timo = (int) strtol(timep, NULL, 0);
            if (errno != 0 || timo <= 0) {
                dy_syslog(LOG_ERR, "Invalid BLE timeout: %s\n", timep);
            } else
                timeo = timo;
        }

        snprintf(timepath, sizeof(timepath),
            "/sys/kernel/debug/bluetooth/%s/supervision_timeout", var->dev_name);
        bfd = open(timepath, O_WRONLY | O_TRUNC);
        if (bfd == -1) {
            bfd = errno;
            dy_syslog(LOG_ERR, "failed to open(%s/supervision_timeout): %s\n",
                var->dev_name, strerror(bfd));
        } else {
            size_t clen;
            clen = (size_t) snprintf(cmd, sizeof(cmd), "%d\n", timeo);
            if (clen == 0 || clen != (size_t) write(bfd, cmd, clen)) {
                dy_syslog(LOG_ERR, "failed to write supervision_timeout clen: %u\n",
                    (unsigned int) clen);
            }
            close(bfd);
        }
    } while (0);
out:
    free(data);
    cJSON_Delete(root);
    return ret;
}

int main(int argc, char *argv[])
{
    GDBusClient *client;
    int status;
    pthread_t thread_sup;
    int slot = 0;
    char *endptr;
    int ret = 0;
    char log_desc[16];

    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s <slot>", argv[0]);
        return -1;
    }

    errno = 0; /* To distinguish success/failure after call */
    slot = strtol(argv[1], &endptr, 10);
    if ((errno == ERANGE && (slot == LONG_MAX || slot == LONG_MIN)) || (errno != 0 && slot == 0))
    {
        perror("strtol");
        exit(EXIT_FAILURE);
    }

    if (endptr == optarg)
    {
        fprintf(stderr, "No digits were found\n");
        exit(EXIT_FAILURE);
    }

    sprintf(log_desc, "BLE_%d", slot);
    openlog(log_desc, LOG_PID, LOG_DAEMON);

    var = g_malloc0(sizeof(ble_var_t));
    if (var == NULL)
    {
        dy_syslog(LOG_DEBUG, "malloc failed\n");
        return -1;
    }
    var->slot = slot;

    ret = get_slot_info(var, slot);
    if (ret == -1)
    {
        while (1)
        {
            sleep(100);
            dy_syslog(LOG_WARNING, "slot not exist: %d", slot);
        }
    }

    dy_syslog(LOG_DEBUG, "proxy_path:%s dev_name:%s dev_id:%d slot:%d", var->proxy_path, var->dev_name, var->dev_id, var->slot);

    mainloop_init();
    auto_register_agent = g_strdup("");

    dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
    g_dbus_attach_object_manager(dbus_conn);
    var->dbus_conn = dbus_conn;

    client = g_dbus_client_new(dbus_conn, "org.bluez", var->proxy_path);
    var->client = client;

    g_dbus_client_set_connect_watch(client, connect_handler, NULL);
    g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
    g_dbus_client_set_signal_watch(client, message_handler, NULL);

    g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
                                     property_changed, NULL);

    g_dbus_client_set_ready_watch(client, client_ready, NULL);
    dy_syslog(LOG_DEBUG, "%s:%d\n", __FUNCTION__, __LINE__);

    ble_mqtt_init(var);

    pthread_create(&thread_sup, NULL, ble_thread_loop, (void *)var);

    status = mainloop_run();

    g_dbus_client_unref(client);
    dbus_connection_unref(dbus_conn);
    g_free(auto_register_agent);
    g_free(var);

    return status;
}
