#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "os/os.h"

#include "uart_port.h"

#include "drivers/chip/gpio.h"
#include "driver/uart.h"
#include "hal/uart_ll.h"

#include "drivers/data_port/sb_data_port.h"

#define CONFIG_SYS_LOG_DUMP_ON 0
#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_WRN
#define SYS_LOG_DOMAIN "UART"
#include "sys_log.h"

#define _MAX_UART_NUM 3

#define UART_IN_TAST_STK_SIZE 2304

typedef struct
{
    uint16_t size;
    uint8_t data[0];
} __fifo_data_t;

typedef struct
{
    int uart_num;
    int baudrate;
    int tx_pin;
    int rx_pin;
    int rx_task_priority;
    uint16_t buffer_size;
    uint8_t frame_ms;
    volatile uint16_t buffer_used;
    os_work_t *rx_resume_work;
    QueueHandle_t event_queue;
    os_thread_t rx_thread_hdl;
    union
    {
        os_pipe_t rx_pipe;
        os_fifo_t rx_fifo;
    };
} __uart_data_t;

static void _uart_rx_thread(void *parmas)
{
    uart_event_t event;
    int ret = 0;
    int len = 0;
    int rx_len = 0;
    __uart_data_t *uart_data = ((sb_data_port_t *)parmas)->data;
    const uint32_t TMP_BUF_LEN = 1024;
    uint8_t tmpbuffer[TMP_BUF_LEN];
    TickType_t tick = portMAX_DELAY;
    uint16_t recv_total = 0;
    os_time_t rx_time = 0;

    for (;;)
    {
        if (xQueueReceive(uart_data->event_queue, (void *)&event, tick))
        {
            switch (event.type)
            {
            case UART_DATA:
            {
                rx_len = event.size;
                while (rx_len > 0)
                {
                    if (uart_data->frame_ms == 0)
                    {
                        len = (rx_len > TMP_BUF_LEN) ? TMP_BUF_LEN : rx_len;
                        ret = uart_read_bytes(uart_data->uart_num, tmpbuffer, len, portMAX_DELAY);
                        if (ret <= 0)
                        {
                            break;
                        }
                        rx_len -= ret;

                        // 将数据写入ringbuffer，由外部轮询ringbuffer
                        uint8_t *p = tmpbuffer;
                        for (int i = 0; i < 100 && ret != 0; i++)
                        {
                            int write_result = os_pipe_fifo_fill(&uart_data->rx_pipe, p, ret);
                            p = &((uint8_t *)p)[write_result];
                            ret -= write_result;
                            if (ret != 0)
                            {
                                os_thread_sleep(10);
                            }
                        }
                        if (ret != 0)
                        {
                            SYS_LOG_ERR("write data to rx_pipe_obj failed, remain: %d bytes", ret);
                        }
                    }
                    else
                    {
                        len = sizeof(tmpbuffer) - recv_total;
                        if (len > rx_len)
                        {
                            len = rx_len;
                        }
                        ret = uart_read_bytes(uart_data->uart_num, &tmpbuffer[recv_total], len, portMAX_DELAY);
                        if (ret <= 0)
                        {
                            break;
                        }

                        rx_len -= ret;
                        recv_total += ret;
                        if (recv_total >= sizeof(tmpbuffer))
                        {
                            rx_time = os_get_sys_time() - uart_data->frame_ms - 1; // 使退出 switch 后的超时条件必定成立而立即保存数据
                            break;
                        }
                        rx_time = os_get_sys_time();
                    }
                }
            }
            break;

            // Event of HW FIFO overflow detected
            case UART_FIFO_OVF:
                SYS_LOG_WRN("hw fifo overflow");
                // If fifo overflow happened, you should consider adding flow control for your application.
                // The ISR has already reset the rx FIFO,
                uart_flush_input(uart_data->uart_num);
                xQueueReset(uart_data->event_queue);
                break;
            // Event of UART ring buffer full
            case UART_BUFFER_FULL:
                SYS_LOG_WRN("ring buffer full");
                // If buffer full happened, you should consider encreasing your buffer size
                uart_flush_input(uart_data->uart_num);
                xQueueReset(uart_data->event_queue);
                break;
            // Event of UART RX break detected
            case UART_BREAK:
                SYS_LOG_WRN("uart rx break");
                break;
            // Event of UART parity check error
            case UART_PARITY_ERR:
                SYS_LOG_WRN("uart parity error");
                break;
            // Event of UART frame error
            case UART_FRAME_ERR:
                SYS_LOG_WRN("uart frame error");
                break;
            // Others
            default:
                SYS_LOG_WRN("uart event type: %d", event.type);
                break;
            }
        }

        if (uart_data->frame_ms != 0)
        {
            if (os_get_sys_time() - rx_time > uart_data->frame_ms)
            {
                if (recv_total > 0)
                {
                    while (uart_data->buffer_used >= uart_data->buffer_size)
                    {
                        os_thread_sleep(1);
                    }

                    __fifo_data_t *fifo_data = os_fifo_alloc(sizeof(__fifo_data_t) + recv_total);
                    if (fifo_data)
                    {
                        os_scheduler_suspend();
                        uart_data->buffer_used += recv_total + sizeof(((__fifo_data_t *)0)->size);
                        os_scheduler_resume();

                        fifo_data->size = recv_total;
                        memcpy(fifo_data->data, tmpbuffer, recv_total);
                        os_fifo_put(&uart_data->rx_fifo, fifo_data);
                    }

                    recv_total = 0;
                    tick = 1;
                }
                else
                {
                    tick = portMAX_DELAY;
                }
            }
            else
            {
                tick = 1;
            }
        }
    }
}

