/*
 * 自然写智能点阵笔嵌入式固件软件 V1.0
 * ble_send_task.c - BLE数据发送任务
 *
 * 功能说明：
 * 1. 从坐标队列获取数据并打包为BLE通知帧
 * 2. 7字节紧凑坐标编码格式
 * 3. 发送速率控制（适配BLE连接间隔）
 * 4. 笔落下/抬起事件通知
 * 5. 设备信息特征值更新（电量/固件版本）
 */

#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "event_groups.h"

#include "ble_gatt_server.h"

/* ========== BLE帧格式定义 ========== */

/* 帧头标识 */
#define BLE_FRAME_HEADER        0xAA55

/* 帧类型 */
#define FRAME_TYPE_COORDINATE   0x00    /* 坐标数据帧 */
#define FRAME_TYPE_PEN_DOWN     0x01    /* 笔落下事件 */
#define FRAME_TYPE_PEN_UP       0x02    /* 笔抬起事件 */
#define FRAME_TYPE_DEVICE_INFO  0x03    /* 设备信息帧 */
#define FRAME_TYPE_PAGE_CHANGE  0x04    /* 翻页事件 */

/* 最大BLE MTU通知载荷 */
#define BLE_MAX_NOTIFY_SIZE     20

/* 批量发送缓冲区大小（可打包多个坐标点） */
#define BATCH_BUFFER_SIZE       3

/* ========== 外部引用 ========== */

extern QueueHandle_t g_coordinate_queue;
extern EventGroupHandle_t g_ble_event_group;
extern SemaphoreHandle_t g_system_mutex;

/* 坐标数据包结构 */
typedef struct {
    uint32_t raw_x;
    uint32_t raw_y;
    uint16_t pressure;
    uint32_t timestamp_ms;
    uint32_t page_id;
    uint8_t  pen_state;
} CoordinatePacket;

/* ========== 静态变量 ========== */

/* 发送缓冲区 */
static uint8_t s_send_buffer[BLE_MAX_NOTIFY_SIZE];

/* BLE连接状态 */
static volatile bool s_ble_connected = false;

/* 当前页面ID（检测翻页） */
static uint32_t s_current_page_id = 0;

/* 发送统计 */
static uint32_t s_total_sent = 0;
static uint32_t s_send_failures = 0;

/* ========== CRC-16 CCITT计算 ========== */

/**
 * CRC-16 CCITT校验计算
 * 用于BLE传输数据帧的完整性校验
 *
 * @param data    数据缓冲区
 * @param length  数据长度
 * @return CRC-16校验值
 */
static uint16_t crc16_ccitt(const uint8_t *data, uint16_t length) {
    uint16_t crc = 0xFFFF;
    uint16_t i;

    for (i = 0; i < length; i++) {
        crc ^= (uint16_t)data[i] << 8;
        uint8_t j;
        for (j = 0; j < 8; j++) {
            if (crc & 0x8000) {
                crc = (crc << 1) ^ 0x1021;
            } else {
                crc <<= 1;
            }
        }
    }

    return crc;
}

/* ========== 坐标编码 ========== */

/**
 * 将坐标数据编码为7字节紧凑格式
 *
 * 编码格式：
 *   字节0-1: X坐标高16位（大端序）
 *   字节2-3: Y坐标高16位
 *   字节4:   X低4位(高半字节) | Y低4位(低半字节)
 *   字节5:   压力值高8位
 *   字节6:   压力值低4位(高半字节) | 标志位(低半字节)
 *
 * @param packet    坐标数据包
 * @param output    输出缓冲区（至少7字节）
 * @param flags     标志位（低2位：00=数据, 01=笔落下, 02=笔抬起）
 */
static void encode_coordinate(const CoordinatePacket *packet, uint8_t *output,
                               uint8_t flags) {
    /* X坐标（20位精度） */
    uint32_t x = packet->raw_x & 0xFFFFF;
    output[0] = (uint8_t)((x >> 12) & 0xFF);    /* X高8位 */
    output[1] = (uint8_t)((x >> 4) & 0xFF);     /* X次高8位 */

    /* Y坐标（20位精度） */
    uint32_t y = packet->raw_y & 0xFFFFF;
    output[2] = (uint8_t)((y >> 12) & 0xFF);    /* Y高8位 */
    output[3] = (uint8_t)((y >> 4) & 0xFF);     /* Y次高8位 */

    /* X低4位和Y低4位合并到一个字节 */
    output[4] = (uint8_t)(((x & 0x0F) << 4) | (y & 0x0F));

    /* 压力值（12位精度） */
    uint16_t p = packet->pressure & 0x0FFF;
    output[5] = (uint8_t)((p >> 4) & 0xFF);     /* 压力高8位 */

    /* 压力低4位 | 标志位 */
    output[6] = (uint8_t)(((p & 0x0F) << 4) | (flags & 0x0F));
}

/* ========== BLE通知发送 ========== */

/**
 * 通过BLE GATT通知发送数据帧
 *
 * @param data    帧数据
 * @param length  帧长度
 * @return 0成功, -1未连接, -2发送失败
 */
static int ble_send_notification(const uint8_t *data, uint16_t length) {
    if (!s_ble_connected) {
        return -1;
    }

    /* 调用BLE GATT服务器发送通知 */
    int ret = ble_gatt_notify(BLE_CHAR_STROKE_DATA, data, length);

    if (ret == 0) {
        s_total_sent++;
    } else {
        s_send_failures++;
    }

    return ret;
}

