/*
 * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_bt_main.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_device.h"
#include "driver/uart.h"
#include "string.h"

#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "ble_spp_server_define.h"

#include "ble_spp_server.h"
#include "os/os.h"

#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_WRN
#define SYS_LOG_DOMAIN "GATTS_SPP"
#define CONS_ABORT()
#include "sys_log.h"

#define _MAX_FIFO_ALLOC_CNT 100 /* 允许最大申请内存的次数 */
#define SPP_MTU_HEAD_SIZE 5

typedef struct
{
    uint32_t buffer_size;     // 指定接收缓存长度
    os_work_t *wkup_work_hdl; // 当接收非空时唤醒的工作项， NULL 值可用
    os_pipe_t pipe_obj;
} __ble_port_data_t;

typedef struct
{
    uint32_t len;
    uint8_t data[0];
} __ble_fifo_t;

static sb_data_port_t s_port_cmd; // 命令通道数据接口
static sb_data_port_t s_port_val; // 数据通道数据接口
static uint32_t s_val_fifo_cnt;   // 用于限制 fifo 申请的次数
static os_fifo_t s_val_fifo_hdl;  // 数据通道的缓存队列句柄
static struct
{
    char device_name[20];                                  // 设备名
    ble_server_gen_manufacturer_data_cb manufacturer_data; // 回调函数，生成 manufacturer[20]
    ble_server_connect_cb connect_cb;                      // 回调函数， NULL 值可用
} s_init_param;                                            // 初始化参数

#define SPP_PROFILE_NUM 1
#define SPP_PROFILE_APP_IDX 0
#define ESP_SPP_APP_ID 0x56
#define SPP_SVC_INST_ID 0

/// SPP Service
static const uint16_t spp_service_uuid = 0xABF0;
/// Characteristic UUID
#define ESP_GATT_UUID_SPP_DATA_RECEIVE 0xABF1    // 透传数据接收
#define ESP_GATT_UUID_SPP_DATA_NOTIFY 0xABF2     // 透传数据发送
#define ESP_GATT_UUID_SPP_COMMAND_RECEIVE 0xABF3 // 命令接收
#define ESP_GATT_UUID_SPP_COMMAND_NOTIFY 0xABF4  // 命令响应的返回。

#define ADV_CONFIG_FLAG (1 << 0)
#define SCAN_RSP_CONFIG_FLAG (1 << 1)

static uint8_t s_manufacturer_data[20];
static uint8_t s_mac[6];
static uint8_t adv_config_done = 0;

static uint8_t const spp_adv_data[23] = {
    /* Flags */
    0x02, 0x01, 0x06,
    /* Complete List of 16-bit Service Class UUIDs */
    0x03, 0x03, 0xF0, 0xAB,
    /* Complete Local Name in advertising */
    0x0F, 0x09, 'E', 'S', 'P', '_', 'S', 'P', 'P', '_', 'S', 'E', 'R', 'V', 'E', 'R'};

static uint8_t const service_uuid[16] = {
    /* LSB <--------------------------------------------------------------------------------> MSB */
    // first uuid, 16bit, [12],[13] is the value
    0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00};

