#include "at32f403a_407.h"
#include "digital_input.h"
#include "FreeRTOS.h"
#include "task.h"
#include "settings_api.h"
#include "io_utils.h"
#include "mux.h"
#include <stdio.h>

#if defined (MDIO_88)

// Цифровые входы
// *port; pin; mode; port_source; pin_source; deb_counter; p_flag; cnt;
din_t d_inputs[DI_NUMBER] = {
    {GPIOB, GPIO_PINS_11, 0, 0, 0, false, false}, // 1   
    {GPIOB, GPIO_PINS_10, 0, 0, 0, false, false}, // 2
    {GPIOB, GPIO_PINS_1,  0, 0, 0, false, false}, // 3 перенесли {GPIOB, GPIO_PINS_1,  1, GPIO_PORT_SOURCE_GPIOB, GPIO_PINS_SOURCE1,  0},
    {GPIOB, GPIO_PINS_0,  0, 0, 0, false, false}, // 4 перенесли {GPIOB, GPIO_PINS_0,  1, GPIO_PORT_SOURCE_GPIOB, GPIO_PINS_SOURCE0,  0},
    {GPIOA, GPIO_PINS_3,  0, 0, 0, false, false}, // 5
    {GPIOA, GPIO_PINS_2,  0, 0, 0, false, false}, // 6
    {GPIOA, GPIO_PINS_1,  0, 0, 0, false, false}, // 7
    {GPIOA, GPIO_PINS_0,  0, 0, 0, false, false}  // 8
};

// Датчики обрыва нагрузки
simple_gpio_t di_load[DI_NUMBER] = {
    {GPIOD, GPIO_PINS_0},
    {GPIOD, GPIO_PINS_1},
    {GPIOC, GPIO_PINS_13},
    {GPIOC, GPIO_PINS_0},
    {GPIOC, GPIO_PINS_10},
    {GPIOD, GPIO_PINS_4},
    {GPIOE, GPIO_PINS_4},
    {GPIOC, GPIO_PINS_3}
};

// -------------------------------------------------------------------------- //
// Текущие параметры

uint32_t di_cnt[DI_NUMBER];             // счетчики входов

uint16_t di_state[DI_NUMBER];           // состояние входов
uint16_t di_state_bit;

uint8_t  di_load_tmp[DO_NUMBER][5];     // буфер для временных значений
uint16_t di_load_state[DO_NUMBER];      // состояние датчиков обрыва нагрузки
uint16_t di_load_state_bit;             // состояние датчиков обрыва нагрузки (битовое поле)

uint16_t di_output_state[DO_NUMBER];    // состояние датчиков неисправности выходов
uint16_t di_output_state_bit;           // состояние датчиков неисправности выходов (битовое поле)

uint16_t di_credibility_state[DO_NUMBER];  // достоверность состояния датчиков нагрузки
uint16_t di_credibility_state_bit;         // достоверность состояния датчиков нагрузки


// Настройка периферии дискретных входов (GPIO, EXTI, TIM).
// Настройка датчиков обрыва нагрузки (GPIO).
// Вызывается единственный раз при старте контроллера.
// Периферия настраивается однозначно и только один раз.
void di_init(void)
{
    // Входы
    for (int i = 0; i < DI_NUMBER; i++) {
        di_base_init(&d_inputs[i]);
        di_load_sens_init(&di_load[i]);
    }
    
    // EXTI
    di_exti_init();
    
    // Таймер для антидребезга
    di_tim_init();
}


// Настройка GPIO дискретных входов
void di_base_init(din_t *input)
{
    gpio_init_type gpio_init_struct;

    gpio_default_para_init(&gpio_init_struct);
        
    gpio_init_struct.gpio_pull           = GPIO_PULL_NONE;  
    gpio_init_struct.gpio_mode           = GPIO_MODE_INPUT;  
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_pins           = input->pin;
    gpio_init(input->port, &gpio_init_struct);
}


