//******************************************** Включаемые файлы *********************************************

#include "lmcal_cfg.h"
#include "lmcal_uart_port.h"

#include "gd32f4xx_rcu.h"
#include "gd32f4xx_gpio.h"
#include "gd32f4xx_usart.h"
#include "gd32f4xx_misc.h"

//******************************************* Локальные константы *******************************************

// Альтернативные функций GPIO
#define LMCAL_GPIO_AF_UART0     (GPIO_AF_7)
#define LMCAL_GPIO_AF_UART1     (GPIO_AF_7)
#define LMCAL_GPIO_AF_UART2     (GPIO_AF_7)
#define LMCAL_GPIO_AF_UART3     (GPIO_AF_8)
#define LMCAL_GPIO_AF_UART4     (GPIO_AF_8)
#define LMCAL_GPIO_AF_UART5     (GPIO_AF_8)
#define LMCAL_GPIO_AF_UART6     (GPIO_AF_8)
#define LMCAL_GPIO_AF_UART7     (GPIO_AF_8)



//******************************************** Определения типов ********************************************



//************************************ Глобальные (публичные) переменные ************************************

uart_ch_service_t lmcal_uart_port_service_var[LMCAL_UART_CHANNEL_QTY];


//************************************* Локальные (приватные) переменные ************************************

//! todo - надо или нет? нужен тест
static u16 lmcal_usart5_rx_data = 0;


//***********************************************************************************************************
//******************************** Определения локальных (приватных) функций ********************************
//***********************************************************************************************************


static fun_res_t f_enable_port_clk(u32 port_num, bool state)
{
    fun_res_t err = ERR_OK;
    if (state == true)
    {
        if (port_num == GPIOA) {
            rcu_periph_clock_enable(RCU_GPIOA);
        } 
        else if(port_num == GPIOB) {
            rcu_periph_clock_enable(RCU_GPIOB);
        }
        else if(port_num == GPIOC) {
            rcu_periph_clock_enable(RCU_GPIOC);
        }
        else if(port_num == GPIOD) {
            rcu_periph_clock_enable(RCU_GPIOD);
        }
        else if(port_num == GPIOE) {
            rcu_periph_clock_enable(RCU_GPIOE);
        }
        else if(port_num == GPIOF) {
                rcu_periph_clock_enable(RCU_GPIOF);
        }
        else if(port_num == GPIOG) {
            rcu_periph_clock_enable(RCU_GPIOG);
        }
        else if(port_num == GPIOH) {
            rcu_periph_clock_enable(RCU_GPIOH);
        }
        else if(port_num == GPIOI) {
            rcu_periph_clock_enable(RCU_GPIOI);
        }
        else if(port_num == LMCAL_UART_PORT_NONE) {
            
        }
        else {
            err = ERR_INVALID_ARGUMENT;
        }
    }
    else 
    {
        if (port_num == GPIOA) {
            rcu_periph_clock_disable(RCU_GPIOA);
        } 
        else if(port_num == GPIOB) {
            rcu_periph_clock_disable(RCU_GPIOB);
        }
        else if(port_num == GPIOC) {
            rcu_periph_clock_disable(RCU_GPIOC);
        }
        else if(port_num == GPIOD) {
            rcu_periph_clock_disable(RCU_GPIOD);
        }
        else if(port_num == GPIOE) {
            rcu_periph_clock_disable(RCU_GPIOE);
        }
        else if(port_num == GPIOF) {
            rcu_periph_clock_disable(RCU_GPIOF);
        }
        else if(port_num == GPIOG) {
            rcu_periph_clock_disable(RCU_GPIOG);
        }
        else if(port_num == GPIOH) {
            rcu_periph_clock_disable(RCU_GPIOH);
        }
        else if(port_num == GPIOI) {
            rcu_periph_clock_disable(RCU_GPIOI);
        }
        else if(port_num == LMCAL_UART_PORT_NONE) {
            
        }
        else {
            err = ERR_INVALID_ARGUMENT;
        }
    }
    return err;
}