/* The length of adv data must be less than 31 bytes */
static esp_ble_adv_data_t adv_data = {
    .set_scan_rsp = false,
    .include_name = true,
    .include_txpower = true,
    .min_interval = 0x20,
    .max_interval = 0x40,
    .appearance = 0x00,
    .manufacturer_len = 0,       // TEST_MANUFACTURER_DATA_LEN,
    .p_manufacturer_data = NULL, // s_manufacturer_data,
    .service_data_len = 0,
    .p_service_data = NULL,
    .service_uuid_len = sizeof(service_uuid),
    .p_service_uuid = (uint8_t *)service_uuid,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

// scan response data
static esp_ble_adv_data_t scan_rsp_data = {
    .set_scan_rsp = true,
    .include_name = false,
    .include_txpower = false,
    .min_interval = 0x20,
    .max_interval = 0x40,
    .appearance = 0x00,
    .manufacturer_len = sizeof(s_manufacturer_data),
    .p_manufacturer_data = s_manufacturer_data,
    .service_data_len = 0,
    .p_service_data = NULL,
    .service_uuid_len = 0,
    .p_service_uuid = NULL,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

static uint16_t spp_mtu_size = 23;
static uint16_t spp_conn_id = 0xffff;
static esp_gatt_if_t spp_gatts_if = 0xff;

#ifdef SUPPORT_HEARTBEAT
#define ESP_GATT_UUID_SPP_HEARTBEAT 0xABF5
static QueueHandle_t cmd_heartbeat_queue = NULL;
static uint8_t heartbeat_s[9] = {'E', 's', 'p', 'r', 'e', 's', 's', 'i', 'f'};
static bool enable_heart_ntf = false; // 是否启用notify
static uint8_t heartbeat_count_num = 0;
#endif

static bool enable_data_ntf = false; // 是否启用notify
static bool is_connected = false;    // 蓝牙有无连接

// 远程蓝牙设备地址
static esp_bd_addr_t spp_remote_bda = {
    0x0,
};

static uint16_t spp_handle_table[SPP_IDX_NB]; // 特征值表

static esp_ble_adv_params_t spp_adv_params = {
    // 广播出去的信息
    .adv_int_min = 0x20,
    .adv_int_max = 0x40,
    .adv_type = ADV_TYPE_IND,
    .own_addr_type = BLE_ADDR_TYPE_PUBLIC,
    .channel_map = ADV_CHNL_ALL,
    .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

struct gatts_profile_inst
{
    esp_gatts_cb_t gatts_cb;
    uint16_t gatts_if;
    uint16_t app_id;
    uint16_t conn_id;
    uint16_t service_handle;
    esp_gatt_srvc_id_t service_id;
    uint16_t char_handle;
    esp_bt_uuid_t char_uuid;
    esp_gatt_perm_t perm;
    esp_gatt_char_prop_t property;
    uint16_t descr_handle;
    esp_bt_uuid_t descr_uuid;
};

typedef struct spp_receive_data_node
{
    int32_t len;
    uint8_t *node_buff;
    struct spp_receive_data_node *next_node;
} spp_receive_data_node_t;

typedef struct spp_receive_data_buff
{
    int32_t node_num;
    int32_t buff_size;
    spp_receive_data_node_t *first_node;
} spp_receive_data_buff_t;

static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);

/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */
static struct gatts_profile_inst spp_profile_tab[SPP_PROFILE_NUM] = {
    [SPP_PROFILE_APP_IDX] = {
        .gatts_cb = gatts_profile_event_handler,
        .gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
    },
};

/*
 *  SPP PROFILE ATTRIBUTES
 ****************************************************************************************
 */

#define CHAR_DECLARATION_SIZE (sizeof(uint8_t))
static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE;
static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE;
static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;

static const uint8_t char_prop_read_notify = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
static const uint8_t char_prop_read_write = ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_READ;

#ifdef SUPPORT_HEARTBEAT
static const uint8_t char_prop_read_write_notify = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
#endif

/// SPP Service - data receive characteristic, read&write without response
static const uint16_t spp_data_receive_uuid = ESP_GATT_UUID_SPP_DATA_RECEIVE;
static const uint8_t spp_data_receive_val[20] = {0x00};

/// SPP Service - data notify characteristic, notify&read
static const uint16_t spp_data_notify_uuid = ESP_GATT_UUID_SPP_DATA_NOTIFY;
static const uint8_t spp_data_notify_val[20] = {0x00};
static const uint8_t spp_data_notify_ccc[2] = {0x00, 0x00};

/// SPP Service - command characteristic, read&write without response
static const uint16_t spp_command_uuid = ESP_GATT_UUID_SPP_COMMAND_RECEIVE;
static const uint8_t spp_command_val[10] = {0x00};

/// SPP Service - status characteristic, notify&read
static const uint16_t spp_status_uuid = ESP_GATT_UUID_SPP_COMMAND_NOTIFY;
static const uint8_t spp_status_val[10] = {0x00};
static const uint8_t spp_status_ccc[2] = {0x00, 0x00};

#ifdef SUPPORT_HEARTBEAT
/// SPP Server - Heart beat characteristic, notify&write&read
static const uint16_t spp_heart_beat_uuid = ESP_GATT_UUID_SPP_HEARTBEAT;
static const uint8_t spp_heart_beat_val[2] = {0x00, 0x00};
static const uint8_t spp_heart_beat_ccc[2] = {0x00, 0x00};
#endif

/// Full HRS Database Description - Used to add attributes into the database
static esp_gatts_attr_db_t const spp_gatt_db[SPP_IDX_NB] = {
    // SPP -  Service Declaration
    [SPP_IDX_SVC] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ,              // 服务的类型 0X2800
                               sizeof(spp_service_uuid), sizeof(spp_service_uuid), (uint8_t *)&spp_service_uuid}}, // 服务的ID

    // SPP -  data receive characteristic Declaration
    [SPP_IDX_SPP_DATA_RECV_CHAR] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,      // 特征值声明的类型 0X2803
                               CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}}, // 特征值属性

    // SPP -  data receive characteristic Value
    [SPP_IDX_SPP_DATA_RECV_VAL] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&spp_data_receive_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, // 特征值value的UUID
                               SPP_DATA_MAX_LEN, sizeof(spp_data_receive_val), (uint8_t *)spp_data_receive_val}},            // 对应的变量

    // SPP -  data notify characteristic Declaration
    [SPP_IDX_SPP_DATA_NOTIFY_CHAR] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,       // 特征值声明的类型 0X2803
                               CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_notify}}, // 特征值属性

    // SPP -  data notify characteristic Value
    [SPP_IDX_SPP_DATA_NTY_VAL] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&spp_data_notify_uuid, ESP_GATT_PERM_READ,           // 特征值声明的类型 0X2803
                               SPP_DATA_MAX_LEN, sizeof(spp_data_notify_val), (uint8_t *)spp_data_notify_val}}, // 对应的变量

    // SPP -  data notify characteristic - Client Characteristic Configuration Descriptor
    [SPP_IDX_SPP_DATA_NTF_CFG] =                                                                                                // Client characteristic configuration UUID 0X2902
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, // 访问权限
                           sizeof(uint16_t), sizeof(spp_data_notify_ccc), (uint8_t *)spp_data_notify_ccc}},

    // SPP -  command characteristic Declaration
    [SPP_IDX_SPP_COMMAND_CHAR] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, // 特征值声明的类型 0X2803
                               CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}},

    // SPP -  command characteristic Value
    [SPP_IDX_SPP_COMMAND_VAL] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&spp_command_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, // 特征值value的UUID
                               SPP_CMD_MAX_LEN, sizeof(spp_command_val), (uint8_t *)spp_command_val}},

    // SPP -  status characteristic Declaration
    [SPP_IDX_SPP_STATUS_CHAR] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, // 特征值声明的类型 0X2803
                               CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_notify}},

    // SPP -  status characteristic Value
    [SPP_IDX_SPP_STATUS_VAL] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&spp_status_uuid, ESP_GATT_PERM_READ, // 特征值value的UUID
                               SPP_STATUS_MAX_LEN, sizeof(spp_status_val), (uint8_t *)spp_status_val}},

    // SPP -  status characteristic - Client Characteristic Configuration Descriptor
    [SPP_IDX_SPP_STATUS_CFG] = // Client characteristic configuration UUID 0X2902
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, sizeof(uint16_t), sizeof(spp_status_ccc), (uint8_t *)spp_status_ccc}},