// Настройка EXTI дискретных входов
void di_exti_init(void)
{
    exint_init_type exint_init_struct;
    
    gpio_exint_line_config(GPIO_PORT_SOURCE_GPIOB, GPIO_PINS_SOURCE11); // 11
    gpio_exint_line_config(GPIO_PORT_SOURCE_GPIOB, GPIO_PINS_SOURCE10); // 10
    gpio_exint_line_config(GPIO_PORT_SOURCE_GPIOE, GPIO_PINS_SOURCE14); // 14
    gpio_exint_line_config(GPIO_PORT_SOURCE_GPIOE, GPIO_PINS_SOURCE15); // 15
    gpio_exint_line_config(GPIO_PORT_SOURCE_GPIOA, GPIO_PINS_SOURCE3);  // 3
    gpio_exint_line_config(GPIO_PORT_SOURCE_GPIOA, GPIO_PINS_SOURCE2);  // 2
    gpio_exint_line_config(GPIO_PORT_SOURCE_GPIOA, GPIO_PINS_SOURCE1);  // 1
    gpio_exint_line_config(GPIO_PORT_SOURCE_GPIOA, GPIO_PINS_SOURCE0);  // 0
    
    exint_default_para_init(&exint_init_struct);
    
    exint_init_struct.line_enable = TRUE;
    exint_init_struct.line_mode = EXINT_LINE_INTERRUPUT;
    exint_init_struct.line_select = EXINT_LINE_0 | EXINT_LINE_1 | EXINT_LINE_2 |
                                    EXINT_LINE_3 | EXINT_LINE_10 | 
                                    EXINT_LINE_11| EXINT_LINE_14 | EXINT_LINE_15; 
    
    exint_init_struct.line_polarity = EXINT_TRIGGER_BOTH_EDGE;
    exint_init(&exint_init_struct);
    
    exint_flag_clear(EXINT_LINE_0);
    exint_flag_clear(EXINT_LINE_1);
    exint_flag_clear(EXINT_LINE_2);
    exint_flag_clear(EXINT_LINE_3);
    exint_flag_clear(EXINT_LINE_10);
    exint_flag_clear(EXINT_LINE_11);
    exint_flag_clear(EXINT_LINE_14);
    exint_flag_clear(EXINT_LINE_15);
    
    nvic_irq_enable(EXINT0_IRQn, 5, 0);
    nvic_irq_enable(EXINT1_IRQn, 5, 0);
    nvic_irq_enable(EXINT2_IRQn, 5, 0);
    nvic_irq_enable(EXINT3_IRQn, 5, 0);
    nvic_irq_enable(EXINT15_10_IRQn, 5, 0);
}


// Настройка датчиков обрыва нагрузки (GPIO).
void di_load_sens_init(simple_gpio_t *sens)
{
    gpio_init_type gpio_init_struct;
    
    gpio_default_para_init(&gpio_init_struct);
        
    gpio_init_struct.gpio_pull           = GPIO_PULL_UP;  
    gpio_init_struct.gpio_mode           = GPIO_MODE_INPUT;  
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_pins           = sens->pin;
    gpio_init(sens->port, &gpio_init_struct); 
}


// Таймер антидребезга для дискретных входов
void di_tim_init(void)
{
    crm_clocks_freq_type crm_clocks_freq_struct = {0};
    
    crm_clocks_freq_get(&crm_clocks_freq_struct);
    
    nvic_irq_disable(TMR8_BRK_TMR12_IRQn);
    
    crm_periph_clock_enable(CRM_TMR12_PERIPH_CLOCK, TRUE);

    // 1 Hz
    tmr_base_init(TMR12, 9, (crm_clocks_freq_struct.ahb_freq / 10000) - 1);
    tmr_cnt_dir_set(TMR12, TMR_COUNT_UP);
    
    NVIC_ClearPendingIRQ(TMR8_BRK_TMR12_IRQn);
    nvic_irq_enable(TMR8_BRK_TMR12_IRQn, 5, 0);
    
    tmr_flag_clear(TMR12, TMR_OVF_FLAG);
    tmr_interrupt_enable(TMR12, TMR_OVF_INT, TRUE);
    tmr_counter_value_set(TMR12, 0);
    tmr_counter_enable(TMR12, TRUE);  
}


// IRQ таймера антидребезга дискретных входов
void TMR8_BRK_TMR12_IRQHandler(void)
{
    if(tmr_flag_get(TMR12, TMR_OVF_FLAG) != RESET)
    {
        tmr_flag_clear(TMR12, TMR_OVF_FLAG);

        debounce();
    }
}


// Установка состояний дискретных входов (вкл./выкл.)
void di_set_state(void)
{
    for (int i = 0; i < DI_NUMBER; i++)
    {
        settings.di[i].state = (uint8_t)((settings.di_state_bits >> i) & 1);
    }
}


// Пробегает по всему массиву структур цифровых входов.
// Если режим входа изменился 
void di_set(void)
{
    for (int i = 0; i < DI_NUMBER; i++)
    {
        if (((settings.di_mode_bits >> i) & 1) != d_inputs[i].mode) 
        {
            d_inputs[i].mode = ((settings.di_mode_bits >> i) & 1);
            if (d_inputs[i].mode == 0) 
            {
                d_inputs[i].cnt = 0;
                d_inputs[i].p_flag = false;
                di_cnt[i] = 0;
            }
            else
            {
                di_state_bit &= ~(1 << i);
            }
        }
    }
}

