#include <rtdevice.h>

#include "gtypes.h"
#include "gh_uart.h"
#include "gh_debug_rct.h"
#include "gd_uart.h"
#include "gd_int.h"

/*---------------------------------------------------------------------------*/
/* local defines                                                             */
/*---------------------------------------------------------------------------*/

#define UART_IRQ_CLEAR_ALL   0x3 /* IRQ CLR Register: rx_IRQ, rx_parity_IRQ  */
#define UART_RISING_TXE_RXE  0x3 /* for UART_control register:               */
                                 /* clock edge polarity = rising,            */
                                 /* TX = enable, RX = enable                 */

/* UART[x]_FC_REG */
#define UART_FC_RX_ONECHAR              0x0 /* RCVR_Trigger: FIFO has 1 char */
#define UART_FC_RX_QUARTER_FULL         0x1 /* RCVR_Trigger: FIFO is one-fourth to full */
#define UART_FC_RX_HALF_FULL            0x2 /* RCVR_Trigger: FIFO is half to full */
#define UART_FC_RX_2_TO_FULL            0x3 /* RCVR_Trigger: FIFO is 2 char to full */
#define UART_FC_TX_EMPTY                0x0 /* TX_Empty_Trigger:  FIFO is empty */
#define UART_FC_TX_2_IN_FIFO            0x1 /* TX_Empty_Trigger:  FIFO has 2 char */
#define UART_FC_TX_QUATER_IN_FIFO       0x2 /* TX_Empty_Trigger:  FIFO is one-fourth to full */
#define UART_FC_TX_HALF_IN_FIFO         0x3 /* TX_Empty_Trigger:  FIFO is half to full */

/* UART[x]_II_REG */
#define UART_II_MODEM_STATUS_CHANGED    0x0
#define UART_II_NO_INT_PENDING          0x1
#define UART_II_THR_EMPTY               0x2
#define UART_II_RCV_DATA_AVAIL          0x4
#define UART_II_RCV_STATUS              0x6
#define UART_II_CHAR_TIMEOUT            0xc

#define MAX_UART_CNT    (3)

/*---------------------------------------------------------------------------*/
/* local data types                                                          */
/*---------------------------------------------------------------------------*/

struct gk_uart
{
    u8 index;
};

/*---------------------------------------------------------------------------*/
/* local data                                                                */
/*---------------------------------------------------------------------------*/

static struct gk_uart uart[MAX_UART_CNT] =
{
    {0},{1},{2}
};

static GD_HANDLE intHandle[MAX_UART_CNT];
static struct rt_serial_device serial[MAX_UART_CNT];

/*---------------------------------------------------------------------------*/
/* local  functions                                                          */
/*---------------------------------------------------------------------------*/

static void uartSetBaudrate(U32 index, U32 baudRate)
{
    U32 brdi;
    GH_PLL_set_SCALER_UART(0x01);
    brdi = (48000000/2) * 10 / baudRate / 16;
    if (brdi % 10 >= 5)
        brdi = (brdi / 10) + 1;
    else
        brdi = (brdi / 10);
    GH_UART_set_LCR_dlab(index, 1);
    GH_UART_set_DLL_BaudDivint_L(index, brdi & 0xff);
    GH_UART_set_DLH_BaudDivint_H(index, (brdi >> 8) & 0xff);
    GH_UART_set_LCR_dlab(index, 0);
}

static void uartSetDataBits(U32 index, U32 dataBits)
{
    U32 data_bits = dataBits;
    // 0 = use 5 data bits
    // 1 = use 6 data bits
    // 2 = use 7 data bits
    // 3 = use 8 data bits
    //data_bits -= GD_UART_5_DATATBITS;

    if(GH_UART_get_LCR_cls(index) != data_bits)
    {
         GH_UART_set_LCR_cls(index, data_bits);
    }
}

static void uartSetStopBits(U32 index, U32 stopBits)
{
    // 0 = use 1 stop bit
    // 1 = use 2 stop bits
    if(GH_UART_get_LCR_stop(index) != stopBits)
    {
         GH_UART_set_LCR_stop(index, stopBits);
    }
}

static void uartSetParity(U32 index, U32 parity)
{
    switch(parity)
    {
    case GD_UART_NO_PARITY:
        if(GH_UART_get_LCR_pen(index))
        {
            GH_UART_set_LCR_pen(index, 0);
        }
        break;

    case GD_UART_ODD_PARITY:
        if(!GH_UART_get_LCR_pen(index))
        {
            GH_UART_set_LCR_pen(index, 1);
        }
        if(GH_UART_get_LCR_eps(index))
        {
            GH_UART_set_LCR_eps(index, 0);
        }
        break;

    case GD_UART_EVEN_PARITY:
        if(!GH_UART_get_LCR_pen(index))
        {
            GH_UART_set_LCR_pen(index, 1);
        }
        if(!GH_UART_get_LCR_eps(index))
        {
            GH_UART_set_LCR_eps(index, 1);
        }
        break;

    default:
        break;
    }
}