#ifdef SUPPORT_HEARTBEAT
    // SPP -  Heart beat characteristic Declaration
    [SPP_IDX_SPP_HEARTBEAT_CHAR] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, // 特征值声明的类型 0X2803
                               CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write_notify}},

    // SPP -  Heart beat characteristic Value
    [SPP_IDX_SPP_HEARTBEAT_VAL] =
        {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&spp_heart_beat_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, sizeof(spp_heart_beat_val), sizeof(spp_heart_beat_val), (uint8_t *)spp_heart_beat_val}},

    // SPP -  Heart beat characteristic - Client Characteristic Configuration Descriptor
    [SPP_IDX_SPP_HEARTBEAT_CFG] = // Client characteristic configuration UUID 0X2902
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, sizeof(uint16_t), sizeof(spp_data_notify_ccc), (uint8_t *)spp_heart_beat_ccc}},
#endif
};

static uint8_t find_char_and_desr_index(uint16_t handle)
{
    uint8_t error = 0xff;

    for (int i = 0; i < SPP_IDX_NB; i++)
    {
        if (handle == spp_handle_table[i])
        {
            return i;
        }
    }

    return error;
}

#ifdef SUPPORT_HEARTBEAT
void spp_heartbeat_task(void *arg)
{
    uint16_t cmd_id;

    for (;;)
    {
        vTaskDelay(50 / portTICK_PERIOD_MS);
        if (xQueueReceive(cmd_heartbeat_queue, &cmd_id, portMAX_DELAY))
        {
            while (1)
            {
                heartbeat_count_num++;
                vTaskDelay(5000 / portTICK_PERIOD_MS);
                if ((heartbeat_count_num > 3) && (is_connected))
                {
                    esp_ble_gap_disconnect(spp_remote_bda); // 主动断开连接
                }
                if (is_connected && enable_heart_ntf)
                {
                    esp_ble_gatts_send_indicate(spp_gatts_if, spp_conn_id, spp_handle_table[SPP_IDX_SPP_HEARTBEAT_VAL], sizeof(heartbeat_s), heartbeat_s, false);
                }
                else if (!is_connected)
                {
                    break;
                }
            }
        }
    }
    vTaskDelete(NULL);
}
#endif

static void spp_task_init(void)
{
#ifdef SUPPORT_HEARTBEAT
    if (cmd_heartbeat_queue == NULL)
    {
        cmd_heartbeat_queue = xQueueCreate(10, sizeof(uint32_t));
        xTaskCreate(spp_heartbeat_task, "spp_heartbeat_task", 2048, NULL, 10, NULL);
    }
#endif
}

static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
    esp_err_t err;
    SYS_LOG_DBG("GAP_EVT, event %d", event);

    switch (event)
    {
    case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
        SYS_LOG_INF("ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT");
        adv_config_done &= (~ADV_CONFIG_FLAG);
        if (adv_config_done == 0)
        {
            esp_ble_gap_start_advertising(&spp_adv_params);
        }
        break;
    case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
        SYS_LOG_INF("ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT");
        adv_config_done &= (~ADV_CONFIG_FLAG);
        if (adv_config_done == 0)
        {
            esp_ble_gap_start_advertising(&spp_adv_params);
        }
        break;
    case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
        SYS_LOG_INF("ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT");
        adv_config_done &= (~SCAN_RSP_CONFIG_FLAG);
        if (adv_config_done == 0)
        {
            esp_ble_gap_start_advertising(&spp_adv_params);
        }
        break;

    case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
        // advertising start complete event to indicate advertising start successfully or failed
        if ((err = param->adv_start_cmpl.status) != ESP_BT_STATUS_SUCCESS)
        {
            SYS_LOG_ERR("Advertising start failed: %s", esp_err_to_name(err));
        }
        break;
    default:
        break;
    }
}

