#include "stdint.h"
#include "stdbool.h"
#include "plat_types.h"
#include "cmsis_os.h"
#include "cmsis_nvic.h"
#include "string.h"
#include "stdio.h"
#include "crc32.h"
#include "hal_trace.h"
#include "hal_iomux.h"
#include "hal_gpio.h"
#include "hal_uart.h"
#include "hal_timer.h"
#include "hal_iomux.h"
#include "app_ai_if_config.h"
#include "communication_svr.h"
#include "communication_sysapi.h"


//======================================================================================================

enum COMMUNICATION_MSG {
    COMMUNICATION_MSG_TX_REQ   = 0,
    COMMUNICATION_MSG_TX_DONE  = 1,
    COMMUNICATION_MSG_RX_REQ   = 2,
    COMMUNICATION_MSG_RX_DONE  = 3,
    COMMUNICATION_MSG_INIT     = 4,
    COMMUNICATION_MSG_REINIT   = 5,
    COMMUNICATION_MSG_RESET    = 6,
    COMMUNICATION_MSG_BREAK    = 7,
};

const static uint8_t communication_process_log[8][26] = {
    "COMMUNICATION_MSG_TX_REQ",
    "COMMUNICATION_MSG_TX_DONE",
    "COMMUNICATION_MSG_RX_REQ",
    "COMMUNICATION_MSG_RX_DONE",
    "COMMUNICATION_MSG_INIT",
    "COMMUNICATION_MSG_REINIT",
    "COMMUNICATION_MSG_RESET",
    "COMMUNICATION_MSG_BREAK",
};

enum COMMUNICATION_MODE {
    COMMUNICATION_MODE_NULL,
    COMMUNICATION_MODE_TX,
    COMMUNICATION_MODE_RX,
    COMMUNICATION_MODE_ENABLE_IRQ,
    COMMUNICATION_MODE_DISABLE_IRQ,
};


#define COMMAND_BLOCK_MAX (5)
#define COMMAND_LEN_MAX (128)
#define COMMAND_TRANSMITTED_SIGNAL (1<<0)

typedef struct {
    uint8_t cmd_buf[COMMAND_LEN_MAX];
    uint8_t cmd_len;
} COMMAND_BLOCK;
osPoolDef (command_mempool, COMMAND_BLOCK_MAX, COMMAND_BLOCK);
osPoolId   command_mempool = NULL;


#define COMMUNICATION_MAILBOX_MAX (10)
typedef struct {
    uint32_t src_thread;
    uint32_t system_time;
    uint32_t message;
    uint32_t parms1;
    uint32_t parms2;
} COMMUNICATION_MAIL;

osMailQDef (communication_mailbox, COMMUNICATION_MAILBOX_MAX, COMMUNICATION_MAIL);
static osMailQId communication_mailbox = NULL;
static uint8_t communication_mailbox_cnt = 0;


static osThreadId communication_tid = NULL;
static void communication_thread(void const *argument);
osThreadDef(communication_thread, osPriorityHigh, 1, 2048, "communication_server");

static bool uart_opened = false;
static int uart_error_detected = 0;

#if defined(CHIP_BEST2300P) || defined(CHIP_BEST2300A)
static const enum HAL_UART_ID_T comm_uart = HAL_UART_ID_2;
#else
static const enum HAL_UART_ID_T comm_uart = HAL_UART_ID_1;
#endif

static const struct HAL_UART_CFG_T uart_cfg = {
    HAL_UART_PARITY_NONE,
    HAL_UART_STOP_BITS_1,
    HAL_UART_DATA_BITS_8,
    HAL_UART_FLOW_CONTROL_NONE,
    HAL_UART_FIFO_LEVEL_1_2,
    HAL_UART_FIFO_LEVEL_1_2,
#if defined(CHIP_BEST2300P) || defined(CHIP_BEST2300A)
    1152000,
#else
    921600,
#endif
    true,
    true,
    false,
};

static bool uart_rx_dma_is_running = false;

static COMMAND_BLOCK *rx_command_block_p = NULL;

static uint32_t uart_rx_idle_counter = 0;
static void uart_rx_idle_handler(void const *param);
osTimerDef(uart_rx_idle_timer, uart_rx_idle_handler);
static osTimerId uart_rx_idle_timer_id = NULL;