static void uart_port_initialize(sb_data_port_t *port)
{
    __uart_data_t *uart_data = port->data;
    uart_config_t uart_config;

    memset(&uart_config, 0, sizeof(uart_config));
    uart_config.baud_rate = uart_data->baudrate;
    uart_config.data_bits = UART_DATA_8_BITS;
    uart_config.parity = UART_PARITY_DISABLE;
    uart_config.stop_bits = UART_STOP_BITS_1;
    uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;

    esp_err_t ret = uart_set_pin(uart_data->uart_num, uart_data->tx_pin, uart_data->rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    if (ret != ESP_OK)
    {
        SYS_LOG_ERR("failed to set uart pin:%d, %d", uart_data->uart_num, ret);
        return;
    }

    ret = uart_param_config(uart_data->uart_num, &uart_config);
    if (ret != ESP_OK)
    {
        SYS_LOG_ERR("failed to set uart param:%d, %d", uart_data->uart_num, ret);
        return;
    }

    ret = uart_driver_install(uart_data->uart_num, 0x400, 0x400, 3, &(uart_data->event_queue), 0);
    if (ret != ESP_OK)
    {
        SYS_LOG_ERR("failed to uart_driver_install:%d, %d", uart_data->uart_num, ret);
        return;
    }

    uart_disable_intr_mask(uart_data->uart_num, UART_INTR_BRK_DET); // 关闭检测UART_BREAK中断，防止过多的UART_BREAK导致的程序异常

    SYS_LOG_INF("uart_driver_install ok. (%d, %d, %d, %d, %d, %d)", uart_data->uart_num, 0x400, 0x400, 3, (uint32_t)&uart_data->event_queue, 0);
}

static int _uart_port_start(sb_data_port_t *port)
{
    __uart_data_t *uart_data = port->data;

    SYS_LOG_INF("uart start, uartnum:%d", uart_data->uart_num);

    if (uart_data->frame_ms == 0)
    {
        if (!os_pipe_is_valid(&uart_data->rx_pipe))
        {
            if (os_pipe_create(&uart_data->rx_pipe, uart_data->buffer_size) != OS_OK)
            {
                port->vtable->stop(port);
                return -1;
            }
            os_pipe_regist(&uart_data->rx_pipe, uart_data->rx_resume_work, 0);
        }
    }
    else
    {
        if (!os_fifo_q_is_valid(&uart_data->rx_fifo))
        {
            if (os_fifo_q_create(&uart_data->rx_fifo) != OS_OK)
            {
                port->vtable->stop(port);
                return -1;
            }
            os_fifo_q_regist(&uart_data->rx_fifo, uart_data->rx_resume_work, 0);
        }
        uart_data->buffer_used = 0;
    }

    if (!uart_is_driver_installed(uart_data->uart_num))
    {
        uart_port_initialize(port);
    }

    if (!os_thread_is_valid(&uart_data->rx_thread_hdl))
    {
        if (os_thread_create(&uart_data->rx_thread_hdl,
                             "uart_rx",
                             _uart_rx_thread,
                             port,
                             UART_IN_TAST_STK_SIZE,
                             uart_data->rx_task_priority) != OS_OK)
        {
            SYS_LOG_WRN("task create fail");
            port->vtable->stop(port);
            return -1;
        }
    }

    return 0;
}

static int _uart_port_stop(sb_data_port_t *port)
{
    __uart_data_t *uart_data = port->data;

    if (os_thread_is_valid(&uart_data->rx_thread_hdl))
    {
        os_thread_delete(&uart_data->rx_thread_hdl);
    }

    if (uart_is_driver_installed(uart_data->uart_num))
    {
        uart_driver_delete(uart_data->uart_num);
    }

    if (uart_data->frame_ms == 0)
    {
        if (os_pipe_is_valid(&uart_data->rx_pipe))
        {
            os_pipe_delete(&uart_data->rx_pipe);
        }
    }
    else
    {
        if (os_fifo_q_is_valid(&uart_data->rx_fifo))
        {
            os_fifo_q_delete(&uart_data->rx_fifo);
        }
    }

    return 0;
}

static int _uart_port_write(sb_data_port_t *port, const void *data, uint32_t size)
{
    __uart_data_t *uart_data = port->data;
    int ret = uart_write_bytes(uart_data->uart_num, data, size);
    if (ret > 0)
    {
        SYS_LOG_DUMP(data, ret, 0, 0);
    }
    return ret;
}

static int _uart_port_read(sb_data_port_t *port, void *data, uint32_t size)
{
    __uart_data_t *uart_data = port->data;
    int ret;
    if (uart_data->frame_ms == 0)
    {
        ret = os_pipe_fifo_read(&uart_data->rx_pipe, data, size);
    }
    else
    {
        __fifo_data_t *fifo_data = os_fifo_take(&uart_data->rx_fifo, 0);
        if (fifo_data)
        {
            ret = size < fifo_data->size ? size : fifo_data->size;
            if (data)
            {
                memcpy(data, fifo_data->data, ret);
            }
            os_fifo_free(fifo_data);

            os_scheduler_suspend();
            uart_data->buffer_used -= ret + sizeof(((__fifo_data_t *)0)->size);
            os_scheduler_resume();
        }
        else
        {
            ret = 0;
        }
    }
    if (ret > 0)
    {
        SYS_LOG_DUMP(data, ret, 0, 0);
    }
    return ret;
}

static bool _uart_port_is_start(sb_data_port_t *port)
{
    if (port == NULL)
    {
        return false;
    }
    __uart_data_t *uart_data = port->data;
    if (uart_data == NULL)
    {
        return false;
    }

    if (uart_data->frame_ms == 0)
    {
        if (!os_pipe_is_valid(&uart_data->rx_pipe))
        {
            return false;
        }
    }
    else
    {
        if (!os_fifo_q_is_valid(&uart_data->rx_fifo))
        {
            return false;
        }
    }

    return true;
}

static uint32_t _uart_port_get_rx_length(sb_data_port_t *port)
{
    __uart_data_t *uart_data = port->data;
    if (uart_data->frame_ms == 0)
    {
        return os_pipe_get_valid_size(&uart_data->rx_pipe);
    }
    else
    {
        __fifo_data_t *fifo_data = os_fifo_peek_head(&uart_data->rx_fifo);
        if (fifo_data)
        {
            return fifo_data->size;
        }
        else
        {
            return 0;
        }
    }
}

static sb_data_port_vtable_t const s_uart_vtable = {
    .start = _uart_port_start,
    .stop = _uart_port_stop,
    .write = _uart_port_write,
    .read = _uart_port_read,
    .is_started = _uart_port_is_start,
    .get_rx_length = _uart_port_get_rx_length,
};

int sb_uart_port_init(void)
{
    return 0;
}

/**
 * @brief
 *
 * @param uart_num          串口号 0..n
 * @param baudrate          波特率
 * @param tx_pin            TXD 引脚
 * @param rx_pin            RXD 引脚
 * @param rx_task_priority  内部接收任务的优先级
 * @param buffer_size       接收缓存大小
 * @param frame_ms          0: 数据流， > 0: 最少连续有 n 毫秒没有收到数据时成为一帧
 * @param rx_resume_work    当收到新数据时唤醒的工作项，可设置为 NULL
 * @return sb_data_port_t*
 */
sb_data_port_t *sb_uart_port_bind(int uart_num,
                                  int baudrate,
                                  int tx_pin,
                                  int rx_pin,
                                  int rx_task_priority,
                                  uint16_t buffer_size,
                                  uint8_t frame_ms,
                                  os_work_t *rx_resume_work)
{
    static sb_data_port_t s_uart_port[_MAX_UART_NUM];
    static __uart_data_t s_uart_data[_MAX_UART_NUM];

    sb_data_port_t *port = &s_uart_port[uart_num];
    __uart_data_t *uart_data = &s_uart_data[uart_num];

    SYS_ASSERT(uart_num < _MAX_UART_NUM, "");
    SYS_ASSERT(port->data == NULL, "The interface has already been bound");
    // SYS_ASSERT(baudrate > 0, "");

    uart_data->uart_num = uart_num;
    uart_data->baudrate = baudrate;
    uart_data->tx_pin = tx_pin;
    uart_data->rx_pin = rx_pin;
    uart_data->rx_task_priority = rx_task_priority;
    uart_data->buffer_size = buffer_size;
    uart_data->frame_ms = frame_ms;
    uart_data->rx_resume_work = rx_resume_work;

    port->vtable = &s_uart_vtable;
    port->data = uart_data;

    return port;
}

void sb_uart_port_unbind(sb_data_port_t *port)
{
    if (_uart_port_is_start(port))
    {
        _uart_port_stop(port);

        __uart_data_t *uart_data = port->data;
        drv_gpio_pin_configure(uart_data->tx_pin, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP);
        drv_gpio_pin_configure(uart_data->rx_pin, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP);

        port->data = NULL;
    }
}