static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
    esp_ble_gatts_cb_param_t *p_data = (esp_ble_gatts_cb_param_t *)param;
    uint8_t res = 0xff;

    SYS_LOG_DBG("event = %d", event);
    switch (event)
    {
    case ESP_GATTS_REG_EVT:
        SYS_LOG_INF("ESP_GATTS_REG_EVT: device_name = '%s'", s_init_param.device_name);
        esp_ble_gap_set_device_name(s_init_param.device_name);

        if (0)
        {
            /* 弃用 来自 demo: examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.c */
            esp_ble_gap_config_adv_data_raw((uint8_t *)spp_adv_data, sizeof(spp_adv_data));
        }
        else
        {
            esp_err_t ret = esp_ble_gap_config_adv_data(&adv_data);
            if (ret)
            {
                SYS_LOG_ERR("config adv data failed, error code = %d", ret);
            }
            adv_config_done |= ADV_CONFIG_FLAG;

            // config scan response data
            ret = esp_ble_gap_config_adv_data(&scan_rsp_data);
            if (ret)
            {
                SYS_LOG_ERR("config scan response data failed, error code = %d", ret);
            }
            adv_config_done |= SCAN_RSP_CONFIG_FLAG;
        }

        esp_ble_gatts_create_attr_tab(spp_gatt_db, gatts_if, SPP_IDX_NB, SPP_SVC_INST_ID);
        break;
    case ESP_GATTS_READ_EVT:
        res = find_char_and_desr_index(p_data->read.handle);
        if (res == SPP_IDX_SPP_STATUS_VAL)
        { // 读状态特征
          // TODO:client read the status characteristic
        }
        break;
    case ESP_GATTS_WRITE_EVT:
    {
        res = find_char_and_desr_index(p_data->write.handle); // 哪个特征值引发event
        if (p_data->write.is_prep == false)
        {
            if (res == SPP_IDX_SPP_COMMAND_VAL) // 收到命令
            {
                SYS_LOG_INF("ESP_GATTS_WRITE_EVT : do os_pipe_fifo_fill()");
                SYS_LOG_DUMP(p_data->write.value, p_data->write.len, 0, 0);
                __ble_port_data_t *port_data = s_port_cmd.data;
                size_t len = os_pipe_fifo_fill(&port_data->pipe_obj, p_data->write.value, p_data->write.len);
                if (len < p_data->write.len)
                {
                    SYS_LOG_WRN("Pipeline cache exceeded %d bytes", p_data->write.len - len);
                }
            }
            else if (res == SPP_IDX_SPP_DATA_NTF_CFG) // 收到客户端发过来的设置信息
            {
                if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x01) && (p_data->write.value[1] == 0x00))
                {
                    enable_data_ntf = true;
                }
                else if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x00) && (p_data->write.value[1] == 0x00))
                {
                    enable_data_ntf = false;
                }
            }
#ifdef SUPPORT_HEARTBEAT
            else if (res == SPP_IDX_SPP_HEARTBEAT_CFG) // 开启心跳
            {
                if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x01) && (p_data->write.value[1] == 0x00))
                {
                    enable_heart_ntf = true;
                }
                else if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x00) && (p_data->write.value[1] == 0x00))
                {
                    enable_heart_ntf = false;
                }
            }
            else if (res == SPP_IDX_SPP_HEARTBEAT_VAL)
            {
                if ((p_data->write.len == sizeof(heartbeat_s)) && (memcmp(heartbeat_s, p_data->write.value, sizeof(heartbeat_s)) == 0))
                {
                    heartbeat_count_num = 0;
                }
            }