//
uint8_t di_get(uint8_t channel)
{
    uint8_t ret = 0;
    
    ret = (uint8_t)(di_state_bit >> channel & 1);
    
    return  ret;
}


// Функция антидребезка. Вызывается в прерывании таймера.
void debounce(void)
{
    for (int i = 0; i < DI_NUMBER; i++)
    {
        if ((d_inputs[i].p_flag) && (d_inputs[i].mode == 0)){
            if (++d_inputs[i].cnt == settings.di_debounce[i]) {
                d_inputs[i].cnt = 0;
                d_inputs[i].p_flag = false;
                
                di_state[i] = (uint16_t)gpio_input_data_bit_read(d_inputs[i].port, d_inputs[i].pin);
                
                if (!di_state[i]) {
                    di_state_bit &= ~(1 << i); // снять бит
                    leds[i].state = LED_OFF;
                }
                else {
                    di_state_bit |= di_state[i] << i;
                    leds[i].state = LED_ON;
                }
            }
        }
    }
}


// Периодический опрос дискретнрых входов и датчиков обрыва нагрузки.
// TODO отрефакторить функцию
void di_task(void *params)
{
    for (;;)
    {
        // Дискретные входы
        for (int i = 0; i < DI_NUMBER; i++)
        {
            // Режим обычного входа
            if (d_inputs[i].mode == 0)
            {
                di_state[i] = (uint16_t)gpio_input_data_bit_read(d_inputs[i].port, d_inputs[i].pin);
                
                if (!di_state[i]) {
                    di_state_bit &= ~(1 << i); // снять бит
                    leds[i].state = LED_OFF;
                }
                else {
                    di_state_bit |= di_state[i] << i;
                    leds[i].state = LED_ON;
                }
            }
        }        
        vTaskDelay(100);
    }
}


// di_load_tmp[]
void di_load_task(void *params)
{
    uint8_t out_state = 0; // текущее состояние выхода (для определения аварий)
    uint8_t out_value = 0; // текущее значение на выходе (для определения аварий)
    uint8_t led_index = 0;
    flag_status in_err_state = RESET; // текущее состояние на датчике аварий (значение на пине)

    uint8_t tmp_index = 0;
    bool buffer_full = false;
      
    for (;;)
    {
        if (tmp_index == 5) {
            tmp_index = 0;
            buffer_full = true;
        }
      
        // Датчики обрыва нагрузки и нисправность выходов
        for (int i = 0; i < DI_NUMBER; i++)
        {
            // Определяем текущие состояние выхода
            if (save_mode_get())
            {
                out_state = settings.dout_save[i].state;
                out_value = settings.dout_save[i].value;
            }
            else
            {
                out_state = settings.dout[i].state;
                out_value = settings.dout[i].value;
            }
             
            if (i < 4)
                led_index = i + 12;
            else
                led_index = i - 4 + 20;
            
            // Текущее состояние пина аварий
            //in_err_state = gpio_input_data_bit_read(di_load[i].port, di_load[i].pin);
            
            di_load_tmp[i][tmp_index] = gpio_input_data_bit_read(di_load[i].port, di_load[i].pin);
            
            // Пока буфер не заполнен нельзя принять оценить состояние входов
            if (!buffer_full)
                continue;
            
            in_err_state = di_load_tmp[i][0]*di_load_tmp[i][1]*di_load_tmp[i][2]*di_load_tmp[i][3]*di_load_tmp[i][4];
#if 1            
            // Если выход разомкнут (соответствующий бит в 0), 
            // то обновляем слово состояний датчиков обрыва нагрузки
            if (out_value == 0)
            {
                if (out_state == 0) {
                    di_load_state_bit &= ~(1 << i);
                    leds[led_index].state = LED_OFF;    
                }
                else if (in_err_state == RESET) {
                    di_load_state_bit &= ~(1 << i);
                    leds[led_index].state = LED_OFF;
                }
                else {
                    leds[led_index].state = LED_ON;
                    di_load_state_bit |= 1 << i;
                }    
            }
              
            // Если выход замкнут (соответствующий бит в 1),
            // то обновляем слово состояний неисправности выходов
            else
            {
                if (out_state == 0) {
                    di_output_state_bit &= ~(1 << i);
                    leds[led_index].state = LED_OFF;
                }
                if (in_err_state == RESET) {
                    di_output_state_bit |= 1 << i;
                    leds[led_index].state = LED_ON;
                }
                else {
                    di_output_state_bit &= ~(1 << i);
                    leds[led_index].state = LED_OFF;
                }
            }
#endif            
        }
        tmp_index++;
        
        
        vTaskDelay(2);
    }
}