static fun_res_t f_enable_uart_clk(u32 uart_num, bool state)
{
    fun_res_t err = ERR_OK;
    if (state == true)
    {
        if (uart_num == LMCAL_UART0) {
            rcu_periph_clock_enable(RCU_USART0);
        } 
        else if(uart_num == LMCAL_UART1) {
            rcu_periph_clock_enable(RCU_USART1);
        }
        else if(uart_num == LMCAL_UART2) {
            rcu_periph_clock_enable(RCU_USART2);
        }
        else if(uart_num == LMCAL_UART3) {
            rcu_periph_clock_enable(RCU_UART3);
        }
        else if(uart_num == LMCAL_UART4) {
            rcu_periph_clock_enable(RCU_UART4);
        }
        else if(uart_num == LMCAL_UART5) {
            rcu_periph_clock_enable(RCU_USART5);
        }
        else if(uart_num == LMCAL_UART6) {
            rcu_periph_clock_enable(RCU_UART6);
        }
        else if(uart_num == LMCAL_UART7) {
            rcu_periph_clock_enable(RCU_UART7);
        }
        else {
            err = ERR_INVALID_ARGUMENT;
        }
    }
    else 
    {
    if (uart_num == LMCAL_UART0) {
            rcu_periph_clock_disable(RCU_USART0);
        } 
        else if(uart_num == LMCAL_UART1) {
            rcu_periph_clock_disable(RCU_USART1);
        }
        else if(uart_num == LMCAL_UART2) {
            rcu_periph_clock_disable(RCU_USART2);
        }
        else if(uart_num == LMCAL_UART3) {
            rcu_periph_clock_disable(RCU_UART3);
        }
        else if(uart_num == LMCAL_UART4) {
            rcu_periph_clock_disable(RCU_UART4);
        }
        else if(uart_num == LMCAL_UART5) {
            rcu_periph_clock_disable(RCU_USART5);
        }
        else if(uart_num == LMCAL_UART6) {
            rcu_periph_clock_disable(RCU_UART6);
        }
        else if(uart_num == LMCAL_UART7) {
            rcu_periph_clock_disable(RCU_UART7);
        }
        else {
            err = ERR_INVALID_ARGUMENT;
        }
    }
    return err;
}


// Включить\отключить тактирование всей необходимой периферии
static fun_res_t f_enable_clk(u32 port_num_tx, 
                              u32 port_num_rx,
                              u32 port_num_clk,
                              u32 uart_num,
                              bool state)
{
    fun_res_t err = ERR_OK;

    err = f_enable_port_clk(port_num_tx, state);
    L_CHECK_ERR(err);

    err = f_enable_port_clk(port_num_rx, state);
    L_CHECK_ERR(err);

    err = f_enable_port_clk(port_num_clk, state);
    L_CHECK_ERR(err);

    err = f_enable_uart_clk(uart_num, state);
    L_CHECK_ERR(err);

    L_EXIT;
    return err;
}


static fun_res_t f_gpio_init(u32 uart_num,
                            u32 port_num_tx,  u32 pin_num_tx,
                            u32 port_num_rx,  u32 pin_num_rx,
                            u32 port_num_clk, u32 pin_num_clk)
{
    fun_res_t err = ERR_OK;
    uint32_t gpio_alt_fun = 0;
    
    if (uart_num == LMCAL_UART0)
    {
        gpio_alt_fun = LMCAL_GPIO_AF_UART0;
        // TX - A9
        // RX - A10
        if ((LMCAL_UART_PORTA == port_num_tx)  && (LMCAL_UART_PIN9 == pin_num_tx))
        {
            // TX
            gpio_af_set(port_num_tx, gpio_alt_fun, pin_num_tx);
            gpio_mode_set(port_num_tx, GPIO_MODE_AF, GPIO_PUPD_PULLUP, pin_num_tx);
            gpio_output_options_set(port_num_tx, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, pin_num_tx);
        }

        if ((LMCAL_UART_PORTA == port_num_rx)  && (LMCAL_UART_PIN10 == pin_num_rx))
        {
            // RX
            gpio_af_set(port_num_rx, gpio_alt_fun, pin_num_rx);
            gpio_mode_set(port_num_rx, GPIO_MODE_AF, GPIO_PUPD_PULLUP, pin_num_rx);
            gpio_output_options_set(port_num_rx, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, pin_num_rx);
        }
    }
    else if (uart_num == LMCAL_UART5)
    {
        gpio_alt_fun = LMCAL_GPIO_AF_UART5;
        // TX - A11
        // RX - A12
        // CLK - G7
        if ((LMCAL_UART_PORTA == port_num_tx)  && (LMCAL_UART_PIN11 == pin_num_tx) &&
            (LMCAL_UART_PORTA == port_num_rx)  && (LMCAL_UART_PIN12 == pin_num_rx) &&
            (LMCAL_UART_PORTG == port_num_clk) && (LMCAL_UART_PIN7 == pin_num_clk))
        {
            // TX RX
            gpio_af_set(GPIOA, gpio_alt_fun, GPIO_PIN_11 | GPIO_PIN_12);
            gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_11 | GPIO_PIN_12);
            gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11 | GPIO_PIN_12);

            // CLK
            gpio_af_set(GPIOG, gpio_alt_fun, GPIO_PIN_7);
            gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_7);
            gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
        }
        else
        {
            err = ERR_NOT_IMPLEMENTED;
        }
    }
    else
    {
        err = ERR_NOT_IMPLEMENTED;
    }

    return err;
}