#endif
            else if (res == SPP_IDX_SPP_DATA_RECV_VAL) // 收到客户端发过来的数据
            {
                SYS_LOG_INF("ESP_GATTS_WRITE_EVT : do os_pipe_fifo_fill()");
                SYS_LOG_DUMP(p_data->write.value, p_data->write.len, 0, 0);
                __ble_port_data_t *port_data = s_port_val.data;
                size_t len = os_pipe_fifo_fill(&port_data->pipe_obj, p_data->write.value, p_data->write.len);
                if (len < p_data->write.len)
                {
                    SYS_LOG_WRN("read Pipeline cache exceeded %d bytes", p_data->write.len - len);
                }
            }
            else
            {
                // TODO:
            }
        }
        else if ((p_data->write.is_prep == true) && (res == SPP_IDX_SPP_DATA_RECV_VAL))
        {
            SYS_LOG_INF("ESP_GATTS_WRITE_EVT : do os_fifo_put()");
            SYS_LOG_DUMP(p_data->write.value, p_data->write.len, 0, 0);

            if (s_val_fifo_cnt < _MAX_FIFO_ALLOC_CNT)
            {
                __ble_fifo_t *fifo = os_fifo_alloc(sizeof(__ble_fifo_t) + p_data->write.len);
                if (fifo)
                {
                    os_interrupt_disable();
                    s_val_fifo_cnt++;
                    os_interrupt_enable();

                    fifo->len = p_data->write.len;
                    memcpy(fifo->data, p_data->write.value, p_data->write.len);
                    os_fifo_put(&s_val_fifo_hdl, fifo);
                }
            }
            else
            {
                SYS_LOG_WRN("s_val_fifo_cnt: %d >= _MAX_FIFO_ALLOC_CNT: %d", s_val_fifo_cnt, _MAX_FIFO_ALLOC_CNT);
            }
        }
        break;
    }
    case ESP_GATTS_EXEC_WRITE_EVT: // 执行写
    {
        SYS_LOG_INF("ESP_GATTS_EXEC_WRITE_EVT: do os_fifo_take()");
        if (p_data->exec_write.exec_write_flag)
        {
            for (;;)
            {
                __ble_fifo_t *fifo = os_fifo_take(&s_val_fifo_hdl, 0);
                if (fifo)
                {
                    __ble_port_data_t *port_data = s_port_val.data;
                    size_t len = os_pipe_fifo_fill(&port_data->pipe_obj, fifo->data, fifo->len);
                    if (len < p_data->write.len)
                    {
                        SYS_LOG_WRN("write Pipeline cache exceeded %d bytes", p_data->write.len - len);
                    }
                    os_fifo_free(fifo);

                    os_interrupt_disable();
                    s_val_fifo_cnt--;
                    os_interrupt_enable();
                }
                else
                {
                    break;
                }
            }
        }
        break;
    }
    case ESP_GATTS_MTU_EVT:
        spp_mtu_size = p_data->mtu.mtu;
        SYS_LOG_WRN("spp_mtu_size set to %d", spp_mtu_size);
        break;
    case ESP_GATTS_CONF_EVT:
        break;
    case ESP_GATTS_UNREG_EVT:
        break;
    case ESP_GATTS_DELETE_EVT:
        break;
    case ESP_GATTS_START_EVT:
        break;
    case ESP_GATTS_STOP_EVT:
        break;
    case ESP_GATTS_CONNECT_EVT: // 客户端连接
        SYS_LOG_INF("ESP_GATTS_CONNECT_EVT");
        spp_conn_id = p_data->connect.conn_id;
        spp_gatts_if = gatts_if;
        is_connected = true;
        memcpy(&spp_remote_bda, &p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
#ifdef SUPPORT_HEARTBEAT
        uint16_t cmd = 0;
        xQueueSend(cmd_heartbeat_queue, &cmd, 10 / portTICK_PERIOD_MS);
#endif
        if (s_init_param.connect_cb)
        {
            s_init_param.connect_cb(ble_server_get_gap_status());
        }
        break;
    case ESP_GATTS_DISCONNECT_EVT: // 断开连接
        SYS_LOG_INF("ESP_GATTS_DISCONNECT_EVT");
        is_connected = false;
        enable_data_ntf = false;
        spp_mtu_size = 23;
#ifdef SUPPORT_HEARTBEAT
        enable_heart_ntf = false;
        heartbeat_count_num = 0;
#endif
        esp_ble_gap_start_advertising(&spp_adv_params); // 开启广播
        if (s_init_param.connect_cb)
        {
            s_init_param.connect_cb(ble_server_get_gap_status());
        }
        break;
    case ESP_GATTS_OPEN_EVT:
        break;
    case ESP_GATTS_CANCEL_OPEN_EVT:
        break;
    case ESP_GATTS_CLOSE_EVT:
        break;
    case ESP_GATTS_LISTEN_EVT:
        break;
    case ESP_GATTS_CONGEST_EVT:
        break;
    case ESP_GATTS_CREAT_ATTR_TAB_EVT:
    {
        SYS_LOG_DBG("The number handle = 0x%X", param->add_attr_tab.num_handle);
        if (param->add_attr_tab.status != ESP_GATT_OK)
        {
            SYS_LOG_ERR("Create attribute table failed, error code=0x%x", param->add_attr_tab.status);
        }
        else if (param->add_attr_tab.num_handle != SPP_IDX_NB)
        {
            SYS_LOG_ERR("Create attribute table abnormally, num_handle (%d) doesn't equal to HRS_IDX_NB(%d)", param->add_attr_tab.num_handle, SPP_IDX_NB);
        }
        else
        {
            memcpy(spp_handle_table, param->add_attr_tab.handles, sizeof(spp_handle_table));
            esp_ble_gatts_start_service(spp_handle_table[SPP_IDX_SVC]); // 开启服务
        }
        break;
    }
    default:
        break;
    }
}

static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
    SYS_LOG_DBG("EVT %d, gatts if %d", event, gatts_if);

    /* If event is register event, store the gatts_if for each profile */
    if (event == ESP_GATTS_REG_EVT)
    {
        if (param->reg.status == ESP_GATT_OK)
        {
            spp_profile_tab[SPP_PROFILE_APP_IDX].gatts_if = gatts_if;
        }
        else
        {
            SYS_LOG_DBG("Reg app failed, app_id %04x, status %d", param->reg.app_id, param->reg.status);
            return;
        }
    }

    do
    {
        int idx;
        for (idx = 0; idx < SPP_PROFILE_NUM; idx++)
        {
            if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
                gatts_if == spp_profile_tab[idx].gatts_if)
            {
                if (spp_profile_tab[idx].gatts_cb)
                {
                    spp_profile_tab[idx].gatts_cb(event, gatts_if, param); // 调用注册的回调函数
                }
            }
        }
    } while (0);
}