static communication_receive_func_typedef communication_receive_cb = NULL;


const static struct HAL_IOMUX_PIN_FUNCTION_MAP cfg_led2 = {
    HAL_IOMUX_PIN_LED2, HAL_IOMUX_FUNC_GPIO, HAL_IOMUX_PIN_VOLTAGE_VBAT, HAL_IOMUX_PIN_PULLUP_ENABLE,
};


inline int communication_mailbox_put(COMMUNICATION_MAIL* msg_src);
static void communication_process(COMMUNICATION_MAIL* mail_p);
int communication_io_mode_switch(enum COMMUNICATION_MODE mode);


static void uart_rx_dma_stop(void)
{
    union HAL_UART_IRQ_T mask;

    uint32_t lock = int_lock();
//    TRACE(1,"uart_rx_dma_stop:%d", uart_rx_dma_is_running);
    if (uart_rx_dma_is_running){
        mask.reg = 0;
        hal_uart_irq_set_mask(comm_uart, mask);
        hal_uart_stop_dma_recv(comm_uart);
        uart_rx_dma_is_running = false;
    }

    int_unlock(lock);
}

static void uart_rx_dma_start(void)
{
    union HAL_UART_IRQ_T mask;

    uint32_t lock = int_lock();

//    TRACE(1,"uart_rx_dma_start:%d", uart_rx_dma_is_running);

    hal_uart_flush(comm_uart, 0);
    //hal_uart_dma_recv(comm_uart, rx_command_block_p->cmd_buf, COMMAND_LEN_MAX, NULL, NULL);
    mask.reg = 0;
    mask.RT = 1;
    //hal_uart_irq_set_mask(comm_uart, mask);
    hal_uart_dma_recv_mask(comm_uart, rx_command_block_p->cmd_buf, COMMAND_LEN_MAX, NULL, NULL,&mask);
    
    uart_rx_dma_is_running = true;

    int_unlock(lock);
}

static void uart_break_handler(void)
{
    union HAL_UART_IRQ_T mask;
    COMMUNICATION_MAIL msg;
    memset(&msg, 0, sizeof(msg));

    TRACE(0,"UART-BREAK");
    mask.reg = 0;
    hal_uart_irq_set_mask(comm_uart, mask);
    hal_uart_flush(comm_uart, 0);
    uart_error_detected = 1;
    msg.message = COMMUNICATION_MSG_BREAK;
    communication_mailbox_put(&msg);
}

static void uart_rx_dma_handler(uint32_t xfer_size, int dma_error, union HAL_UART_IRQ_T status)
{
    COMMUNICATION_MAIL msg;

    //TRACE(8,"UART-RX size:%d dma_error=%d, status=0x%08x rt:%d fe:%d pe:%d be:%d oe:%d", xfer_size, dma_error, status, status.RT, status.FE, status.PE, status.BE, status.OE);

    if (status.BE) {
        uart_break_handler();
        return;
    }

    memset(&msg, 0, sizeof(COMMUNICATION_MAIL));
    msg.message = COMMUNICATION_MSG_RX_DONE;

    if (dma_error || status.FE|| status.PE || status.BE || status.OE ) {
        uart_error_detected = 1;
        uart_rx_dma_stop();
        msg.parms1 = false;
        msg.message = COMMUNICATION_MSG_RESET;
        communication_mailbox_put(&msg);
    }else{
        if (xfer_size){
            msg.parms1 = true;
            msg.parms2 = xfer_size;
            communication_mailbox_put(&msg);
        }else{
            uart_rx_dma_stop();
            msg.message = COMMUNICATION_MSG_RESET;
            communication_mailbox_put(&msg);
        }
    }
}

static void uart_tx_dma_handler(uint32_t xfer_size, int dma_error)
{
    COMMUNICATION_MAIL msg;

    memset(&msg, 0, sizeof(COMMUNICATION_MAIL));

//    TRACE(2,"UART-TX size:%d dma_error=%d", xfer_size, dma_error);

    osSignalSet(communication_tid, COMMAND_TRANSMITTED_SIGNAL);

    msg.message = COMMUNICATION_MSG_TX_DONE;

    communication_mailbox_put(&msg);
}