static fun_res_t f_uart_init(u32 uart_num, u32 mode, u32 parity, u32 baudrate)
{
    fun_res_t err = ERR_OK;

    if (uart_num == LMCAL_UART0)
    {
        if (mode == 1)
        {
            // Асинхронный режим
            err = ERR_NOT_IMPLEMENTED;
            L_CHECK_ERR(err);
        }

        // UART mode
        usart_deinit(uart_num);
        usart_baudrate_set(uart_num, baudrate);
        usart_parity_config(uart_num, parity);
        usart_receive_config(uart_num, USART_RECEIVE_ENABLE);
        usart_transmit_config(uart_num, USART_TRANSMIT_ENABLE);
        usart_interrupt_enable(uart_num, USART_INT_RBNE);
        nvic_irq_enable(USART0_IRQn, 0, 0);
        
    }
    else if (uart_num == LMCAL_UART5)
    {
        if (mode == 1)
        {
            // Асинхронный режим - не поддерживается
            err = ERR_NOT_IMPLEMENTED;
            L_CHECK_ERR(err);
        }

        // USART mode, 9 bit
        usart_deinit(uart_num);

        usart_oversample_config(uart_num, USART_OVSMOD_8);
        usart_baudrate_set(uart_num, baudrate);
        usart_word_length_set(uart_num, USART_WL_9BIT);
        usart_synchronous_clock_enable(uart_num);
        usart_synchronous_clock_config(uart_num, USART_CLEN_EN, USART_CPH_2CK, USART_CPL_HIGH);
        usart_data_first_config(uart_num, USART_MSBF_MSB);
        usart_parity_config(uart_num, parity);
        usart_receive_config(uart_num, USART_RECEIVE_ENABLE);
        usart_transmit_config(uart_num, USART_TRANSMIT_ENABLE);
    }
    else
    {
        err = ERR_NOT_IMPLEMENTED;
    }

    L_EXIT;
    return err;
}



//***********************************************************************************************************
//******************************** Определения процедур обработки прерываний ********************************
//***********************************************************************************************************

void USART0_IRQHandler(void)
{
    // Обработка RX прерывания
    if (usart_interrupt_flag_get(LMCAL_UART0, USART_INT_FLAG_RBNE) != RESET)
    {
        // Не сбрасывать флаг вручную, чтение DR сбросит его
        // Вызов RX колбека, который прочитает данные
        u8 cur_channel = 0;
        for (u8 i = LMCAL_UART_CHANNEL0; i < LMCAL_UART_CHANNEL_QTY; i++)
        {
            if (lmcal_uart_port_service_var[i].init && 
                lmcal_uart_port_service_var[i].uart_num == LMCAL_UART0)
            {
                cur_channel = i;
                break;
            }
        }
        if (lmcal_uart_port_service_var[cur_channel].rx_callback != NULL)
        {
            lmcal_uart_port_service_var[cur_channel].rx_callback();
        }
    }
}
 


void USART1_IRQHandler(void)
{
}

void USART2_IRQHandler(void)
{
}

void UART3_IRQHandler(void)
{ 
}

void UART4_IRQHandler(void)
{ 
}

void USART5_IRQHandler(void)
{
}

void UART6_IRQHandler(void)
{
}


void UART7_IRQHandler(void)
{
}


//***********************************************************************************************************
//******************************* Определения глобальных (публичных) функций ********************************
//***********************************************************************************************************

fun_res_t lmcal_uart_port_init(u8 param)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(param);
    // Опциональная функция

    return err;
}



fun_res_t lmcal_uart_port_init_periph(u32 uart_num,
                                      u32 mode,
                                      u32 port_num_tx,
                                      u32 pin_num_tx,
                                      u32 port_num_rx,
                                      u32 pin_num_rx,
                                      u32 port_num_clk,
                                      u32 pin_num_clk,
                                      u32 parity,
                                      u32 baudrate)
{
    fun_res_t err = ERR_OK;
    
    err = f_enable_clk(port_num_tx, port_num_rx, port_num_clk, uart_num, true);
    L_CHECK_ERR(err);

    err = f_gpio_init(uart_num,
                      port_num_tx,  pin_num_tx,
                      port_num_rx,  pin_num_rx,
                      port_num_clk, pin_num_clk);
    L_CHECK_ERR(err);

    err = f_uart_init(uart_num, mode, parity, baudrate);
    L_CHECK_ERR(err);

    L_EXIT;
    return err;
}



fun_res_t lmcal_uart_port_deinit(u8 param)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(param);
    // Опциональная функция

    return err;
}



