#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <getopt.h>
#include <stdbool.h>
#include <wordexp.h>
#include <ctype.h>

#include "lib/bluetooth.h"
#include "lib/hci.h"
#include "lib/hci_lib.h"
#include "lib/sdp.h"
#include "lib/sdp_lib.h"

#include "src/uuid-helper.h"
#include "lib/mgmt.h"
#include "monitor/bt.h"

#include "src/shared/mainloop.h"
#include "src/shared/io.h"
#include "src/shared/util.h"
#include "src/shared/mgmt.h"
#include "src/shared/shell.h"

static struct mgmt *mgmt = NULL;
static uint16_t mgmt_index = MGMT_INDEX_NONE;

#define MAX_AD_UUID_BYTES 32

unsigned int send_cmd(struct mgmt *mgmt, uint16_t op, uint16_t id,
                      uint16_t len, const void *param,
                      void (*cb)(uint16_t id, uint16_t op,
                                 uint8_t status, uint16_t len,
                                 const void *param));
void setting_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len,
                 const void *param);

void set_adv_enable( uint8_t enable)
{
    int index;
	struct bt_hci_cmd_le_set_adv_parameters cp;

	memset(&cp, 0, sizeof(cp));

    index = mgmt_index;
    if (index == MGMT_INDEX_NONE)
        index = 0;

    send_cmd(mgmt, BT_HCI_CMD_LE_SET_ADV_PARAMETERS, index, &cp, sizeof(cp), setting_rsp);
    send_cmd(mgmt, BT_HCI_CMD_LE_SET_ADV_ENABLE, index, &enable, 1, setting_rsp);
}



static void add_adv_rsp(uint8_t status, uint16_t len, const void *param,
                        void *user_data)
{
    const struct mgmt_rp_add_advertising *rp = param;

    if (status != 0)
    {
        printf("Add Advertising failed with status 0x%02x (%s)",
               status, mgmt_errstr(status));
        return;
    }

    if (len != sizeof(*rp))
    {
        printf("Invalid Add Advertising response length (%u)", len);
        return;
    }

    printf("Instance added: %u", rp->instance);
set_adv_enable(1);

    return;
}




int l2cap_le_att_advertise(int dev_id, uint32_t flags, uuid_t UUIDs[], int uuid_cnt,
                           uint8_t *adv_data, uint8_t adv_len,
                           uint8_t *scan_rsp, uint8_t scan_rsp_len,
                           uint16_t timeout, uint16_t duration, int instance)
{
    int index;
    uint8_t val;
    struct mgmt_cp_add_advertising *cp = NULL;
    size_t cp_len;
    uuid_t uuid;
    size_t uuid_bytes = 0;
    uint8_t uuids[MAX_AD_UUID_BYTES];
    int i;
    uint8_t uuid_type = 0;

    // TODO dont support oepn the given dev_id with HCI_CHANNEL_CONTROL
    // it will cause bind fail
    //var->dev[i].mgmt = mgmt_new_by_id(di->dev_id);
    mgmt = mgmt_new_default();

    index = mgmt_index;
    if (index == MGMT_INDEX_NONE)
        index = 0;
    val = 1;

    send_cmd(mgmt, MGMT_OP_SET_ADVERTISING, index, sizeof(val), &val, setting_rsp);

    for (i = 0; i < uuid_cnt; i++)
    {
        uuid = UUIDs[i];

        if (uuid_type && uuid_type != uuid.type)
        {
            printf("UUID types must be consistent");
            goto done;
        }

        if (uuid.type == SDP_UUID16)
        {
            if (uuid_bytes + 2 >= MAX_AD_UUID_BYTES)
            {
                printf("Too many UUIDs");
                goto done;
            }

            put_le16(uuid.value.uuid16, uuids + uuid_bytes);
            uuid_bytes += 2;
        }
        else if (uuid.type == SDP_UUID128)
        {
            if (uuid_bytes + 16 >= MAX_AD_UUID_BYTES)
            {
                printf("Too many UUIDs");
                goto done;
            }

            bswap_128(uuid.value.uuid128.data,
                      uuids + uuid_bytes);
            uuid_bytes += 16;
        }
        else
        {
            printf("Unsupported UUID type");
            goto done;
        }

        if (!uuid_type)
            uuid_type = uuid.type;
    }

    cp_len = sizeof(*cp) + uuid_bytes + adv_len + scan_rsp_len;
    cp = malloc0(cp_len);
    if (!cp)
        goto done;
    cp->instance = instance;
    put_le32(flags, &cp->flags);
    put_le16(timeout, &cp->timeout);
    put_le16(duration, &cp->duration);
    cp->adv_data_len = adv_len + uuid_bytes;
    cp->scan_rsp_len = scan_rsp_len;

    if (uuid_bytes)
    {
        cp->data[0] = uuid_bytes - 1;
        cp->data[1] = uuid_type == SDP_UUID16 ? 0x03 : 0x07;
        memcpy(cp->data + 2, uuids, uuid_bytes - 2);
    }

    memcpy(cp->data + uuid_bytes, adv_data, adv_len);
    memcpy(cp->data + uuid_bytes + adv_len, scan_rsp, scan_rsp_len);

    if (!mgmt_send(mgmt, MGMT_OP_ADD_ADVERTISING, index, cp_len, cp,
                   add_adv_rsp, NULL, NULL))
    {
        printf("Unable to send \"Add Advertising\" command");
        goto done;
    }

done:
    return 0;
}