static void _ble_spp_server_init(void)
{
    esp_err_t ret;
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();

    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));

    ret = esp_bt_controller_init(&bt_cfg);
    if (ret)
    {
        SYS_LOG_ERR("enable controller failed: %s", esp_err_to_name(ret));
        return;
    }

    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
    if (ret)
    {
        SYS_LOG_ERR("enable controller failed: %s", esp_err_to_name(ret));
        return;
    }

    SYS_LOG_DBG("init bluetooth");
    esp_bluedroid_config_t bluedroid_cfg = BT_BLUEDROID_INIT_CONFIG_DEFAULT();
    ret = esp_bluedroid_init_with_cfg(&bluedroid_cfg);
    if (ret)
    {
        SYS_LOG_ERR("init bluetooth failed: %s", esp_err_to_name(ret));
        return;
    }
    ret = esp_bluedroid_enable();
    if (ret)
    {
        SYS_LOG_ERR("enable bluetooth failed: %s", esp_err_to_name(ret));
        return;
    }

    memcpy(s_mac, esp_bt_dev_get_address(), sizeof(s_mac));

    if (s_init_param.manufacturer_data)
    {
        s_init_param.manufacturer_data(s_manufacturer_data);
    }
    else
    {
        for (int i = 0; i < sizeof(s_manufacturer_data); i++)
        {
            s_manufacturer_data[i] = i + 1;
        }
    }

    esp_ble_gatts_register_callback(gatts_event_handler);
    esp_ble_gap_register_callback(gap_event_handler);
    esp_ble_gatts_app_register(ESP_SPP_APP_ID);

    spp_task_init();

    return;
}

/* port ------------------------------------------------------------------------------------------------------------*/

static int _port_start_cmd(sb_data_port_t *port);
static int _port_stop_cmd(sb_data_port_t *port);
static int _port_write_cmd(sb_data_port_t *port, const void *data, uint32_t size);
static int _port_read_cmd(sb_data_port_t *port, void *data, uint32_t size);
static bool _port_cmd_is_started(sb_data_port_t *port);
static uint32_t _port_cmd_get_rx_length(sb_data_port_t *port);

static int _port_start_val(sb_data_port_t *port);
static int _port_stop_val(sb_data_port_t *port);
static int _port_write_val(sb_data_port_t *port, const void *data, uint32_t size);
static int _port_read_val(sb_data_port_t *port, void *data, uint32_t size);
static bool _port_val_is_started(sb_data_port_t *port);
static uint32_t _port_val_get_rx_length(sb_data_port_t *port);

static sb_data_port_vtable_t const s_port_vtable_cmd = {
    .start = _port_start_cmd,                 // 由具体驱动实现的，以达到节省资源为主要目的，启动数据接口
    .stop = _port_stop_cmd,                   // 由具体驱动实现的，以达到节省资源为主要目的，关闭数据接口
    .write = _port_write_cmd,                 // 由具体驱动实现的，写数据到对应的接口。
    .read = _port_read_cmd,                   // 由具体驱动实现的，从数据接口中读取已缓存的数据。
    .is_started = _port_cmd_is_started,       // 由具体驱动实现的，获取当前数据接口是否可用（是否已启动）
    .get_rx_length = _port_cmd_get_rx_length, // 由具体驱动实现的，获取当前数据接口的本次可读长度
};

static sb_data_port_vtable_t const s_port_vtable_val = {
    .start = _port_start_val,                 // 由具体驱动实现的，以达到节省资源为主要目的，启动数据接口
    .stop = _port_stop_val,                   // 由具体驱动实现的，以达到节省资源为主要目的，关闭数据接口
    .write = _port_write_val,                 // 由具体驱动实现的，写数据到对应的接口。
    .read = _port_read_val,                   // 由具体驱动实现的，从数据接口中读取已缓存的数据。
    .is_started = _port_val_is_started,       // 由具体驱动实现的，获取当前数据接口是否可用（是否已启动）
    .get_rx_length = _port_val_get_rx_length, // 由具体驱动实现的，获取当前数据接口的本次可读长度
};

static int _port_start_cmd(sb_data_port_t *port)
{
    ble_server_status_t status = ble_server_get_gap_status();

    do
    {
        /* port_data->pipe_obj */
        __ble_port_data_t *port_data = s_port_cmd.data;
        if (!os_pipe_is_valid(&port_data->pipe_obj))
        {
            os_pipe_create(&port_data->pipe_obj, port_data->buffer_size);
            os_pipe_regist(&port_data->pipe_obj, port_data->wkup_work_hdl, 0);
        }

    } while (0);

    if (status == BLE_SERVER_STATUS_STOP)
    {
        _ble_spp_server_init();

        if (s_init_param.connect_cb)
        {
            s_init_param.connect_cb(ble_server_get_gap_status());
        }
    }

    return 0;
}