// -------------------------------------------------------------------------- // 
//        Обработчики прерываний EXTI для режима счетных входов

// IN_8 GPIOA_0
void EXINT0_IRQHandler(void)
{
    if (exint_flag_get(EXINT_LINE_0) != RESET) 
    {
        exint_flag_clear(EXINT_LINE_0);
        if (d_inputs[7].mode == 0)
            d_inputs[7].p_flag = true;
        else { 
            if (d_inputs[7].cnt_flag) {
                di_cnt[7]++;
                d_inputs[7].cnt_flag = false;
            }
            else
                d_inputs[7].cnt_flag = true;
        }
    }
}

// IN_7 GPIOA_1
void EXINT1_IRQHandler(void)
{
    if(exint_flag_get(EXINT_LINE_1) != RESET) 
    {
        exint_flag_clear(EXINT_LINE_1);
        if (d_inputs[6].mode == 0)
            d_inputs[6].p_flag = true;
        else { 
            if (d_inputs[6].cnt_flag) {
                di_cnt[6]++;
                d_inputs[6].cnt_flag = false;
            }
            else
                d_inputs[6].cnt_flag = true;
        }
    }
}

// IN_6 GPIOA_2
void EXINT2_IRQHandler(void)
{
    if(exint_flag_get(EXINT_LINE_2) != RESET) 
    {
        exint_flag_clear(EXINT_LINE_2);
        if (d_inputs[5].mode == 0)
            d_inputs[5].p_flag = true;
        else { 
            if (d_inputs[5].cnt_flag) {
                di_cnt[5]++;
                d_inputs[5].cnt_flag = false;
            }
            else
                d_inputs[5].cnt_flag = true;
        }
    }
}

// IN_5 GPIOA_3
void EXINT3_IRQHandler(void)
{
    if(exint_flag_get(EXINT_LINE_3) != RESET) 
    {
        exint_flag_clear(EXINT_LINE_3);
        if (d_inputs[4].mode == 0)
            d_inputs[4].p_flag = true;
        else { 
            if (d_inputs[4].cnt_flag) {
                di_cnt[4]++;
                d_inputs[4].cnt_flag = false;
            }
            else
                d_inputs[4].cnt_flag = true;
        }
    }
}

// IN_1, IN_2, IN_3, IN_4
void EXINT15_10_IRQHandler(void)
{
    // IN_2 GPIOB_10
    if (exint_flag_get(EXINT_LINE_10) != RESET)  
    {
        exint_flag_clear(EXINT_LINE_10);
        if (d_inputs[1].mode == 0)
            d_inputs[1].p_flag = true;
        else { 
            if (d_inputs[1].cnt_flag) {
                di_cnt[1]++;
                d_inputs[1].cnt_flag = false;
            }
            else
                d_inputs[1].cnt_flag = true;
        }
    }
    // IN_1 GPIOB_11
    else if (exint_flag_get(EXINT_LINE_11) != RESET)  
    {
        exint_flag_clear(EXINT_LINE_11);
        if (d_inputs[0].mode == 0)
            d_inputs[0].p_flag = true;
        else { 
            if (d_inputs[0].cnt_flag) {
                di_cnt[0]++;
                d_inputs[0].cnt_flag = false;
            }
            else
                d_inputs[0].cnt_flag = true;
        }
    }
    // IN_3 GPIOE_14
    else if (exint_flag_get(EXINT_LINE_14) != RESET)  
    {
        exint_flag_clear(EXINT_LINE_14);
        if (d_inputs[2].mode == 0)
            d_inputs[2].p_flag = true;
        else { 
            if (d_inputs[2].cnt_flag) {
                di_cnt[2]++;
                d_inputs[2].cnt_flag = false;
            }
            else
                d_inputs[2].cnt_flag = true;
        }
    }
    // IN_4 GPIOE_15
    else if (exint_flag_get(EXINT_LINE_15) != RESET) 
    {
        exint_flag_clear(EXINT_LINE_15);
        if (d_inputs[3].mode == 0)
            d_inputs[3].p_flag = true;
        else { 
            if (d_inputs[3].cnt_flag) {
                di_cnt[3]++;
                d_inputs[3].cnt_flag = false;
            }
            else
                d_inputs[3].cnt_flag = true;
        }
    }    
}

#endif
