#include "link_dma_uart.h"

#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"

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

static const char *tag_s = "LINK_DMA_UART";

#define UART_BUF_SIZE       4096
#define UART_EVT_QUEUE_LEN  16

static QueueHandle_t uart_evt_q_s;
static TaskHandle_t rx_task_s;
static link_dma_uart_rx_cb_t rx_cb_s;
static void *rx_ctx_s;
static uart_port_t uart_port_s;
static volatile bool running_s;
static SemaphoreHandle_t tx_mutex_s;

static void uart_evt_rx_task(void *param)
{
    (void)param;
    uint8_t *buf = (uint8_t *)malloc(UART_BUF_SIZE);
    if (!buf) {
        vTaskDelete(NULL);
        return;
    }

    uart_event_t evt;
    while (running_s) {
        if (xQueueReceive(uart_evt_q_s, &evt, pdMS_TO_TICKS(50)) != pdTRUE) {
            continue;
        }

        switch (evt.type) {
        case UART_DATA:
        case UART_BUFFER_FULL:
        case UART_FIFO_OVF:
            if (evt.type == UART_FIFO_OVF) {
                ESP_LOGW(tag_s, "UART FIFO overflow, flushing");
                uart_flush_input(uart_port_s);
            }
            for (;;) {
                int n = uart_read_bytes(uart_port_s, buf, UART_BUF_SIZE,
                                        (evt.type == UART_DATA) ? 0 : pdMS_TO_TICKS(10));
                if (n <= 0) {
                    break;
                }
                if (rx_cb_s) {
                    rx_cb_s(buf, (size_t)n, rx_ctx_s);
                }
            }
            break;
        default:
            break;
        }
    }

    free(buf);
    vTaskDelete(NULL);
}

esp_err_t link_dma_uart_start(uart_port_t port, int baud_rate, int tx_gpio, int rx_gpio,
                              link_dma_uart_rx_cb_t rx_cb, void *rx_ctx)
{
    if (running_s) {
        return ESP_OK;
    }
    if (tx_gpio < 0 || rx_gpio < 0 || rx_cb == NULL) {
        return ESP_ERR_INVALID_ARG;
    }

    uart_port_s = port;
    rx_cb_s = rx_cb;
    rx_ctx_s = rx_ctx;

    uart_config_t uart_cfg = {
        .baud_rate = baud_rate,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };

    gpio_reset_pin((gpio_num_t)tx_gpio);
    gpio_reset_pin((gpio_num_t)rx_gpio);

    esp_err_t err = uart_param_config(port, &uart_cfg);
    if (err != ESP_OK) {
        return err;
    }

    err = uart_set_pin(port, tx_gpio, rx_gpio, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    if (err != ESP_OK) {
        return err;
    }

    err = uart_driver_install(port, UART_BUF_SIZE, UART_BUF_SIZE, UART_EVT_QUEUE_LEN,
                              &uart_evt_q_s, 0);
    if (err != ESP_OK) {
        return err;
    }

    uart_set_rx_full_threshold(port, 64);
    uart_set_rx_timeout(port, 10);
    gpio_set_pull_mode((gpio_num_t)rx_gpio, GPIO_PULLUP_ONLY);
    uart_flush(port);

    tx_mutex_s = xSemaphoreCreateMutex();
    if (!tx_mutex_s) {
        link_dma_uart_stop();
        return ESP_ERR_NO_MEM;
    }

    running_s = true;
    if (xTaskCreate(uart_evt_rx_task, "uart_evt_rx", 4096, NULL, 6, &rx_task_s) != pdPASS) {
        running_s = false;
        link_dma_uart_stop();
        return ESP_ERR_NO_MEM;
    }

    ESP_LOGI(tag_s, "UART 事件驱动收发已启动 (port=%d, %d baud, TX=GPIO%d RX=GPIO%d, buf=%d)",
             (int)port, baud_rate, tx_gpio, rx_gpio, UART_BUF_SIZE);
    return ESP_OK;
}

void link_dma_uart_stop(void)
{
    running_s = false;

    if (rx_task_s) {
        vTaskDelete(rx_task_s);
        rx_task_s = NULL;
    }

    if (uart_port_s >= 0 && uart_evt_q_s != NULL) {
        uart_driver_delete(uart_port_s);
    }
    uart_evt_q_s = NULL;

    if (tx_mutex_s) {
        vSemaphoreDelete(tx_mutex_s);
        tx_mutex_s = NULL;
    }

    rx_cb_s = NULL;
    rx_ctx_s = NULL;
}

int link_dma_uart_send(const uint8_t *data, size_t len, int timeout_ms)
{
    if (!running_s || !data || len == 0) {
        return -1;
    }

    int wait_ms = (timeout_ms > 0) ? timeout_ms : 500;
    if (tx_mutex_s == NULL || xSemaphoreTake(tx_mutex_s, pdMS_TO_TICKS(wait_ms)) != pdTRUE) {
        ESP_LOGW(tag_s, "UART TX mutex timeout");
        return -2;
    }

    int n = uart_write_bytes(uart_port_s, data, len);
    if (n > 0) {
        esp_err_t err = uart_wait_tx_done(uart_port_s, pdMS_TO_TICKS(wait_ms));
        if (err != ESP_OK) {
            ESP_LOGW(tag_s, "uart_wait_tx_done: %s", esp_err_to_name(err));
            n = -2;
        }
    } else {
        ESP_LOGW(tag_s, "uart_write_bytes failed: %d", n);
    }

    (void)xSemaphoreGive(tx_mutex_s);
    return n;
}

bool link_dma_uart_is_running(void)
{
    return running_s;
}