static int _port_stop_cmd(sb_data_port_t *port)
{
    bool deinit_flag = false;

    if (!_port_cmd_is_started(&s_port_val))
    {
        deinit_flag = true;
    }

    if (deinit_flag)
    {
        if (is_connected)
        {
            esp_ble_gap_disconnect(spp_remote_bda);
            is_connected = false;
        }

        esp_bluedroid_disable();
        esp_bluedroid_deinit();
        esp_bt_controller_disable();
        esp_bt_controller_deinit();
    }

    do
    {
        /* port_data->pipe_obj */
        __ble_port_data_t *port_data = s_port_cmd.data;
        if (os_pipe_is_valid(&port_data->pipe_obj))
        {
            os_pipe_delete(&port_data->pipe_obj);
        }

    } while (0);

    if (deinit_flag)
    {
        if (s_init_param.connect_cb)
        {
            s_init_param.connect_cb(ble_server_get_gap_status());
        }
    }

    return 0;
}

static int _port_write_cmd(sb_data_port_t *port, const void *data, uint32_t size)
{
    int wtotal = size;
    while (size)
    {
        int wsize = size;
        if (wsize > spp_mtu_size - SPP_MTU_HEAD_SIZE)
        {
            wsize = spp_mtu_size - SPP_MTU_HEAD_SIZE;
        }

        if (is_connected)
        {
            if (esp_ble_gatts_send_indicate(spp_gatts_if,
                                            spp_conn_id,
                                            spp_handle_table[SPP_IDX_SPP_STATUS_VAL],
                                            wsize,
                                            (uint8_t *)data,
                                            false) != ESP_OK)
            {
                SYS_LOG_WRN("esp_ble_gatts_send_indicate() fail");
                return -1;
            }
        }
        else
        {
            return -1;
        }

        size -= wsize;
        data = &((uint8_t *)data)[wsize];
    }
    return wtotal;
}

static int _port_read_cmd(sb_data_port_t *port, void *data, uint32_t size)
{
    __ble_port_data_t *port_data = port->data;
    return os_pipe_fifo_read(&port_data->pipe_obj, data, size);
}

static bool _port_cmd_is_started(sb_data_port_t *port)
{
    __ble_port_data_t *port_data = s_port_cmd.data;
    if (port_data == NULL)
    {
        return false;
    }
    return os_pipe_is_valid(&port_data->pipe_obj);
}

static uint32_t _port_cmd_get_rx_length(sb_data_port_t *port)
{
    __ble_port_data_t *port_data = port->data;
    return os_pipe_get_valid_size(&port_data->pipe_obj);
}

static int _port_start_val(sb_data_port_t *port)
{
    ble_server_status_t status = ble_server_get_gap_status();

    do
    {
        /* 设置数据通道 FIFO */
        if (!os_fifo_q_is_valid(&s_val_fifo_hdl))
        {
            s_val_fifo_cnt = 0;
            os_fifo_q_create(&s_val_fifo_hdl);
        }

        /* port_data->pipe_obj */
        __ble_port_data_t *port_data = s_port_val.data;
        if (!os_pipe_is_valid(&port_data->pipe_obj))
        {
            os_pipe_create(&port_data->pipe_obj, port_data->buffer_size);
            os_pipe_regist(&port_data->pipe_obj, port_data->wkup_work_hdl, 0);
        }

    } while (0);

    if (status == BLE_SERVER_STATUS_STOP)
    {
        _ble_spp_server_init();

        if (s_init_param.connect_cb)
        {
            s_init_param.connect_cb(ble_server_get_gap_status());
        }
    }

    return 0;
}

static int _port_stop_val(sb_data_port_t *port)
{
    bool deinit_flag = false;

    if (!_port_cmd_is_started(&s_port_cmd))
    {
        deinit_flag = true;
    }

    if (deinit_flag)
    {
        if (is_connected)
        {
            esp_ble_gap_disconnect(spp_remote_bda);
            is_connected = false;
        }

        esp_bluedroid_disable();
        esp_bluedroid_deinit();
        esp_bt_controller_disable();
        esp_bt_controller_deinit();
    }

    do
    {
        /* 设置数据通道 FIFO */
        if (os_fifo_q_is_valid(&s_val_fifo_hdl))
        {
            os_fifo_q_delete(&s_val_fifo_hdl);
        }

        /* port_data->pipe_obj */
        __ble_port_data_t *port_data = s_port_val.data;
        if (os_pipe_is_valid(&port_data->pipe_obj))
        {
            os_pipe_delete(&port_data->pipe_obj);
        }

    } while (0);

    if (deinit_flag)
    {
        if (s_init_param.connect_cb)
        {
            s_init_param.connect_cb(ble_server_get_gap_status());
        }
    }

    return 0;
}

static int _port_write_val(sb_data_port_t *port, const void *data, uint32_t size)
{
    int wtotal = size;

    if (is_connected)
    {
        while (size)
        {
            uint32_t wsize = size;
            if (wsize > spp_mtu_size - SPP_MTU_HEAD_SIZE)
            {
                wsize = spp_mtu_size - SPP_MTU_HEAD_SIZE;
            }

            if (is_connected)
            {
                if (esp_ble_gatts_send_indicate(spp_gatts_if,
                                                spp_conn_id,
                                                spp_handle_table[SPP_IDX_SPP_DATA_NTY_VAL],
                                                wsize,
                                                (uint8_t *)data,
                                                false) != ESP_OK)
                {
                    SYS_LOG_WRN("esp_ble_gatts_send_indicate() fail");
                    return -1;
                }
            }
            else
            {
                return -1;
            }

            size -= wsize;
            data = &((uint8_t *)data)[wsize];
        }
    }

    return wtotal;
}

