#include "ble_spp_server_shell.h"
#include "ble_spp_server.h"
#include "shell/sh.h"

#include <string.h>
#include "os/os.h"

#include "drivers/data_port/sb_data_port.h"

#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF
#define SYS_LOG_DOMAIN "SH"
#include "sys_log.h"

static void _sb_manufacturer_encode(uint8_t manufacturer_data[20]);
static void _ble_connect_cb(ble_server_status_t status);
static void _work_speed_handler(void *arg);
static void _work_read_handler(void *arg);
static int _write_process(sb_data_port_t *data_port, uint32_t size);

static struct
{
    sb_data_port_t *port_ble_cmd;
    sb_data_port_t *port_ble_val;
    os_work_t s_work_hdl_cmd;
    os_work_t s_work_hdl_val;
    os_work_t s_speed_work_hdl;
    int s_size_total;
    int s_recv_total_cmd;
    int s_recv_total_val;
} s_cm;

SH_CMD_FN(_start);
SH_CMD_FN(_stop);
SH_CMD_FN(_status);
SH_CMD_FN(_write_cmd);
SH_CMD_FN(_write_val);

SH_DEF_SUB_CMD(
    sub_ble,
    SH_SETUP_CMD("start", "开始广播 <设备名>", _start, NULL),                        //
    SH_SETUP_CMD("stop", "断开连接并停止广播", _stop, NULL),                         //
    SH_SETUP_CMD("status", "获取当前连接状态", _status, NULL),                       //
    SH_SETUP_CMD("write-cmd", "发送数据到命令通道 <数值|字符串>", _write_cmd, NULL), //
    SH_SETUP_CMD("write-val", "发送数据到数据通道 <数值|字符串>", _write_val, NULL), //
);

SH_DEF_CMD(
    _register_cmd_ble,
    SH_SETUP_CMD("ble", "操作 BLE 透传数据接口", NULL, sub_ble), //
);

void ble_server_shell_register(void)
{
    sh_register_cmd(&_register_cmd_ble);

    ble_server_init_t ble_init_param = {
        .device_name = "12345",                       // 设备名
        .manufacturer_data = _sb_manufacturer_encode, // 回调函数，生成 manufacturer[20]
        .connect_cb = _ble_connect_cb,                // 回调函数， NULL 值可用
    };
    sb_ble_server_port_init(&ble_init_param);
    s_cm.port_ble_cmd = ble_server_port_bind_cmd(0x2000, &s_cm.s_work_hdl_cmd);
    s_cm.port_ble_val = ble_server_port_bind_val(0x400, &s_cm.s_work_hdl_val);

    sb_data_port_start(s_cm.port_ble_cmd);
    sb_data_port_start(s_cm.port_ble_val);
    // ble cmd通道读取
    os_work_create(&s_cm.s_work_hdl_cmd, "work-read", _work_read_handler, s_cm.port_ble_cmd, OS_PRIORITY_LOWEST);
    os_work_submit(default_os_work_q_hdl, &s_cm.s_work_hdl_cmd, 0);
    // ble 数据通道读取
    os_work_create(&s_cm.s_work_hdl_val, "work-read", _work_read_handler, s_cm.port_ble_val, OS_PRIORITY_LOWEST);
    os_work_submit(default_os_work_q_hdl, &s_cm.s_work_hdl_val, 0);
    // 速度
    os_work_create(&s_cm.s_speed_work_hdl, "work-speed", _work_speed_handler, NULL, OS_PRIORITY_LOWEST);
    os_work_submit(default_os_work_q_hdl, &s_cm.s_speed_work_hdl, 0);
}

void ble_server_shell_unregister(void)
{
    sh_unregister_cmd(&_register_cmd_ble);
}

static void _sb_manufacturer_encode(uint8_t manufacturer_data[20])
{
    for (int i = 0; i < 20; i++)
    {
        manufacturer_data[i] = i + 1;
    }
}

static void _ble_connect_cb(ble_server_status_t status)
{
    sh_echo(&g_uart_handle_vt100, "\r\n");
    _status(&g_uart_handle_vt100, 0, NULL);
}

static void _work_speed_handler(void *arg)
{
    static os_time_t last_time;
    os_time_t curr_time = os_get_sys_time();
    int time_cost = curr_time - last_time;

    os_work_later(1000);

    time_cost += !time_cost;
    if (sb_data_port_is_started(s_cm.port_ble_cmd))
    {
        if (s_cm.s_size_total)
        {
            int result = s_cm.s_size_total * 1000 / time_cost;
            s_cm.s_recv_total_cmd += s_cm.s_size_total;
            SYS_LOG_INF("cmd trans speed: %2u.%02u KiB/S, recv total: %d bytes", result / 0x400, result % 0x400 * 100 / 0x400, s_cm.s_recv_total_cmd);
        }
        else
        {
            s_cm.s_recv_total_cmd = 0;
        }
        s_cm.s_size_total = 0;
    }

    if (sb_data_port_is_started(s_cm.port_ble_val))
    {
        if (s_cm.s_size_total)
        {
            int result = s_cm.s_size_total * 1000 / time_cost;
            s_cm.s_recv_total_val += s_cm.s_size_total;
            SYS_LOG_INF("val trans speed: %2u.%02u KiB/S, recv total: %d bytes", result / 0x400, result % 0x400 * 100 / 0x400, s_cm.s_recv_total_val);
        }
        else
        {
            s_cm.s_recv_total_val = 0;
        }
        s_cm.s_size_total = 0;
    }

    last_time = curr_time;
}