/**
 * 发送笔状态事件（落下/抬起）
 */
static void send_pen_event(uint8_t event_type) {
    uint8_t frame[7];
    memset(frame, 0, sizeof(frame));

    /* 事件帧只需要标志位，坐标和压力都为0 */
    frame[6] = event_type & 0x0F;

    ble_send_notification(frame, 7);
}

/**
 * 发送翻页事件
 * 当检测到坐标所在页面发生变化时通知上位机
 */
static void send_page_change_event(uint32_t new_page_id) {
    uint8_t frame[8];

    frame[0] = FRAME_TYPE_PAGE_CHANGE;
    frame[1] = (uint8_t)((new_page_id >> 24) & 0xFF);
    frame[2] = (uint8_t)((new_page_id >> 16) & 0xFF);
    frame[3] = (uint8_t)((new_page_id >> 8) & 0xFF);
    frame[4] = (uint8_t)(new_page_id & 0xFF);

    /* CRC校验 */
    uint16_t crc = crc16_ccitt(frame, 5);
    frame[5] = (uint8_t)((crc >> 8) & 0xFF);
    frame[6] = (uint8_t)(crc & 0xFF);
    frame[7] = 0;

    ble_send_notification(frame, 8);
}

/**
 * 更新设备信息特征值（电量、固件版本等）
 * 上位机可以随时读取此特征值获取笔的状态
 */
static void update_device_info(uint8_t battery_percent) {
    uint8_t info[4];
    info[0] = battery_percent;         /* 电量百分比 */
    info[1] = 2;                       /* 固件主版本号 */
    info[2] = 1;                       /* 固件次版本号 */
    info[3] = 5;                       /* 固件补丁版本号 → V2.1.5 */

    ble_gatt_update_characteristic(BLE_CHAR_DEVICE_INFO, info, sizeof(info));
}

/* ========== BLE连接事件回调 ========== */

/**
 * BLE连接建立回调（由BLE协议栈调用）
 */
void on_ble_connected(void) {
    s_ble_connected = true;

    BaseType_t higher_priority_woken = pdFALSE;
    xEventGroupSetBitsFromISR(g_ble_event_group, EVT_BLE_CONNECTED,
                               &higher_priority_woken);
    portYIELD_FROM_ISR(higher_priority_woken);
}

/**
 * BLE连接断开回调
 */
void on_ble_disconnected(void) {
    s_ble_connected = false;

    BaseType_t higher_priority_woken = pdFALSE;
    xEventGroupSetBitsFromISR(g_ble_event_group, EVT_BLE_DISCONNECTED,
                               &higher_priority_woken);
    portYIELD_FROM_ISR(higher_priority_woken);
}

/* ========== BLE发送主任务 ========== */

/**
 * BLE发送任务（FreeRTOS任务函数）
 *
 * 运行流程：
 * 1. 等待BLE连接建立
 * 2. 监听笔状态事件（落下/抬起）并发送事件通知
 * 3. 从坐标队列读取数据，编码为7字节格式发送
 * 4. 翻页检测与通知
 * 5. 定期更新设备信息特征值
 */
void ble_send_task(void *pvParameters) {
    (void)pvParameters;

    CoordinatePacket packet;
    uint32_t info_update_counter = 0;

    /* 注册BLE连接回调 */
    ble_gatt_register_connect_callback(on_ble_connected);
    ble_gatt_register_disconnect_callback(on_ble_disconnected);

    /* 启动BLE广播 */
    ble_gatt_start_advertising();

    while (1) {
        /* 等待BLE连接 */
        if (!s_ble_connected) {
            xEventGroupWaitBits(g_ble_event_group, EVT_BLE_CONNECTED,
                                pdTRUE, pdFALSE, portMAX_DELAY);
        }

        /* 检查笔状态事件 */
        EventBits_t events = xEventGroupGetBits(g_ble_event_group);

        if (events & EVT_PEN_DOWN) {
            xEventGroupClearBits(g_ble_event_group, EVT_PEN_DOWN);
            send_pen_event(FRAME_TYPE_PEN_DOWN);
        }

        if (events & EVT_PEN_UP) {
            xEventGroupClearBits(g_ble_event_group, EVT_PEN_UP);
            send_pen_event(FRAME_TYPE_PEN_UP);
        }

        /* 从坐标队列读取数据（超时10ms，避免永久阻塞） */
        if (xQueueReceive(g_coordinate_queue, &packet, pdMS_TO_TICKS(10)) == pdTRUE) {
            /* 翻页检测 */
            if (packet.page_id != s_current_page_id && s_current_page_id != 0) {
                send_page_change_event(packet.page_id);
            }
            s_current_page_id = packet.page_id;

            /* 编码并发送坐标 */
            uint8_t encoded[7];
            encode_coordinate(&packet, encoded, FRAME_TYPE_COORDINATE);
            ble_send_notification(encoded, 7);
        }

        /* 每500次循环更新一次设备信息（约每5秒） */
        info_update_counter++;
        if (info_update_counter >= 500) {
            info_update_counter = 0;
            /* 读取当前电量 */
            extern uint8_t power_get_battery_percent(void);
            uint8_t battery = power_get_battery_percent();
            update_device_info(battery);
        }
    }
}