fun_res_t lmcal_uart_port_deinit_periph(u32 uart_num,
                                        u32 port_num_tx,
                                        u32 pin_num_tx,
                                        u32 port_num_rx,
                                        u32 pin_num_rx,
                                        u32 port_num_clk,
                                        u32 pin_num_clk)
{
    fun_res_t err = ERR_OK;
    
    if (uart_num == LMCAL_UART0)
    {
        usart_deinit(USART0);
    }
    else if (uart_num == LMCAL_UART5)
    {
        usart_deinit(USART5);
    }

    // Добавить деинициализацию пинов
    #warning "TODO lmcal_uart_port_deinit_periph()"

    L_UNUSED(port_num_tx);
    L_UNUSED(pin_num_tx);
    L_UNUSED(port_num_rx);
    L_UNUSED(pin_num_rx);
    L_UNUSED(port_num_clk);
    L_UNUSED(pin_num_clk);

    return err;
}



fun_res_t lmcal_uart_port_get_status(u32 uart_num, lmcal_ch_sts_t* sts)
{
    fun_res_t err = ERR_NOT_IMPLEMENTED;

    err = L_ASSERT(((uart_num == LMCAL_UART0) || (uart_num == LMCAL_UART5)));
    L_CHECK_ERR(err);

    // Может быть полезно при асинхронном обмене, при передаче буфера
    // Варианты - смотреть статус UART или DMA 

    // todo - проверять флаги ошибок
    if (SET == usart_flag_get(uart_num, USART_FLAG_TC))
    {
        // Передача завершена
        *sts = CH_STS_IDLE;
    }
    else
    {
        *sts = CH_STS_BUSY;
    }

    L_EXIT;
    return err;
}



fun_res_t lmcal_uart_port_enable(u32 uart_num, bool new_state)
{
    fun_res_t err = ERR_OK;
    err = L_ASSERT(((uart_num == LMCAL_UART0) || (uart_num == LMCAL_UART5)));
    L_CHECK_ERR(err);

    if (new_state == true)
    {
        usart_enable(uart_num);
    }
    else
    {
        while(RESET == usart_flag_get(uart_num, USART_FLAG_TC));
        usart_disable(uart_num);
    }
        
    L_EXIT;
    return err;
}



fun_res_t lmcal_uart_port_write(u32 uart_num, u32 mode, u16 data)
{
    fun_res_t err = ERR_OK;

    // UART
    if (mode == 1)
    {
        // Асинхронный режим
        while(RESET == usart_flag_get(uart_num, USART_FLAG_TBE));
        USART_DATA(uart_num) = data; 
    }
    else
    {
        if (uart_num != LMCAL_UART5)
        {
            // Синхронный режим
            USART_DATA(uart_num) = data;
            while(RESET == usart_flag_get(uart_num, USART_FLAG_TBE));
        }
        else if (uart_num == LMCAL_UART5)
        {
            USART_DATA(uart_num) = data;
            while(RESET == usart_flag_get(uart_num, USART_FLAG_TC));

            // while(RESET == usart_flag_get(uart_num, USART_FLAG_RBNE));
            // lmcal_usart5_rx_data = USART_DATA(USART5) & 0x1FF;
            // while(RESET == usart_flag_get(uart_num, USART_FLAG_TC));
        }

    }

    return err;
}


fun_res_t lmcal_uart_port_read(u32 uart_num, u32 mode, u16* data)
{
    fun_res_t err = ERR_OK;
    
    if (mode == 1)
    {
        // Асинхронный режим
        *data = USART_DATA(uart_num) & 0x1FF;
    }
    else
    {
        if (uart_num != LMCAL_UART5)
        {
            // Синхронный режим
            while(RESET == usart_flag_get(uart_num, USART_FLAG_RBNE));
            *data = USART_DATA(uart_num) & 0x1FF;
        }
        else if (uart_num == LMCAL_UART5)
        {
            *data = USART_DATA(uart_num) & 0x1FF;

            // *data = lmcal_usart5_rx_data;
        }


    }

    return err;
}


fun_res_t lmcal_uart_port_write_buf(u32 uart_num, u32 mode, u8* data, u16 size)
{
    fun_res_t err = ERR_NOT_IMPLEMENTED;

    #warning "TODO lmcal_uart_port_write_buf()"

    L_UNUSED(uart_num);
    L_UNUSED(mode);
    L_UNUSED(data);
    L_UNUSED(size);

    // if (mode == 1)
    // {
    //     // Асинхронный режим 
    // }
    // else
    // {
    //     // Синхронный режим
    // }

    return err;
}


fun_res_t lmcal_uart_port_read_buf(u32 uart_num, u32 mode, u8* data, u16 size)
{
    fun_res_t err = ERR_NOT_IMPLEMENTED;

    #warning "TODO lmcal_uart_port_read_buf()"

    L_UNUSED(uart_num);
    L_UNUSED(mode);
    L_UNUSED(data);
    L_UNUSED(size);

    // if (mode == 1)
    // {
    //     // Асинхронный режим 
    // }
    // else
    // {
    //     // Синхронный режим
    // }

    return err;
}


//*********************************************** КОНЕЦ ФАЙЛА ***********************************************