static void _work_read_handler(void *arg)
{
    static uint8_t val_log = '0';

    while (sb_data_port_is_started(arg))
    {
        uint8_t buf[0x100];
        int r_size = sb_data_port_read(arg, buf, sizeof(buf), 0);
        if (r_size <= 0)
        {
            break;
        }
        else
        {
            s_cm.s_size_total += r_size;
            for (int i = 0; i < r_size; i++)
            {
                if (buf[i] != val_log)
                {
                    SYS_LOG_WRN("buf[i]: %d != val_log: %d", buf[i], val_log);
                    val_log = buf[i];
                }
                val_log = '0' + (val_log - '0' + 1) % 10;
            }
        }
    }
}

SH_CMD_FN(_start)
{
    if (argc < 1)
    {
        sh_echo(sh_hdl, "缺少参数\r\n");
        return -1;
    }

    if (argc > 1)
    {
        sh_echo(sh_hdl, "参数中有空格。如果目标设备名中包含空格，请使用引号表示\r\n");
        return -1;
    }

    sb_data_port_start(s_cm.port_ble_cmd);
    sb_data_port_start(s_cm.port_ble_val);
    return 0;
}

SH_CMD_FN(_stop)
{
    sb_data_port_stop(s_cm.port_ble_cmd);
    sb_data_port_stop(s_cm.port_ble_val);
    os_thread_sleep(100);
    return 0;
}

SH_CMD_FN(_status)
{
    static char *const str[] = {
        [BLE_SERVER_STATUS_STOP] = "stop",
        [BLE_SERVER_STATUS_CONNECTED] = "connected",
        [BLE_SERVER_STATUS_DISCONNECTED] = "disconnected",
    };
    ble_server_status_t status = ble_server_get_gap_status();
    if (status < sizeof(str) / sizeof(str[0]) && str[status] != NULL)
    {
        sh_echo(sh_hdl, "connect status: %s\r\n", str[status]);
    }
    else
    {
        sh_echo(sh_hdl, "unknow status: %d\r\n", status);
    }
    return 0;
}

SH_CMD_FN(_write_cmd)
{
    int ret = -1;
    if (argc < 1)
    {
        sh_echo(sh_hdl, "缺少参数\r\n");
        return -1;
    }

    sh_parse_t pv = sh_parse_value(argv[0]);
    switch (pv.type)
    {
    case _PARSE_TYPE_STRING: // 字符串
        ret = sb_data_port_write(s_cm.port_ble_cmd, pv.value.val_string, strlen(pv.value.val_string), 0);
        break;
    case _PARSE_TYPE_INTEGER: // 带符号整型
        ret = sb_data_port_write(s_cm.port_ble_cmd, &pv.value.val_integer, sizeof(pv.value.val_integer), 0);
        break;
    case _PARSE_TYPE_UNSIGNED: // 无符号整型
    {
        ret = _write_process(s_cm.port_ble_cmd, pv.value.val_unsigned);
        break;
    }
    case _PARSE_TYPE_FLOAT: // 浮点
        ret = sb_data_port_write(s_cm.port_ble_cmd, &pv.value.val_float, sizeof(pv.value.val_float), 0);
        break;

    default:
        return -1;
    }

    os_thread_sleep(50);
    return ret;
}

SH_CMD_FN(_write_val)
{
    int ret = -1;
    if (argc < 1)
    {
        sh_echo(sh_hdl, "缺少参数\r\n");
        return -1;
    }

    sh_parse_t pv = sh_parse_value(argv[0]);
    switch (pv.type)
    {
    case _PARSE_TYPE_STRING: // 字符串
        ret = sb_data_port_write(s_cm.port_ble_val, pv.value.val_string, strlen(pv.value.val_string), 0);
        break;
    case _PARSE_TYPE_INTEGER: // 带符号整型
        ret = sb_data_port_write(s_cm.port_ble_val, &pv.value.val_integer, sizeof(pv.value.val_integer), 0);
        break;
    case _PARSE_TYPE_UNSIGNED: // 无符号整型
    {
        ret = _write_process(s_cm.port_ble_val, pv.value.val_unsigned);
        break;
    }
    case _PARSE_TYPE_FLOAT: // 浮点
        ret = sb_data_port_write(s_cm.port_ble_val, &pv.value.val_float, sizeof(pv.value.val_float), 0);
        break;

    default:
        return -1;
    }

    os_thread_sleep(50);
    return ret;
}

static int _write_process(sb_data_port_t *data_port, uint32_t size)
{
    int ret = -1;
    char start_char = '0';
    int w_total = 0;

    while (size)
    {
        char buf[0x200];
        int mtu = ble_server_get_mtu();
        int w_size = size;

        if (w_size > mtu)
        {
            w_size = mtu;
        }

        for (int i = 0; i < w_size; i++)
        {
            buf[i] = start_char;
            start_char = '0' + (start_char - '0' + 1) % 10;
        }

        ret = sb_data_port_write(data_port, &buf, w_size, 0);
        if (ret <= 0)
        {
            for (int r = 0; r < 3; r++)
            {
                ret = sb_data_port_write(data_port, &buf, w_size, 0);
                SYS_LOG_WRN("write success = %u, retry %d", w_total, r + 1);
                if (ret > 0)
                {
                    break;
                }
                os_thread_sleep(50 * (r + 1));
            }
            if (ret <= 0)
            {
                break;
            }
        }
        else
        {
            os_thread_sleep(20);
        }

        size -= w_size;
        w_total += w_size;
    }

    return ret;
}