#include "link_dma_ble.h"

#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"

#include <stdlib.h>
#include <string.h>

static const char *tag_s = "LINK_DMA_BLE";

#define BLE_DMA_RX_QUEUE_DEPTH   24
#define BLE_DMA_TX_QUEUE_DEPTH   48
#define BLE_DMA_RX_MAX_BYTES     512
#define BLE_DMA_TX_MAX_BYTES     256

typedef struct {
    link_dma_ble_ch_t ch;
    uint16_t len;
    uint8_t data[BLE_DMA_RX_MAX_BYTES];
} ble_dma_rx_item_t;

typedef struct {
    uint16_t attr_handle;
    uint16_t len;
    uint8_t *data;
} ble_dma_tx_item_t;

static QueueHandle_t rx_q_s;
static QueueHandle_t tx_q_s;
static TaskHandle_t rx_task_s;
static TaskHandle_t tx_task_s;
static link_dma_ble_rx_cb_t rx_cb_s;
static void *rx_ctx_s;
static link_dma_ble_tx_fn_t tx_fn_s;
static void *tx_ctx_s;
static volatile uint16_t conn_handle_s;
static volatile bool running_s;

static void ble_dma_rx_task(void *param)
{
    (void)param;
    ble_dma_rx_item_t item;
    while (running_s) {
        if (xQueueReceive(rx_q_s, &item, pdMS_TO_TICKS(100)) == pdTRUE) {
            if (rx_cb_s && item.len > 0) {
                rx_cb_s(item.ch, item.data, item.len, rx_ctx_s);
            }
        }
    }
    vTaskDelete(NULL);
}

static void ble_dma_tx_task(void *param)
{
    (void)param;
    ble_dma_tx_item_t item;
    while (running_s) {
        if (xQueueReceive(tx_q_s, &item, pdMS_TO_TICKS(100)) != pdTRUE) {
            continue;
        }
        if (!tx_fn_s || conn_handle_s == 0U || item.data == NULL || item.len == 0) {
            free(item.data);
            continue;
        }

        int rc = tx_fn_s(item.attr_handle, item.data, item.len, tx_ctx_s);
        if (rc != 0) {
            vTaskDelay(pdMS_TO_TICKS(2));
            (void)tx_fn_s(item.attr_handle, item.data, item.len, tx_ctx_s);
        }
        free(item.data);
    }
    vTaskDelete(NULL);
}

esp_err_t link_dma_ble_start(link_dma_ble_rx_cb_t rx_cb, void *rx_ctx,
                              link_dma_ble_tx_fn_t tx_fn, void *tx_ctx)
{
    if (running_s) {
        return ESP_OK;
    }
    if (!rx_cb || !tx_fn) {
        return ESP_ERR_INVALID_ARG;
    }

    rx_cb_s = rx_cb;
    rx_ctx_s = rx_ctx;
    tx_fn_s = tx_fn;
    tx_ctx_s = tx_ctx;

    rx_q_s = xQueueCreate(BLE_DMA_RX_QUEUE_DEPTH, sizeof(ble_dma_rx_item_t));
    tx_q_s = xQueueCreate(BLE_DMA_TX_QUEUE_DEPTH, sizeof(ble_dma_tx_item_t));
    if (!rx_q_s || !tx_q_s) {
        link_dma_ble_stop();
        return ESP_ERR_NO_MEM;
    }

    running_s = true;
    if (xTaskCreate(ble_dma_rx_task, "ble_dma_rx", 4096, NULL, 6, &rx_task_s) != pdPASS ||
        xTaskCreate(ble_dma_tx_task, "ble_dma_tx", 4096, NULL, 5, &tx_task_s) != pdPASS) {
        running_s = false;
        link_dma_ble_stop();
        return ESP_ERR_NO_MEM;
    }

    ESP_LOGI(tag_s, "BLE 异步收发队列已启动 (RX depth=%d, TX depth=%d)",
             BLE_DMA_RX_QUEUE_DEPTH, BLE_DMA_TX_QUEUE_DEPTH);
    return ESP_OK;
}

void link_dma_ble_stop(void)
{
    running_s = false;
    if (rx_task_s) {
        vTaskDelete(rx_task_s);
        rx_task_s = NULL;
    }
    if (tx_task_s) {
        vTaskDelete(tx_task_s);
        tx_task_s = NULL;
    }

    if (rx_q_s) {
        vQueueDelete(rx_q_s);
        rx_q_s = NULL;
    }
    if (tx_q_s) {
        ble_dma_tx_item_t item;
        while (xQueueReceive(tx_q_s, &item, 0) == pdTRUE) {
            free(item.data);
        }
        vQueueDelete(tx_q_s);
        tx_q_s = NULL;
    }

    rx_cb_s = NULL;
    tx_fn_s = NULL;
    conn_handle_s = 0;
}

bool link_dma_ble_post_rx(link_dma_ble_ch_t ch, const uint8_t *data, uint16_t len)
{
    if (!running_s || !data || len == 0 || len > BLE_DMA_RX_MAX_BYTES) {
        return false;
    }

    ble_dma_rx_item_t item = {
        .ch = ch,
        .len = len,
    };
    memcpy(item.data, data, len);

    if (xQueueSend(rx_q_s, &item, 0) != pdTRUE) {
        ESP_LOGW(tag_s, "BLE RX 队列满，丢弃 %u 字节", (unsigned)len);
        return false;
    }
    return true;
}

bool link_dma_ble_post_tx(uint16_t attr_handle, const uint8_t *data, uint16_t len)
{
    if (!running_s || !data || len == 0 || len > BLE_DMA_TX_MAX_BYTES) {
        return false;
    }

    uint8_t *copy = (uint8_t *)malloc(len);
    if (!copy) {
        return false;
    }
    memcpy(copy, data, len);

    ble_dma_tx_item_t item = {
        .attr_handle = attr_handle,
        .len = len,
        .data = copy,
    };

    if (xQueueSend(tx_q_s, &item, 0) != pdTRUE) {
        ESP_LOGW(tag_s, "BLE TX 队列满，丢弃 %u 字节", (unsigned)len);
        free(copy);
        return false;
    }
    return true;
}

void link_dma_ble_set_conn_handle(uint16_t conn_handle)
{
    conn_handle_s = conn_handle;
}
