#include "common.h"
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

static void set_discovery_filter_setup(DBusMessageIter *iter, void *user_data)
{
    ble_scanner_cfg_t *scan_cfg = user_data;
    DBusMessageIter dict;

    dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
                                     DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                                         DBUS_TYPE_STRING_AS_STRING
                                             DBUS_TYPE_VARIANT_AS_STRING
                                                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
                                     &dict);

    g_dbus_dict_append_array(&dict, "UUIDs", DBUS_TYPE_STRING,
                             &scan_cfg->UUIDs,
                             scan_cfg->uuids_len);

    if (scan_cfg->Pathloss != DISTANCE_VAL_INVALID)
        g_dbus_dict_append_entry(&dict, "Pathloss", DBUS_TYPE_UINT16,
                                 &scan_cfg->Pathloss);

    if (scan_cfg->RSSI != DISTANCE_VAL_INVALID)
        g_dbus_dict_append_entry(&dict, "RSSI", DBUS_TYPE_INT16,
                                 &scan_cfg->RSSI);

    if (scan_cfg->Transport != NULL)
        g_dbus_dict_append_entry(&dict, "Transport", DBUS_TYPE_STRING,
                                 &scan_cfg->Transport);

    if (scan_cfg->DuplicateData)
        g_dbus_dict_append_entry(&dict, "DuplicateData",
                                 DBUS_TYPE_BOOLEAN,
                                 &scan_cfg->DuplicateData);

    dbus_message_iter_close_container(iter, &dict);
}

static void set_discovery_filter_reply(DBusMessage *message, void *user_data)
{
    DBusError error;

    dbus_error_init(&error);
    if (dbus_set_error_from_message(&error, message) == TRUE)
    {
        bt_shell_printf("SetDiscoveryFilter failed: %s\n", error.name);
        dbus_error_free(&error);
        return bt_shell_noninteractive_quit(EXIT_FAILURE);
    }

    bt_shell_printf("SetDiscoveryFilter success\n");

    return;
}

static void set_discovery_filter(ble_scanner_cfg_t *scan_cfg)
{
    struct adapter *adapter;

    adapter = scan_cfg->adapter;

    if (g_dbus_proxy_method_call(adapter->proxy, "SetDiscoveryFilter",
                                 set_discovery_filter_setup, set_discovery_filter_reply,
                                 scan_cfg, NULL) == FALSE)
    {
        bt_shell_printf("Failed to set discovery filter\n");
        return;
    }
}

int start_hci_scan(ble_scanner_cfg_t *scan_cfg, struct adapter *adapter, bool start)
{
    uint8_t filter_dup = scan_cfg->filter_dup;
    int fd;
    int err;
    struct hci_filter new_filter;

    if (scan_cfg->scan_fd == 0)
    {
        fd = hci_open_dev(adapter->dev_id);
        if (fd < 0)
        {
            dy_syslog(LOG_DEBUG, "open %d failed\n", adapter->dev_id);
            return -1;
        }
        scan_cfg->scan_fd = fd;
    }

    if (scan_cfg->scan_fd)
    {
        fd = scan_cfg->scan_fd;
        if (start)
        {
            err = hci_le_set_scan_enable(fd, 0x01, 0x01, 10000);
            if (err < 0)
            {
                dy_syslog(LOG_DEBUG, "set scan enable failed, err:%d; %s errno:%d\n", err, strerror(errno), errno);
                return err;
            }

            dy_syslog(LOG_DEBUG, "start scan\n");
        }
        else
        {
            err = hci_le_set_scan_enable(fd, 0x00, 0x00, 10000);
            if (err < 0)
            {
                dy_syslog(LOG_DEBUG, "set scan disable failed, err:%d; %s errno:%d\n", err, strerror(errno), errno);
                return err;
            }
            dy_syslog(LOG_DEBUG, "stop scan\n");
        }
    }
    return 0;
}

bool set_hci_scan_param(ble_scanner_cfg_t *scan_cfg, struct adapter *adapter)
{
    uint8_t filter_dup = scan_cfg->filter_dup;
    int fd;
    int err;
    struct hci_filter new_filter;

    if (scan_cfg->scan_fd == 0)
    {
        fd = hci_open_dev(adapter->dev_id);
        if (fd < 0)
        {
            dy_syslog(LOG_DEBUG, "open %d failed\n", adapter->dev_id);
            return false;
        }
        scan_cfg->scan_fd = fd;
    }

    if (scan_cfg->scan_fd)
    {
        fd = scan_cfg->scan_fd;
        err = hci_le_set_scan_enable(fd, 0x00, 0x00, 10000);
        if (err < 0)
        {
            dy_syslog(LOG_DEBUG, "set scan disable failed, err:%d; %s errno:%d\n", err, strerror(errno), errno);
            return false;
        }
        err = hci_le_set_scan_parameters(fd, scan_cfg->scan_type,
                                         htobs(scan_cfg->interval), htobs(scan_cfg->window),
                                         scan_cfg->own_type, scan_cfg->filter_policy, 10000);
        if (err < 0)
        {
            dy_syslog(LOG_DEBUG, "set scan parameter failed, err:%d; %s errno:%d\n", err, strerror(errno), errno);
            return false;
        }
    }

    dy_syslog(LOG_DEBUG, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n");
    return true;
}

bool start_scan(ble_scanner_cfg_t *scan_cfg)
{
    dbus_bool_t enable;
    const char *method;
    struct adapter *adapter;

    adapter = scan_cfg->adapter;

    enable = TRUE;
    set_discovery_filter(scan_cfg);
    method = "StartDiscovery";

    if (g_dbus_proxy_method_call(adapter->proxy, method,
                                 NULL, start_discovery_reply,
                                 adapter, NULL) == FALSE)
    {
        bt_shell_printf("Failed to %s discovery\n",
                        enable == TRUE ? "start" : "stop");
        return true;
    }
}

bool stop_scan(ble_scanner_cfg_t *scan_cfg)
{
    // TODO
    return true;
}