static void uartSetFlowControl(U32 index, U32 flowCtrl)
{
    if(index == 0)
    {
        // In UART0, only the Loopback bit is used and flow control is not supported.
        // In UART1, all the bits are used.
        if((flowCtrl == 0) || (flowCtrl == 0x10))
        {
            if(GH_UART_get_MCR(index) != flowCtrl)
            {
                GH_UART_set_MCR(index, flowCtrl);
            }
        }
        return;
    }
    if(GH_UART_get_MCR(index) != flowCtrl)
    {
        GH_UART_set_MCR(index, flowCtrl);
    }
}

static void uartISR(void)
{
    U8 interruptID;
    interruptID = GH_UART_get_IIR_interrupt_id(0);

    switch (interruptID)
    {
        case UART_II_MODEM_STATUS_CHANGED:
        case UART_II_NO_INT_PENDING:
            break;
        case UART_II_THR_EMPTY:
            rt_hw_serial_isr(&serial[0], RT_SERIAL_EVENT_TX_DONE);
            break;
        case UART_II_RCV_DATA_AVAIL:
        case UART_II_RCV_STATUS:
            rt_hw_serial_isr(&serial[0], RT_SERIAL_EVENT_RX_IND);
            break;
        case UART_II_CHAR_TIMEOUT:
            GH_UART_get_RBR_Data(0);
            rt_hw_serial_isr(&serial[0], RT_SERIAL_EVENT_RX_TIMEOUT);
            break;
        default:
            break;
    }

}
static void uartISR1(void)
{
    U8 interruptID;
    interruptID = GH_UART_get_IIR_interrupt_id(1);

    switch (interruptID)
    {
        case UART_II_MODEM_STATUS_CHANGED:
        case UART_II_NO_INT_PENDING:
            break;
        case UART_II_THR_EMPTY:
            rt_hw_serial_isr(&serial[1], RT_SERIAL_EVENT_TX_DONE);
            break;
        case UART_II_RCV_DATA_AVAIL:
        case UART_II_RCV_STATUS:
            rt_hw_serial_isr(&serial[1], RT_SERIAL_EVENT_RX_IND);
            break;
        case UART_II_CHAR_TIMEOUT:
            GH_UART_get_RBR_Data(1);
            rt_hw_serial_isr(&serial[1], RT_SERIAL_EVENT_RX_TIMEOUT);
            break;
        default:
            break;
    }

}

static void uartISR2(void)
{
    U8 interruptID;
    interruptID = GH_UART_get_IIR_interrupt_id(2);

    switch (interruptID)
    {
        case UART_II_MODEM_STATUS_CHANGED:
        case UART_II_NO_INT_PENDING:
            break;
        case UART_II_THR_EMPTY:
            rt_hw_serial_isr(&serial[2], RT_SERIAL_EVENT_TX_DONE);
            break;
        case UART_II_RCV_DATA_AVAIL:
        case UART_II_RCV_STATUS:
            rt_hw_serial_isr(&serial[2], RT_SERIAL_EVENT_RX_IND);
            break;
        case UART_II_CHAR_TIMEOUT:
            GH_UART_get_RBR_Data(2);
            rt_hw_serial_isr(&serial[2], RT_SERIAL_EVENT_RX_TIMEOUT);
            break;
        default:
            break;
    }

}


static GERR uartSetIntMode(U8 channel)
{

    GD_UART_STATE_MACHINE_S* uart_handle_ptr = NULL;
    GD_INT_OPEN_PARAMS_S intParams;
    GERR                 ret = GD_OK;

    intParams.sensitivity    = GD_INT_LEVEL_HIGH;    //hhl note: check this value.
    intParams.active         = GD_INT_INVERT_IRQ;
    intParams.priority       = GD_INT_MID_PRIORITY;

    if(channel == 0)
    {
        intParams.type           = (S8)GD_INT_UART_IRQ;
        intParams.isrFct.lowPrio = uartISR;
        ret = GD_INT_Open(&intParams, &intHandle[0]);
    }
    else if(channel == 1)
    {
        intParams.type           = (S8)GD_INT_UART1_IRQ;
        intParams.isrFct.lowPrio = uartISR1;
        ret = GD_INT_Open(&intParams, &intHandle[1]);
    }
    else
    {
        intParams.type           = (S8)GD_INT_UART2_IRQ;
        intParams.isrFct.lowPrio = uartISR2;
        ret = GD_INT_Open(&intParams, &intHandle[2]);
    }

    return ret;
}

