#include <string.h>

#include "omw_config.h"
#include "omw_dbg.h"
#include "omw_svc.h"

#ifdef OMW_BLE_HOST_V0
#include "hci_core.h"
#include "ll.h"

#include "ll_conn.h"

#define OMW_DEFT_ADV_INTLV_MIN BT_GAP_ADV_SLOW_INT_MIN
#define OMW_DEFT_ADV_INTLV_MAX BT_GAP_ADV_SLOW_INT_MIN
#endif

// #ifdef  OMW_SVC_ERR_CHK
#define OMW_SVC_SET_STARTED_FLAG() (omw_svc_started = 1)
#define OMW_SVC_CLR_STARTED_FLAG() (omw_svc_started = 0)
#define OMW_SVC_CHK_STARTED()  ({if (omw_svc_started) return -1;})
#define OMW_SVC_CHK_CLR_STARTED()  ({if (!omw_svc_started) return; OMW_SVC_CLR_STARTED_FLAG();})
// #define OMW_SVC_CHK_ADV_INITD() ({if (omw_svc_adv_inited != OMW_SVC_INIT_MASK) return -2;})

//flag for adv/scan started or not
static uint8_t omw_svc_started;
// #else
// #define OMW_SVC_CHK_STARTED()
// // #define OMW_SVC_CHK_ADV_INITD()
// #endif


uint8_t tx_enabled;
uint8_t omw_svc_num;

void * omw_sevice_list[OMW_SVC_MAX_NUM];

void dummy() {}
void (* omw_svc_spec_cnnt_cb)() = &dummy;
void (* omw_svc_spec_dcnt_cb)() = &dummy;


__WEAK void omw_app_connected_cb(uint32_t conn_handle, uint16_t cintlv, uint16_t cintlv_min, uint16_t cintlv_max)
{
}

__WEAK void omw_app_disconnected_cb(uint32_t conn_handle, uint8_t reason)
{
}

__WEAK void omw_app_rx_process_cb(const uint8_t * buf, uint16_t len)
{
}

#ifdef OMW_BLE_HOST_V0
uint8_t omw_adv_data_sz;
uint8_t omw_sc_rsp_data_sz;
const void * omw_adv_data;
const void * omw_sc_rsp_data;

static uint8_t omw_svc_inited;

struct bt_hci_cp_le_set_adv_param  g_adv_param =
{
    OMW_DEFT_ADV_INTLV_MIN,
    OMW_DEFT_ADV_INTLV_MAX,
    #ifndef OMW_BLE_EN_BEACON
    BT_HCI_ADV_IND,
    #else
    BT_HCI_ADV_NONCONN_IND,
    #endif
    BT_ADDR_LE_PUBLIC,
    {0},
    0x07,
    BT_LE_ADV_FP_NO_FILTER
};

static void connected(struct bt_conn *conn, uint8_t err)
{
    OMW_SVC_CLR_STARTED_FLAG();

    if (err)
    {
        printf("Connection failed (err 0x%02x)\n", err);
    }
    else
    {
        omw_svc_spec_cnnt_cb();

        omw_app_connected_cb((uint32_t)conn, conn->le.interval, conn->le.interval_min, conn->le.interval_max);
    }
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
    tx_enabled = 0;

    omw_svc_spec_dcnt_cb();

    omw_app_disconnected_cb((uint32_t)conn, reason);

    #if !defined(CONFIG_BT_CENTRAL) && !defined(OMW_MULTI_CNNT)
    omw_svc_start_adv();
    #endif
}
#endif

static struct bt_conn_cb conn_callbacks = {
        .connected = connected,
        .disconnected = disconnected,
        // .le_phy_updated = le_phy_updated,
};

void bt_ready(int err)
{
    if (err)
    {
        printf("Bluetooth init failed (err %d)\n", err);
        return;
    }

    printf("Bluetooth initialized!\n");

    bt_gatt_service_init(omw_svc_num, (const struct bt_gatt_service_static**)omw_sevice_list);

    bt_conn_cb_register(&conn_callbacks);

    omw_svc_inited = 1;

    err = omw_svc_start_adv();

    if (err)
    {
        printf("Advertising failed to start (err %d)\n", err);
        return;
    }

    printf("Advertising successfully started!\n");
}

int omw_svc_updata_cnnt_param(uint32_t handle, uint16_t cnt_intvl_min, uint16_t cnt_intvl_max, uint16_t lt, uint16_t  cnt_to)
{
    struct bt_conn *conn = (struct bt_conn *)handle;
    struct bt_le_conn_param param;

    param.interval_min = cnt_intvl_min;
    param.interval_max = cnt_intvl_max;
    param.latency = lt;
    param.timeout = cnt_to;

    return bt_conn_le_param_update(conn, &param);
}