static void uart_init(void)
{
    struct HAL_UART_CFG_T comm_uart_cfg;

    if (!uart_opened) {
        memcpy(&comm_uart_cfg, &uart_cfg, sizeof(comm_uart_cfg));
        hal_uart_open(comm_uart, &comm_uart_cfg);
        hal_uart_irq_set_dma_handler(comm_uart, uart_rx_dma_handler, uart_tx_dma_handler);
        uart_opened = true;
    }

    hal_uart_flush(comm_uart, 0);
}

static void uart_deinit(void)
{
    if (uart_opened) {
        hal_uart_close(comm_uart);
        uart_opened = false;
    }
}

static void uart_rx_idle_timer_start(void)
{
    uart_rx_idle_counter = 0;
    TRACE(1,"[%s] enter...", __func__);

    if (uart_rx_idle_timer_id != NULL) {
        osTimerStop(uart_rx_idle_timer_id);
        osTimerStart(uart_rx_idle_timer_id, 100);
    }
}

#if defined(CHIP_BEST1400) || defined(CHIP_BEST1402)
static void uart_rx_edge_detect_handler(enum HAL_GPIO_PIN_T pin)
{
    COMMUNICATION_MAIL msg = {0};

    //disable led2 pin external interrupt mode...
    communication_io_mode_switch(COMMUNICATION_MODE_DISABLE_IRQ);

    //post uart rx request...
    msg.message = COMMUNICATION_MSG_RX_REQ;
    communication_mailbox_put(&msg);
}
#endif

static void uart_rx_idle_handler(void const *param)
{
    const uint8_t rx_idle_indicate[2] = {0x10,0x24};

    if(uart_rx_idle_counter++ >= 150)//150 * 100 = 15s
    {
        TRACE(1,"[%s] enter...", __func__);

        uart_rx_idle_counter = 0;

        //stop virtual timer...
        osTimerStop(uart_rx_idle_timer_id);

        //stop uart rx dma...
        uart_rx_dma_stop();

        //notify charge box uart rx dma has closed...
        communication_io_mode_switch(COMMUNICATION_MODE_TX);
        hal_uart_dma_send(comm_uart, rx_idle_indicate, 2, NULL, NULL);
        osDelay(10);

        //config led2 pin as external interrupt mode...
        communication_io_mode_switch(COMMUNICATION_MODE_ENABLE_IRQ);
    }
}


int communication_io_mode_switch(enum COMMUNICATION_MODE mode)
{
    //best1400 and best1402 platform
    switch (mode)
    {
        case COMMUNICATION_MODE_TX: {
        #if defined(CHIP_BEST1400) || defined(CHIP_BEST1402)
            hal_iomux_single_wire_uart1_tx();
        #endif
        }
        break;
        case COMMUNICATION_MODE_RX: {
        #if defined(CHIP_BEST1400) || defined(CHIP_BEST1402)
            hal_iomux_single_wire_uart1_rx();
        #endif
        }
        break;
        case COMMUNICATION_MODE_ENABLE_IRQ: {
        #if defined(CHIP_BEST1400) || defined(CHIP_BEST1402)
            hal_iomux_single_wire_uart1_enable_irq(uart_rx_edge_detect_handler);
        #endif
        }
        break;
        case COMMUNICATION_MODE_DISABLE_IRQ: {
        #if defined(CHIP_BEST1400) || defined(CHIP_BEST1402)
            hal_iomux_single_wire_uart1_disable_irq();
        #endif
        }
        break;
        default:break;
    }
    return 0;
}

int communication_io_mode_init(void)
{
    communication_io_mode_switch(COMMUNICATION_MODE_RX);

    return 0;
}

