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

#include "lmcal_cfg.h"
#include "lmcal_gpio.h"
#include "lmcal_gpio_port.h"

#include "gd32f4xx_gpio.h"


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


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

// Переменные канала для работы прерываний
// typedef struct
// {
//     u8                   port_num;
//     u8                   pin_num;
//     void_fun_t           callback;
// } gpio_ch_irq_service_t;


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



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

static fun_res_t f_enable_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 {
            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 {
            err = ERR_INVALID_ARGUMENT;
        }
    }
    return err;
}



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

void EXTI0_IRQHandler(void)
{
    // Примечание:
    // 1) На векторе может быть несколько ножек т.е. нужно читать флаги и 
    // найти нужную ножку.
    // 2) сброс прерывания
    // 3) вызвать колбек функцию (если была определена)
}

void EXTI1_IRQHandler(void)
{
}

void EXTI2_IRQHandler(void)
{
}

void EXTI3_IRQHandler(void)
{
}

void EXTI4_IRQHandler(void)
{
}

void EXTI5_9_IRQHandler(void)
{
}

void EXTI10_15_IRQHandler(void)
{
}


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

fun_res_t lmcal_gpio_port_init(u8 param)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(param);

    // Опциональная функция

    // Примеры использования:
    // - аппаратный сброс периферии
    // - включение тактирования
    // - настройка структур данных для работы функции *_init_pin() инициализации каналов.

    
    return err;
}



fun_res_t lmcal_gpio_port_init_periph(u32   port_num,
                                      u32   pin_num,
                                      u32   mode,
                                      u32   out_type,
                                      u32   pull_type,
                                      u32   irq_mode,
                                      bool  state)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(irq_mode);

    // Настройка тактирования
    err = f_enable_clk(port_num, true);
    L_CHECK_ERR(err);

    // Настройка
    gpio_mode_set(port_num, 
                mode, 
                pull_type,
                pin_num);
    gpio_output_options_set(port_num, 
                            out_type, 
                            GPIO_OSPEED_50MHZ,
                            pin_num);


    // Установка начального значения
    gpio_bit_write(port_num, pin_num, state);

    L_EXIT;
    return err;    
}



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

    return err;
}



fun_res_t lmcal_gpio_port_deinit_periph(u32 port_num, u32 pin_num)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(port_num);
    L_UNUSED(pin_num);

    #warning "TODO lmcal_gpio_port_deinit_periph()"
    // Настройка ножек на вход.
    // Отключение подтяжек.
    // Тактирование не отключать!
    
    return err;
}



fun_res_t lmcal_gpio_port_write_pin(u32 port_num, 
                                    u32 pin_num,
                                    bool state)
{
    fun_res_t err = ERR_OK;

    gpio_bit_write(port_num, pin_num, state);

    return err;
}



fun_res_t lmcal_gpio_port_read_pin(u32 port_num, 
                                   u32 pin_num,
                                   bool* const state)
{
    fun_res_t err = ERR_OK;
    u16 port_state = 0U;

    // Читаю состояние порта
    port_state = gpio_input_port_get(port_num);

    // Получаю состояние ножки через сдвиг и маску
    *state = (bool)((port_state >> pin_num) & 0x01);

    return err;
}


fun_res_t lmcal_gpio_port_set_callback(u32 port_num, u32 pin_num, void_fun_t function)
{
    fun_res_t err = ERR_OK;
    L_UNUSED(port_num);
    L_UNUSED(pin_num);
    L_UNUSED(function);

    #warning "TODO lmcal_gpio_port_set_callback()"

    return err;
}


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