#include "fpga.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "driver/parlio_tx.h"
#include "driver/parlio_rx.h"
#include "driver/gpio.h"
#include "gpio.h"

#define DBUS0       3
#define DBUS1       0
#define DBUS2       1
#define DBUS3       6
#define DBUS4       7
#define DBUS5       8
#define DBUS6       9
#define DBUS7       10
#define TX_EN       24     //high active
#define RX_EN       23     //high active
#define PAD_CLK     25


extern const char *TAG;                              //define in softap_main.c

typedef struct {
    parlio_tx_unit_handle_t unit;
    volatile bool ing;                      //是否有并口数据正在发送中
    parlio_transmit_config_t transmitConfig;
} ParlioTx;

typedef struct {
    parlio_rx_unit_handle_t unit;
    parlio_rx_delimiter_handle_t deli;    //RX软界定符
    uint8_t *buffer;                      //用于并口数据接收的DMA兼容缓冲区
    volatile bool bufferFilled;           //用于接收数据的缓冲区中存在收到但尚未处理的数据
    volatile bool ing;                    //是否有并口数据正在接收中
    volatile bool timeoutError; 
    parlio_receive_config_t receiveConfig;
} ParlioRx;

static ParlioTx tx =  {
    .unit = NULL,
    .ing = false,
    .transmitConfig = {
        .idle_value = 0x00 // 空闲状态下所有数据线均为低电平
    }
};

static ParlioRx rx = {
    .unit = NULL,
    .deli = NULL,
    .buffer = NULL,
    .bufferFilled = false,
    .ing = false,
    .timeoutError = false,
        // 配置 RX 单元接收参数
    .receiveConfig = {
        .delimiter = NULL,       // 使用上面创建的软帧界定符
        .flags = {
            .partial_rx_en = false,         // 禁用部分接收模式
            .indirect_mount = false,
        }
    }
};


static IRAM_ATTR bool parlio_tx_done_cb(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx)
{
    //printf("parlioTx transaction completed.\n");  这行代码会引起 lock error
    tx.ing = false;
    return false;  //Whether a high priority task has been waken up by this callback function
}

static bool on_rx_partial_receive_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_ctx)
{
    // 当接收到部分数据时调用（用于无限事务）。可在回调中作简单处理，如队列、任务操作，或将接受完成的数据拷贝到用户 buffer 中
    return false;
}

static bool on_rx_receive_done_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_ctx)
{
    parlio_rx_soft_delimiter_start_stop(rx.unit, rx.deli, false);  //停止软帧界定符
    rx.ing = false;
    rx.bufferFilled = true;
    return false;
}

static bool on_rx_timeout_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_ctx)
{
    // 当接收超时时调用
    parlio_rx_soft_delimiter_start_stop(rx.unit, rx.deli, false);  //停止软帧界定符
    rx.timeoutError = true;   //置接收超时标志
    rx.ing = false;
    return false;
}

bool openParlioTx()
{
    if (tx.unit!=NULL){
        ESP_LOGE(TAG,"Found tx.unit is not NULL in openParlioTx().");
        return false;
    }

    tx.ing = false;

    parlio_tx_unit_config_t config = {
        .clk_src = PARLIO_CLK_SRC_DEFAULT,      // 选择默认的时钟源
        .data_width = 8,                        // 数据宽度为 8 位
        .clk_in_gpio_num = -1,                  // 不使用外部时钟源
        .valid_gpio_num = TX_EN,                // 在TX_EN输出有效信号
        .clk_out_gpio_num = PAD_CLK,
        .data_gpio_nums = {
            DBUS0,DBUS1,DBUS2,DBUS3,DBUS4,DBUS5,DBUS6,DBUS7,
        },
        .output_clk_freq_hz = 10 * 1000 * 1000, // 输出时钟频率为 10 MHz
        .trans_queue_depth =  1,                // 待处理事务队列深度为1
        .max_transfer_size =  128,              // 一次传输的最大传输大小为 128 字节
        .sample_edge = PARLIO_SAMPLE_EDGE_NEG,  // 在时钟下降沿采样数据
        .flags = {
            .invert_valid_out = false, // 有效信号默认高电平有效（不反转）
        }
    };
    
    esp_err_t r = parlio_new_tx_unit(&config, &tx.unit);  // 创建 TX 单元实例
    ESP_ERROR_CHECK(r);
    if (r!=ESP_OK) {
        tx.unit = NULL;
        return false;
    }

    parlio_tx_event_callbacks_t parlio_cbs = {
        .on_trans_done = parlio_tx_done_cb,
    };
    r = parlio_tx_unit_register_event_callbacks(tx.unit, &parlio_cbs, NULL);
    ESP_ERROR_CHECK(r);
    if (r!=ESP_OK)
        return false;

    r = parlio_tx_unit_enable(tx.unit);  // 使能 TX 单元
    ESP_ERROR_CHECK(r);
    if (r!=ESP_OK)
        return false;
 
    printf("Open Parlio Tx\n");
    return true;
}