inline int communication_mailbox_put(COMMUNICATION_MAIL* msg_src)
{
    osStatus status;

    COMMUNICATION_MAIL *mail_p = NULL;

    if (msg_src == NULL)
    {
        return -1;
    }

    mail_p = (COMMUNICATION_MAIL*)osMailAlloc(communication_mailbox, 0);
    if (!mail_p){
        osEvent evt;
        TRACE(0,"communication_mailbox");
        for (uint8_t i=0; i<COMMUNICATION_MAILBOX_MAX; i++){
            evt = osMailGet(communication_mailbox, 0);
            if (evt.status == osEventMail) {
                TRACE(4,"msg cnt:%d msg:%d parms:%08x/%08x", i,
                                                              ((COMMUNICATION_MAIL *)(evt.value.p))->message,
                                                              ((COMMUNICATION_MAIL *)(evt.value.p))->parms1,
                                                              ((COMMUNICATION_MAIL *)(evt.value.p))->parms2);
            }else{
                break;
            }
        }
        ASSERT(mail_p, "communication_mailbox error");
    }
    mail_p->src_thread = (uint32_t)osThreadGetId();
    mail_p->system_time = hal_sys_timer_get();
    mail_p->message = msg_src->message;
    mail_p->parms1 = msg_src->parms1;
    mail_p->parms2 = msg_src->parms2;
    status = osMailPut(communication_mailbox, mail_p);
    if (osOK == status){
        communication_mailbox_cnt++;
    }

    return (int)status;
}

inline int communication_mailbox_free(COMMUNICATION_MAIL* mail_p)
{
    osStatus status;

    status = osMailFree(communication_mailbox, mail_p);
    if (osOK == status){
        communication_mailbox_cnt--;
    }
    return (int)status;
}

inline int communication_mailbox_get(COMMUNICATION_MAIL** mail_p)
{
    osEvent evt;
    evt = osMailGet(communication_mailbox, osWaitForever);
    if (evt.status == osEventMail) {
        *mail_p = (COMMUNICATION_MAIL *)evt.value.p;
        return 0;
    }
    return -1;
}

static void communication_thread(void const *argument)
{
    while(1){
        COMMUNICATION_MAIL *mail_p = NULL;
        if (!communication_mailbox_get(&mail_p)){
            communication_process(mail_p);
            communication_mailbox_free(mail_p);
        }
    }
}

int communication_command_block_alloc(COMMAND_BLOCK **cmd_blk)
{
    *cmd_blk = (COMMAND_BLOCK *)osPoolCAlloc (command_mempool);
    ASSERT(*cmd_blk,  "%s error", __func__);
    return 0;
}

void communication_command_block_free(COMMAND_BLOCK *cmd_blk)
{
    osPoolFree(command_mempool, cmd_blk);
}

void communication_send_command(COMMAND_BLOCK *cmd_blk)
{
    COMMUNICATION_MAIL mail;
    memset(&mail, 0, sizeof(mail));
    ASSERT(cmd_blk->cmd_len <=COMMAND_LEN_MAX, "%s len error", __func__);
    mail.message = COMMUNICATION_MSG_TX_REQ;
    mail.parms2 = (uint32_t)cmd_blk;
    communication_mailbox_put(&mail);
}