/**
* UART device in RT-Thread
*/
static rt_err_t gk_uart_configure(struct rt_serial_device *serial,
                                struct serial_configure *cfg)
{
    int div;
    GD_UART_DATABITS_E data_mode;
    GD_UART_STOPBITS_E stop_mode;
    GD_UART_PARITY_E parity_mode;
    struct gk_uart *uart;

    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);
    uart = (struct gk_uart *)serial->parent.user_data;

    switch (cfg->data_bits)
    {
        case DATA_BITS_8:
            data_mode = GD_UART_8_DATATBITS;
            break;
        case DATA_BITS_7:
            data_mode = GD_UART_7_DATATBITS;
            break;
        case DATA_BITS_6:
            data_mode = GD_UART_6_DATATBITS;
            break;
        case DATA_BITS_5:
            data_mode = GD_UART_5_DATATBITS;
            break;
        default:
            data_mode = GD_UART_8_DATATBITS;
            break;
    }

    switch (cfg->stop_bits)
    {
        case STOP_BITS_2:
            stop_mode = GD_UART_20_STOPBITS;//UART_STOP_BIT2;
            break;
        case STOP_BITS_1:
        default:
            stop_mode = GD_UART_10_STOPBITS;
            break;
    }

    switch (cfg->parity)
    {
        case PARITY_ODD:
            parity_mode = GD_UART_ODD_PARITY;
            break;
        case PARITY_EVEN:
            parity_mode = GD_UART_EVEN_PARITY;
            break;
        case PARITY_NONE:
        default:
            parity_mode = GD_UART_NO_PARITY;
            break;
    }

    uartSetBaudrate(uart->index,cfg->baud_rate);
    uartSetDataBits(uart->index,   data_mode);
    uartSetParity(uart->index,     parity_mode);
    uartSetStopBits(uart->index,   stop_mode);
    uartSetFlowControl(uart->index,0);

    return RT_EOK;
}

#define RT_DEVICE_CTRL_GET_CONFIG 0xFF

static rt_err_t gk_uart_control(struct rt_serial_device *serial,
                              int cmd, void *arg)
{
    struct gk_uart* uart;

    RT_ASSERT(serial != RT_NULL);
    uart = (struct gk_uart *)serial->parent.user_data;

    switch (cmd)
    {
        case RT_DEVICE_CTRL_CLR_INT:
            /* disable rx irq */
            GD_INT_Enable(&intHandle[uart->index],0);

            GH_UART_set_IER_erbfi(uart->index, 0);

            break;
        case RT_DEVICE_CTRL_SET_INT:
            /* enable rx irq */

            GH_UART_set_FCR_FIFO_Enable(uart->index, 1);
            GH_UART_set_FCR_RCVR_Trigger(uart->index, UART_FC_RX_ONECHAR);
            GH_UART_set_FCR_TX_Empty_Trigger(uart->index, UART_FC_TX_EMPTY);
            GH_UART_set_FCR_XMIT_FIFO_Reset(uart->index, 1);
            GH_UART_set_FCR_RCVR_FIFO_Reset(uart->index, 1);
            GH_UART_set_IER_etbei(uart->index, 0); //Turn off THRE interrupt

            uartSetIntMode(uart->index);

            GD_INT_Enable(&intHandle[uart->index],1);

            GH_UART_set_IER_erbfi(uart->index,1);
            GH_UART_set_IER_elsi(uart->index,1);

            break;

        case RT_DEVICE_CTRL_GET_CONFIG:
            if(!arg)
            {
                rt_kprintf("%s,line=%d,param is NULL!\n",__FUNCTION__,__LINE__);
                return RT_ERROR;
            }

            *((struct serial_configure *)arg) = serial->config;

            break;

        default:
            break;
    }

    return RT_EOK;
}

static int gk_uart_putc(struct rt_serial_device *serial, char c)
{
    struct gk_uart *uart = serial->parent.user_data;
    unsigned int ret;
    ret = GH_UART_get_LSR_temt(uart->index);

    if(serial->parent.open_flag & RT_DEVICE_FLAG_INT_TX){

        GH_UART_set_THR_Data(uart->index, c);
        if (GH_UART_get_IER_etbei(uart->index) == 0)
        {
            GH_UART_set_IER_etbei(uart->index, 1); //Turn on THRE interrupt
        }
        return 1;
    }
    else
    {
        while(!GH_UART_get_LSR_temt(uart->index));
        GH_UART_set_THR_Data(uart->index, c);
        return 1;
    }

}

static int gk_uart_getc(struct rt_serial_device *serial)
{
    struct gk_uart *uart = serial->parent.user_data;

    if(!GH_UART_get_LSR_dr(uart->index))
        return -1;

    return GH_UART_get_RBR_Data(uart->index);

}

static const struct rt_uart_ops gk_uart_ops =
{
    gk_uart_configure,
    gk_uart_control,
    gk_uart_putc,
    gk_uart_getc,
};

/**
 * This function will handle init uart
 */
void rt_hw_uart_init(void)
{
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    int i;
    char devname[6];

    for(i=0; i<MAX_UART_CNT; i++)
    {
        serial[i].ops = &gk_uart_ops;
        serial[i].config = config;

        rt_sprintf(devname,"uart%d",i);

        rt_hw_serial_register(&serial[i], devname,
                RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_STREAM ,
                &uart[i]);
    }
}


int GM_Printf(const char *__format, ...)
{
    rt_kprintf(__format);

    return 0;
}