bool openParlioRx() {
    if (rx.unit!=NULL){
        ESP_LOGE(TAG,"Found rx.unit is not NULL in openParlioRx().");
        return false;
    }

    if (rx.buffer==NULL) {  //分配用于并口数据接收的缓冲区（全生命周期有效，不考虑回收问题）
        rx.buffer = heap_caps_calloc(1, 512, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
        if (rx.buffer == NULL) {
            ESP_LOGE(TAG,"Allocation of DMA capatible bufferParlioRx failed!");
        }
    }

    rx.ing = false;
    rx.bufferFilled = false;
    rx.timeoutError = false;

    parlio_rx_unit_config_t config = {
        .clk_src = PARLIO_CLK_SRC_DEFAULT,      // 选择默认的时钟源
        .data_width = 8,                        // 数据宽度为 8 位
        .clk_in_gpio_num = -1,                  // 不使用外部时钟源
        .clk_out_gpio_num = PAD_CLK,            // 输出时钟引脚
        .valid_gpio_num = RX_EN,                // 在RX_EN脚上输出有效信号
        .data_gpio_nums = {
            DBUS0,DBUS1,DBUS2,DBUS3,DBUS4,DBUS5,DBUS6,DBUS7,
        },
        .exp_clk_freq_hz = 1 * 1000 * 1000,    // 期望时钟频率为 10 MHz
        .trans_queue_depth = 10,                 // 事务队列深度为 1
        .max_recv_size = 1024,                   // 最大接收大小为 512 字节
    };

    esp_err_t r = parlio_new_rx_unit(&config, &rx.unit);     // 创建 RX 单元实例
    ESP_ERROR_CHECK(r);
    if (r!=ESP_OK) {
        rx.unit = NULL;
        return false;
    }

    parlio_rx_event_callbacks_t cbs = {
        .on_partial_receive = on_rx_partial_receive_callback,
        .on_receive_done = on_rx_receive_done_callback,
        .on_timeout = on_rx_timeout_callback
    };
    r = parlio_rx_unit_register_event_callbacks(rx.unit, &cbs, NULL);
    ESP_ERROR_CHECK(r);
    if (r!=ESP_OK)
        return false;

    r = parlio_rx_unit_enable(rx.unit,true);  //  使能RX单元
    ESP_ERROR_CHECK(r);
    if (r!=ESP_OK)
        return false;

    parlio_rx_soft_delimiter_config_t soft_config = {
        .sample_edge = PARLIO_SAMPLE_EDGE_POS,       // 上升沿采样
        .bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, // 从 MSB 开始打包位
        .eof_data_len = 512,                         // 512字节后结束帧，由于没有其他结束依据，软帧界定符必须设置该字段
        .timeout_ticks = 0                           // 无超时
    };
    r = parlio_new_rx_soft_delimiter(&soft_config, &rx.deli);
    ESP_ERROR_CHECK(r);
    if (r!=ESP_OK)
        return false;

    printf("Open Parlio Rx\n");
    return true;
}

void closeParlioTx() {
    if (tx.unit==NULL){
        ESP_LOGE(TAG,"Found tx.unit is NULL in closeParlioTx().");
        return;
    }

    ESP_ERROR_CHECK(parlio_tx_unit_disable(tx.unit));
    ESP_ERROR_CHECK(parlio_del_tx_unit(tx.unit));    
    tx.unit = NULL;
    tx.ing = false;
    printf("ParlioTx closed\n");
}

void closeParlioRx() {
    if (rx.unit==NULL){
        ESP_LOGE(TAG,"Found rx.unit is NULL in closeParlioRx().");
        return;
    }

    ESP_ERROR_CHECK(parlio_rx_unit_disable(rx.unit));
    ESP_ERROR_CHECK(parlio_del_rx_unit(rx.unit));

    rx.unit = NULL;
    rx.ing = false;
    rx.bufferFilled = false;
    rx.timeoutError = false;

    ESP_ERROR_CHECK(parlio_del_rx_delimiter(rx.deli));
    rx.deli = NULL;
    printf("PalioRx closed\n");
}


bool parlioTxSend(const uint8_t* data, uint16_t sizeBytes)
{
    if (tx.ing || rx.ing)  //正在发送或接收数据，不能发送
        return false;

    if (rx.unit!=NULL)
        closeParlioRx();
    
    if (tx.unit==NULL)
        openParlioTx();

    tx.ing = true;    
    esp_err_t r = parlio_tx_unit_transmit(tx.unit,data,sizeBytes*sizeof(uint8_t)*8,&tx.transmitConfig);
    ESP_ERROR_CHECK(r);

    return (r==ESP_OK);
}

bool parlioRxReceive(uint8_t* buffer, const size_t sizeBuffer){
    if (tx.ing || rx.ing)  //正在发送或接收数据，不能发起新的接收事务
        return false;

    if (tx.unit!=NULL)
        closeParlioTx();
    
    if (rx.unit==NULL)
        openParlioRx();

    printf("parlioRxReceive execute.\n");
    rx.ing = true;

    // 配置 RX 单元接收参数
    rx.receiveConfig.delimiter = rx.deli;


    // 启动软帧界定符（仅软帧界定符需要）
    esp_err_t r = parlio_rx_soft_delimiter_start_stop(rx.unit, rx.deli, true);
    ESP_ERROR_CHECK(r);

    if (rx.deli==NULL)
        printf("Error: rx.deli==NULL\n");

    // 启动接收事务
    r = parlio_rx_unit_receive(rx.unit, buffer, sizeBuffer, &rx.receiveConfig);
    ESP_ERROR_CHECK(r);

    return (r==ESP_OK);
}

//如果有且条件允许，接收FPGA响应包
void tryReceiveResponsePacket() {
    if (tx.ing || rx.ing)       //正在发送或接收数据，不能发起新的接收事务
        return;

    if (rx.bufferFilled)        //接收缓冲区不空，不能发起新的接收事务
        return;

    if (gpio_get_level(RDATA_RDY)==0)
        return;

    //RDATA_RDY为高，表示FPGA请求发送响应包
    parlioRxReceive(rx.buffer,512); 
}

void processResponsePacket() {
    if (!rx.bufferFilled)
        return;

    printf("recv: %x, %x, %x, %x\n", rx.buffer[0],rx.buffer[1],rx.buffer[2],rx.buffer[15]);
    rx.bufferFilled = false;
}