void omw_set_adv_data(void * padv_data, uint8_t len)
{
    #ifdef OMW_BLE_HOST_V0
    omw_adv_data_sz = len;
    omw_adv_data = padv_data;
    #endif
}

#ifdef OMW_BLE_HOST_V0
uint8_t ll_adv_data_get(uint8_t * data);
#endif

uint8_t omw_svc_get_adv_raw_data(uint8_t * buf)
{
    #ifdef OMW_BLE_HOST_V0
    return ll_adv_data_get(buf);
    #endif
    return 0;
}

void omw_set_scan_rsp_data(void * pscr_data, uint8_t len)
{
    #ifdef OMW_BLE_HOST_V0
    omw_sc_rsp_data_sz = len;
    omw_sc_rsp_data = pscr_data;
    #endif
}

void omw_svc_add_base_svc(const void * p_svc)
{
    for (int i = 0; i < omw_svc_num; i++){
        if (omw_sevice_list[i] == p_svc) return;
    }

    omw_sevice_list[omw_svc_num++] = (void *)p_svc;
}

int omw_svc_add_svc(const omw_svc_info_t * p_svc_info)
{
    OMW_SVC_CHK_STARTED();

    // #ifdef  OMW_SVC_ERR_CHK
    if (omw_svc_num >= OMW_SVC_MAX_NUM)  return -2;
    // #endif

    omw_svc_add_base_svc(p_svc_info->svc_data_addr);

    return 0;
}

void omw_svc_set_adv_intvl(uint16_t min, uint16_t max)
{
    #ifdef OMW_BLE_HOST_V0
    g_adv_param.min_interval = min;
    g_adv_param.max_interval = max;
    #endif
}

void omw_svc_set_adv_type(uint8_t type)
{
    #ifdef OMW_BLE_HOST_V0
    g_adv_param.type = type;
    #endif
}

void omw_svc_set_adv_addr(uint8_t addr_type, bt_addr_le_t * dir_addr_info)
{
    #ifdef OMW_BLE_HOST_V0
    g_adv_param.own_addr_type = addr_type;
    if (NULL != dir_addr_info) g_adv_param.direct_addr = *dir_addr_info;
    #endif
}

void omw_svc_set_adv_chl_map(uint8_t cm)
{
    #ifdef OMW_BLE_HOST_V0
    g_adv_param.channel_map = cm;
    #endif
}

void omw_svc_set_adv_filter_plc(uint8_t fplc)
{
    #ifdef OMW_BLE_HOST_V0
    g_adv_param.filter_policy = fplc;
    #endif
}

int omw_svc_start_svc_adv(const omw_svc_info_t * p_svc_info)
{
    #ifdef OMW_BLE_HOST_V0
    omw_adv_data = p_svc_info->svc_adv_data_addr;
    omw_adv_data_sz = p_svc_info->svc_adv_data_len;

    omw_sc_rsp_data = p_svc_info->svc_scan_rsp_data_addr;
    omw_sc_rsp_data_sz = p_svc_info->svc_scan_rsp_data_len;
    #endif

    return omw_svc_start_adv();
}
int ll_adv_reset(void);
int omw_svc_start_adv()
{
    OMW_SVC_CHK_STARTED();

    ll_adv_reset();

    #ifdef OMW_BLE_HOST_V0
    if (omw_svc_inited)
    {
        // #ifdef  OMW_SVC_ERR_CHK
        OMW_SVC_SET_STARTED_FLAG();
        // #endif
        (void)hci_send_cmd_le_set_adv_param(g_adv_param.min_interval, g_adv_param.max_interval, g_adv_param.type, g_adv_param.own_addr_type, &g_adv_param.direct_addr, g_adv_param.channel_map, g_adv_param.filter_policy);
        if (g_adv_param.type != BT_HCI_ADV_DIRECT_IND)
        {
            (void)hci_send_cmd_le_set_adv_data(omw_adv_data, omw_adv_data_sz);
            (void)hci_send_cmd_le_set_scan_rsp_data(omw_sc_rsp_data, omw_sc_rsp_data_sz);
        }
        (void)hci_send_cmd_le_set_adv_enable(true);
    }
    #endif

    return 0;
}

void omw_svc_stop_adv()
{
    // #ifdef  OMW_SVC_ERR_CHK
    OMW_SVC_CHK_CLR_STARTED();
    // #endif

    #ifdef OMW_BLE_HOST_V0
    (void)hci_send_cmd_le_set_adv_enable(false);
    #endif
}

void omw_svc_disconnect(uint32_t handle)
{
    #ifdef OMW_BLE_HOST_V0
    bt_hci_disconnect(((struct bt_conn *)handle)->handle, 0x13);  //BT_ERR_REMOTE_USER_TERM_CONN
    #endif
}


void omw_svc_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
    bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
    tx_enabled = notif_enabled;
}