static int _port_read_val(sb_data_port_t *port, void *data, uint32_t size)
{
    if (data == NULL) // 值为 NULL 表示仅释放空间
    {
        os_fifo_q_clr(&s_val_fifo_hdl);
        os_interrupt_disable();
        s_val_fifo_cnt = 0;
        os_interrupt_enable();
    }
    __ble_port_data_t *port_data = port->data;
    return os_pipe_fifo_read(&port_data->pipe_obj, data, size);
}

static bool _port_val_is_started(sb_data_port_t *port)
{
    __ble_port_data_t *port_data = s_port_val.data;
    if (port_data == NULL)
    {
        return false;
    }
    return os_pipe_is_valid(&port_data->pipe_obj);
}

static uint32_t _port_val_get_rx_length(sb_data_port_t *port)
{
    __ble_port_data_t *port_data = port->data;
    return os_pipe_get_valid_size(&port_data->pipe_obj);
}

/**
 * @brief 初始化数据接口
 *
 * @param init_param 初始化参数
 * @return 0
 */
int sb_ble_server_port_init(ble_server_init_t *init_param)
{
    /* 设置 s_init_param.device_name */
    s_init_param.device_name[sizeof(s_init_param.device_name) - 1] = '\0';
    strncpy(s_init_param.device_name, init_param->device_name, sizeof(s_init_param.device_name) - 1);

    /* 设置剩余的初始化参数 */
    s_init_param.manufacturer_data = init_param->manufacturer_data;
    s_init_param.connect_cb = init_param->connect_cb;

    return 0;
}

/**
 * @brief
 *
 * @param buffer_size   指定接收缓存长度
 * @param wkup_work_hdl 当接收非空时唤醒的工作项， NULL 值可用
 * @return sb_data_port_t*
 */
sb_data_port_t *ble_server_port_bind_cmd(uint32_t buffer_size, os_work_t *wkup_work_hdl)
{
    static __ble_port_data_t s_cmd_data;

    SYS_ASSERT(s_port_cmd.data == NULL, "The interface has already been bound");

    __ble_port_data_t *port_data = &s_cmd_data;
    port_data->buffer_size = buffer_size;
    port_data->wkup_work_hdl = wkup_work_hdl;

    s_port_cmd.vtable = &s_port_vtable_cmd;
    s_port_cmd.data = &s_cmd_data;

    return &s_port_cmd;
}

/**
 * @brief
 *
 * @param buffer_size   指定接收缓存长度
 * @param wkup_work_hdl 当接收非空时唤醒的工作项， NULL 值可用
 * @return sb_data_port_t*
 */
sb_data_port_t *ble_server_port_bind_val(uint32_t buffer_size, os_work_t *wkup_work_hdl)
{
    static __ble_port_data_t s_val_data;

    SYS_ASSERT(s_port_val.data == NULL, "The interface has already been bound");

    __ble_port_data_t *port_data = &s_val_data;
    port_data->buffer_size = buffer_size;
    port_data->wkup_work_hdl = wkup_work_hdl;

    s_port_val.vtable = &s_port_vtable_val;
    s_port_val.data = &s_val_data;

    return &s_port_val;
}

void ble_server_port_unbind_cmd(sb_data_port_t *port)
{
    if (_port_cmd_is_started(port))
    {
        _port_stop_cmd(port);
    }
    s_port_cmd.data = NULL;
}

void ble_server_port_unbind_val(sb_data_port_t *port)
{
    if (_port_val_is_started(port))
    {
        _port_stop_val(port);
    }
    s_port_val.data = NULL;
}

/**
 * @brief 获取当前的链接状态
 *
 * @return ble_server_status_t
 */
ble_server_status_t ble_server_get_gap_status(void)
{
    if (!_port_cmd_is_started(&s_port_cmd) && !_port_cmd_is_started(&s_port_val))
    {
        return BLE_SERVER_STATUS_STOP;
    }

    if (is_connected)
    {
        return BLE_SERVER_STATUS_CONNECTED;
    }

    return BLE_SERVER_STATUS_DISCONNECTED;
}

/**
 * @brief 获取 MTU
 *
 * @return uint32_t
 */
uint32_t ble_server_get_mtu(void)
{
    if (!is_connected)
    {
        return 0;
    }

    return spp_mtu_size - SPP_MTU_HEAD_SIZE;
}

/**
 * @brief 获取 MAC 地址
 *
 * @return const uint8_t MAC[6]
 */
const uint8_t *ble_server_get_mac(void)
{
    return s_mac;
}

/**
 * @brief 获取远程设备的 MAC 地址
 *
 * @return const uint8_t* MAC[6]
 */
void ble_server_get_remote_mac(uint8_t *mac)
{
    if (mac == NULL)
    {
        return;
    }
    memcpy(mac, spp_remote_bda, sizeof(esp_bd_addr_t));
}