static void communication_process(COMMUNICATION_MAIL* mail_p)
{
    osEvent evt;
    COMMAND_BLOCK *command_block_p;
    COMMUNICATION_MAIL msg;
    uint32_t lock;

    TRACE(2,"[%s]: %s", __func__, communication_process_log[mail_p->message]);

    memset(&msg, 0, sizeof(COMMUNICATION_MAIL));
    switch (mail_p->message) {
        case COMMUNICATION_MSG_TX_REQ:
            command_block_p = (COMMAND_BLOCK *)mail_p->parms2;
            TRACE(1,"UART TX:%d", command_block_p->cmd_len);
            DUMP8("%02x ", command_block_p->cmd_buf, command_block_p->cmd_len);
            uart_rx_dma_stop();
            communication_io_mode_switch(COMMUNICATION_MODE_TX);
            osSignalClear(communication_tid, COMMAND_TRANSMITTED_SIGNAL);
            hal_uart_dma_send(comm_uart, command_block_p->cmd_buf, command_block_p->cmd_len, NULL, NULL);
            evt = osSignalWait(COMMAND_TRANSMITTED_SIGNAL, 1000);
            if (evt.status== osEventTimeout){
                ASSERT(0, "%s osEventTimeout", __func__);
            }
            communication_command_block_free(command_block_p);
            while (!hal_uart_get_flag(comm_uart).TXFE || hal_uart_get_flag(comm_uart).BUSY){
                osThreadYield();
            }
            communication_io_mode_switch(COMMUNICATION_MODE_RX);
            if (!uart_error_detected){
                uart_rx_dma_start();
                uart_rx_idle_timer_start();
            }
            break;
        case COMMUNICATION_MSG_TX_DONE:
            break;
        case COMMUNICATION_MSG_RX_REQ:
            communication_io_mode_init();
            uart_init();
            uart_rx_dma_start();
            uart_rx_idle_timer_start();
            break;
        case COMMUNICATION_MSG_RX_DONE:
            TRACE(2,"UART RX status:%d len:%d", mail_p->parms1, mail_p->parms2);
            DUMP8("%02x ", rx_command_block_p->cmd_buf, mail_p->parms2);
            TRACE(1,"%s", rx_command_block_p->cmd_buf);

            if(communication_receive_cb != NULL)
                (*communication_receive_cb)(rx_command_block_p->cmd_buf, mail_p->parms2);

            memset(rx_command_block_p->cmd_buf, 0, mail_p->parms2);

            if (!uart_error_detected){
                uart_rx_dma_start();
                uart_rx_idle_timer_start();
            }
            break;
        case COMMUNICATION_MSG_REINIT:
            uart_deinit();
        case COMMUNICATION_MSG_INIT:
            uart_error_detected = 0;
            communication_io_mode_init();
            uart_init();
            uart_rx_dma_start();
            uart_rx_idle_timer_id = osTimerCreate(osTimer(uart_rx_idle_timer), osTimerPeriodic, NULL);
            uart_rx_idle_timer_start();
            break;
        case COMMUNICATION_MSG_RESET:
        case COMMUNICATION_MSG_BREAK:
            lock = int_lock();
            uart_rx_dma_stop();
            communication_io_mode_switch(COMMUNICATION_MODE_RX);
            hal_uart_flush(comm_uart, 0);
            uart_rx_dma_start();
            uart_error_detected = 0;
            int_unlock(lock);
            uart_rx_idle_timer_start();
            break;

        default:
            break;
    }

}

void communication_init(void)
{
    COMMUNICATION_MAIL msg;
    COMMAND_BLOCK *cmd_blk;

    memset(&msg, 0, sizeof(COMMUNICATION_MAIL));
    TRACE(1,"%s", __func__);

    if (command_mempool == NULL){
        command_mempool = osPoolCreate(osPool(command_mempool));
    }

    if (communication_mailbox == NULL){
        communication_mailbox = osMailCreate(osMailQ(communication_mailbox), NULL);
    }

    if (communication_tid == NULL){
        communication_tid = osThreadCreate(osThread(communication_thread), NULL);
    }

    if (rx_command_block_p == NULL){
        communication_command_block_alloc(&rx_command_block_p);
        memset(rx_command_block_p->cmd_buf, 0, COMMAND_LEN_MAX);
        rx_command_block_p->cmd_len = 0;
    }

    msg.message = COMMUNICATION_MSG_INIT;
    communication_mailbox_put(&msg);

    communication_command_block_alloc(&cmd_blk);
    cmd_blk->cmd_buf[0] = 0xff;
    cmd_blk->cmd_len = 1;
    communication_send_command(cmd_blk);
}

int communication_receive_register_callback(communication_receive_func_typedef p)
{
    if(p == NULL)
        return -1;

    communication_receive_cb = p;

    TRACE(1,"[%s] register receive callback success\n", __func__);

    return 0;
}

int communication_send_buf(uint8_t * buf, uint8_t len)
{
    COMMAND_BLOCK *cmd_blk;

    communication_command_block_alloc(&cmd_blk);
    memcpy(cmd_blk->cmd_buf, buf, len);
    cmd_blk->cmd_len = len;
    communication_send_command(cmd_blk);
    return 0;
}

#ifdef KNOWLES_UART_DATA
#include "knowles_uart.h"

void uart_audio_init()
{
    TRACE(1,"%s run!!!", __func__);
    init_transport();
}

void uart_audio_deinit()
{
    deinit_transport();
    reopen_uart();
}

#endif

