#include "stdio.h"
#include "ch32v30x.h"
#include "lprintf.h"
#include "chip.h"
#include "lprintf_config.h"

/************************ Stubs end */

#define DEBUG_PORT_UART_NUM         LPRINTF_UART_NUM

#define DEBUG_UART                     DEBUG_UART_NUM(DEBUG_PORT_UART_NUM)
#define DEBUG_UART_NUM(tnum)           _DEBUG_UART_NUM(tnum)
#define _DEBUG_UART_NUM(tnum)          USART##tnum

#define DEBUG_UART_CLOCK_CMD(enable)              DEBUG_UART_CLOCK_CMD_NUM(DEBUG_PORT_UART_NUM, enable)
#define DEBUG_UART_CLOCK_CMD_NUM(tnum, enable)    _DEBUG_UART_CLOCK_CMD_NUM(tnum, enable)


#if DEBUG_PORT_UART_NUM == 1
    #define _DEBUG_UART_CLOCK_CMD_NUM(tnum, enable)   RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART##tnum, enable)
#elif DEBUG_PORT_UART_NUM == 2 || DEBUG_PORT_UART_NUM == 3
    #define _DEBUG_UART_CLOCK_CMD_NUM(tnum, enable)   RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART##tnum, enable)
#else
    #define _DEBUG_UART_CLOCK_CMD_NUM(tnum, enable)   RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART##tnum, enable)
#endif

#ifdef USE_UART_5_AND_6
// UART6_TX
#define PIN_CMD_UART_TX ARG(GPIOE, RCC_APB2Periph_GPIOE, GPIO_Pin_10)
// UART5_RX
#define PIN_CMD_UART_RX ARG(GPIOE, RCC_APB2Periph_GPIOE, GPIO_Pin_9)

void fpga_uart_read(void) {
    uint8_t ch;
    static uint8_t uart_tx_cntr = 1;
    static uint8_t uart_rx_cntr = 1;
    if (UART6->STATR & USART_FLAG_TXE) {
        UART6->DATAR = uart_tx_cntr++;
        //lprintf("send ok\n");
    }
    if ((UART5->STATR & USART_FLAG_RXNE) == 0) {
        return;
    }
    ch = UART5->DATAR;
    if (ch != uart_rx_cntr) {
        lprintf("read ch=%x expected %x\n", ch, uart_rx_cntr);
        uart_rx_cntr = ch;
    }
    uart_rx_cntr++;
}

void fpga_uart_init(void) {
    USART_InitTypeDef USART_InitStructure;

    // UART5_RM
    AFIO->PCFR2 |= (1 << 19) | (1 << 21);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART6, ENABLE);
    CHIP_PIN_CONFIG(PIN_CMD_UART_TX, 0, GPIO_Mode_AF_PP);
    CHIP_PIN_CONFIG(PIN_CMD_UART_RX, 0, GPIO_Mode_IN_FLOATING);

    USART_InitStructure.USART_BaudRate = 1250000;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx;
    USART_Init(UART6, &USART_InitStructure);
    USART_Cmd(UART6, ENABLE);

    USART_InitStructure.USART_BaudRate = 1250000;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx;
    USART_Init(UART5, &USART_InitStructure);
    USART_Cmd(UART5, ENABLE);

    while (UART5->STATR & USART_FLAG_RXNE) {
        asm volatile ("" : : "r" (UART5->DATAR));
    }
}
#endif //USE_UART_5_AND_6

int lprintf_uart_init(unsigned baud, unsigned databits, t_lprintf_uart_parity parity) {
    USART_InitTypeDef USART_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    DEBUG_UART_CLOCK_CMD(ENABLE);
    CHIP_PIN_CONFIG(PIN_DEBUG_UART_TX, 0, GPIO_Mode_AF_PP);
#ifdef WCH_DEBUG_BOARD
    CHIP_PIN_CONFIG(PIN_DEBUG_UART_RX, 0, GPIO_Mode_IN_FLOATING);
#endif

    USART_InitStructure.USART_BaudRate = baud;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
#ifdef WCH_DEBUG_BOARD
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
#else
    USART_InitStructure.USART_Mode = USART_Mode_Tx;
#endif
    USART_Init(DEBUG_UART, &USART_InitStructure);
    USART_Cmd(DEBUG_UART, ENABLE);
    return 0;
}

#if 0
__attribute__((used)) int _write(int fd, char *buf, int size)
{
    for(int i = 0; i < size; i++)
    {
        while ((DEBUG_UART->STATR & USART_FLAG_TXE) == 0);
        DEBUG_UART->DATAR = *buf++;
    }

    return size;
}

void *_sbrk(ptrdiff_t incr) {
    return NULL;
}

#endif

void lprintf_putchar(char c) {
    while ((DEBUG_UART->STATR & USART_FLAG_TXE) == 0);
    DEBUG_UART->DATAR = c;
}

/*TODO: lprintf_close*/
void lprintf_close(void) {}

unsigned int chip_stack_get_unused_words (void) {
    return 0;
}
