/*
 * Copyright (c) 2022 OpenLuat & AirM2M
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "luat_base.h"
#include "luat_malloc.h"
#include "luat_msgbus.h"
#include "luat_uart.h"

#include "app_interface.h"


#define LUAT_LOG_TAG "luat.uart"
#include "luat_log.h"

// #define UART_RX_USE_DMA

//串口数量，编号从0开始
#define MAX_DEVICE_COUNT UART_MAX
//存放串口设备句柄
typedef struct
{
	timer_t *rs485_timer;
	union
	{
		uint16_t rs485_param;
		struct
		{
			uint16_t wait_time:14;
			uint16_t rx_level:1;
			uint16_t is_485used:1;
		}rs485_param_bit;
	};
	uint8_t rx_mark;
	uint8_t rs485_pin;
}serials_info;
static serials_info serials[MAX_DEVICE_COUNT+1] ={0};

static int32_t luat_uart_wait_timer_cb(void *pData, void *pParam)
{
    uint32_t uartid = (uint32_t)pParam;
    rtos_msg_t msg;
    msg.handler = l_uart_handler;
    msg.arg1 = uartid;
    msg.arg2 = 0;
    msg.ptr = NULL;
    luat_msgbus_put(&msg, 1);
    if (serials[uartid].rs485_param_bit.is_485used) {
    	GPIO_Output(serials[uartid].rs485_pin, serials[uartid].rs485_param_bit.rx_level);
    }
}

int luat_uart_exist(int uartid){
	if (uartid >= LUAT_VUART_ID_0) uartid = MAX_DEVICE_COUNT;
    if (uartid < 1 || uartid > MAX_DEVICE_COUNT){
        return 0;
    }
    return 1;
}

static int32_t luat_uart_cb(void *pData, void *pParam){
    rtos_msg_t msg;
    uint32_t uartid = (uint32_t)pData;
    uint32_t State = (uint32_t)pParam;
    uint32_t len;
//    LLOGD("luat_uart_cb pData:%d pParam:%d ",uartid,State);
    switch (State){
        case UART_CB_TX_BUFFER_DONE:
            break;
        case UART_CB_TX_ALL_DONE:
        	if (serials[uartid].rs485_param_bit.is_485used && serials[uartid].rs485_param_bit.wait_time)
        	{
        		Timer_StartUS(serials[uartid].rs485_timer, serials[uartid].rs485_param_bit.wait_time, 0);
        	}
        	else
        	{
                msg.handler = l_uart_handler;
                msg.arg1 = uartid;
                msg.arg2 = 0;
                msg.ptr = NULL;
                luat_msgbus_put(&msg, 1);
                if (serials[uartid].rs485_param_bit.is_485used && Uart_IsTSREmpty(uartid)) {
                	GPIO_Output(serials[uartid].rs485_pin, serials[uartid].rs485_param_bit.rx_level);
                }
        	}
            break;
        case UART_CB_RX_BUFFER_FULL:
        case UART_CB_RX_TIMEOUT:
            if (serials[uartid].rx_mark) return 0;
            serials[uartid].rx_mark = 1;
            len = Uart_RxBufferRead(uartid, NULL, 0);
            msg.handler = l_uart_handler;
            msg.ptr = NULL;
            msg.arg1 = uartid;
            msg.arg2 = len;
            luat_msgbus_put(&msg, 1);
            break;
        case UART_CB_RX_NEW:
            break;
        case DMA_CB_DONE:
            break;
        case UART_CB_ERROR:
            break;
        case DMA_CB_ERROR:
            LLOGD("%x", Uart_GetLastError(uartid));
            break;
	}
}

static int usb_uart_handler(lua_State *L, void* ptr,int arg1) {
    rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
    lua_pop(L, 1);
    int uart_id = msg->arg1;
    // LLOGD("msg->arg1: %d,msg->arg2: %d",msg->arg1,msg->arg2);
    if (msg->arg2 == 1) {
        lua_getglobal(L, "sys_pub");
        lua_pushstring(L, "USB_UART_INC");
        lua_pushinteger(L, uart_id);
        lua_pushinteger(L, 1);
        lua_call(L, 3, 0);
    }
    else if(msg->arg2 == 2){
        lua_getglobal(L, "sys_pub");
        lua_pushstring(L, "USB_UART_INC");
        lua_pushinteger(L, uart_id);
        lua_pushinteger(L, 0);
        lua_call(L, 3, 0);
    }
    // 给rtos.recv方法返回个空数据
    // lua_pushinteger(L, 0);
    return 0;
}

static int32_t luat_uart_usb_cb(void *pData, void *pParam){
    rtos_msg_t msg = {0};
    uint32_t uartid = (uint32_t)pData;
    uint32_t State = (uint32_t)pParam;
    uint32_t len;
    // LLOGD("luat_uart_cb pData:%d pParam:%d ",uartid,State);
    switch (State){
        case UART_CB_TX_BUFFER_DONE:
            break;
        case UART_CB_TX_ALL_DONE:
            msg.handler = l_uart_handler;
            msg.arg1 = LUAT_VUART_ID_0;
            msg.arg2 = 0;
            msg.ptr = NULL;
            luat_msgbus_put(&msg, 1);
            break;
        case UART_CB_RX_BUFFER_FULL:
            break;
        case UART_CB_RX_TIMEOUT:
            break;
        case UART_CB_RX_NEW:
            if (serials[MAX_DEVICE_COUNT].rx_mark) return 0;
            serials[MAX_DEVICE_COUNT].rx_mark = 1;
            len = Core_VUartRxBufferRead(VIRTUAL_UART0,NULL,0);
            msg.handler = l_uart_handler;
            msg.ptr = NULL;
            msg.arg1 = LUAT_VUART_ID_0;
            msg.arg2 = len;
            luat_msgbus_put(&msg, 1);
            break;
        case DMA_CB_DONE:
            break;
        case UART_CB_ERROR:
            // LLOGI("usb disconnect");
            Core_VUartBufferTxStop(VIRTUAL_UART0);
            msg.handler = usb_uart_handler;
            msg.ptr = NULL;
            msg.arg1 = LUAT_VUART_ID_0;
            msg.arg2 = 2;
            luat_msgbus_put(&msg, 1);
            break;
        case UART_CB_CONNECTED:
            //对方打开串口工具
            msg.handler = usb_uart_handler;
            msg.ptr = NULL;
            msg.arg1 = LUAT_VUART_ID_0;
            msg.arg2 = 1;
            luat_msgbus_put(&msg, 1);
		    break;
	}
}

int luat_uart_setup(luat_uart_t *uart){
    int parity = 0;
    if (uart->id > MAX_DEVICE_COUNT){
        return -1;
    }
    switch (uart->id){
        case UART_ID0:
            break;
        case UART_ID1:
            GPIO_Iomux(GPIOC_01, 3);
            GPIO_Iomux(GPIOC_00, 3);
            break;
        case UART_ID2:
            GPIO_Iomux(GPIOD_13, 0);
            GPIO_Iomux(GPIOD_12, 0);
            break;
        case UART_ID3:
            GPIO_Iomux(GPIOE_08, 2);
            GPIO_Iomux(GPIOE_09, 2);
            break;
        default:
            break;
    }
    if (uart->parity == 1)parity = UART_PARITY_ODD;
    else if (uart->parity == 2)parity = UART_PARITY_EVEN;
    int stop_bits = (uart->stop_bits)==1?UART_STOP_BIT1:UART_STOP_BIT2;
    if (uart->id >= MAX_DEVICE_COUNT) {
    	serials[MAX_DEVICE_COUNT].rx_mark = 0;
    	Core_VUartInit(VIRTUAL_UART0, uart->baud_rate, 1, (uart->data_bits), parity, stop_bits, NULL);
    } else {
    	serials[uart->id].rx_mark = 0;
        Uart_BaseInit(uart->id, uart->baud_rate, 1, (uart->data_bits), parity, stop_bits, NULL);
        serials[uart->id].rs485_param_bit.is_485used = (uart->pin485 < GPIO_NONE)?1:0;
        serials[uart->id].rs485_pin = uart->pin485;
        serials[uart->id].rs485_param_bit.rx_level = uart->rx_level;
        serials[uart->id].rs485_param_bit.wait_time = uart->delay;
        if (!serials[uart->id].rs485_timer) {
        	serials[uart->id].rs485_timer = Timer_Create(luat_uart_wait_timer_cb, uart->id, NULL);
        }
        GPIO_Config(serials[uart->id].rs485_pin, 0, serials[uart->id].rs485_param_bit.rx_level);
    }

    return 0;
}


int luat_uart_write(int uartid, void *data, size_t length){
    //printf("uid:%d,data:%s,length = %d\r\n",uartid, (char *)data, length);
    if (!luat_uart_exist(uartid)){
        return 0;
    }
    if (uartid >= MAX_DEVICE_COUNT) {
        Core_VUartBufferTx(VIRTUAL_UART0, (const uint8_t *)data, length);
    }
    else {
    	if (serials[uartid].rs485_param_bit.is_485used) GPIO_Output(serials[uartid].rs485_pin, !serials[uartid].rs485_param_bit.rx_level);
        Uart_BufferTx(uartid, (const uint8_t *)data, length);
    }
    return length;
}

int luat_uart_read(int uartid, void *buffer, size_t length){
    int ret = 0;
    if (!luat_uart_exist(uartid)){
        return 0;
    }
    if (uartid >= LUAT_VUART_ID_0) uartid = MAX_DEVICE_COUNT;
    serials[uartid].rx_mark = 0;
    if (uartid >= MAX_DEVICE_COUNT)
        ret = Core_VUartRxBufferRead(VIRTUAL_UART0, (uint8_t *)buffer,length);
    else
        ret = Uart_RxBufferRead(uartid, (uint8_t *)buffer,length);
    // LLOGD("uartid:%d buffer:%s ",uartid,buffer);
    return ret;
}

int luat_uart_close(int uartid){
    if (!luat_uart_exist(uartid)){
        return 0;
    }
    if (uartid >= LUAT_VUART_ID_0) uartid = MAX_DEVICE_COUNT;
    serials[uartid].rs485_param_bit.is_485used = 0;
    if (uartid >= MAX_DEVICE_COUNT)
        Core_VUartDeInit(VIRTUAL_UART0);
    else
        Uart_DeInit(uartid);

    return 0;
}

int luat_setup_cb(int uartid, int received, int sent){
    if (!luat_uart_exist(uartid)){
        return -1;
    }
    if (uartid >= MAX_DEVICE_COUNT)
        Core_VUartSetCb(VIRTUAL_UART0, luat_uart_usb_cb);
    else {
        Uart_SetCb(uartid, luat_uart_cb);
#ifndef UART_RX_USE_DMA
        Uart_EnableRxIrq(uartid);
#endif
    }
    return 0;
}

int luat_uart_wait_485_tx_done(int uartid)
{
	int cnt = 0;
    if (luat_uart_exist(uartid)){
        if (serials[uartid].rs485_param_bit.is_485used){
        	while(!Uart_IsTSREmpty(uartid)) {cnt++;}
        	GPIO_Output(serials[uartid].rs485_pin, serials[uartid].rs485_param_bit.rx_level);
        }
    }
    return cnt;
}
