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

#include "bsp.h"
#include "task_man.h"
#include "self_diag.h"
#include "lboot_port.h"

#include <string.h>

// #include "fpga_main_tst.h"
// #include "flash_mem_tst.h"

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

#define GCH_LED_RED     (LMCAL_GPIO_CHANNEL0)
#define GCH_LED_GREEN   (LMCAL_GPIO_CHANNEL1)

#define GCH_LED_2       (LMCAL_GPIO_CHANNEL4)


#define APP_START_DELAY     0
#define APP_START_DELAY_CNT             (200000U)

#define START_SDIO_TEST     0

//******************************************* Проверка параметров *******************************************


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


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

// Переменные только для чтения!
// Версия проекта
extern u32 fpga_prj_ver;



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

// Запрос на выход из приложения
static volatile bool b_req_to_boot = false;

// Запрос на перезагрузку
static volatile bool b_req_to_reset = false;

// Таймаут перед сбросом
static volatile u16 n_reset_tout = 0;


static u8 task_1ms = 0;
static u8 task_100ms = 0;
static u8 task_1000ms = 0;


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



static void f_task_1ms(void)
{    
    // Перезагрузка МК по запросу
    if ((b_req_to_reset == true) && (n_reset_tout == 0))
    {
        lmcal_cpu_reset();
    }
    else if (n_reset_tout > 0)
    {
        n_reset_tout--;
    }
}


static void f_task_100ms(void)
{
    // todo - перенести в другое место управление светодиодом соединения
    // Добавить логику "подмигивания" по приему синхро-импульса
    
    if (tcp_cmd_socket_is_est() == true)
    {
        // CH1
        lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL1, true);
    }
    else
    {
        lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL1, false);
    }

}


static void f_task_1000ms(void)
{
    //! trig
    // lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL8, true);
    // lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL8, false);
}



// Обновление структуры загрузчика
static void f_update_lboot_struct(void)
{
    // Указатель на заводскую информацию
    extern fm_factory_mem_data_t* fm_factory_mem_data;

    if (fm_factory_mem_data != NULL)
    {
        lboot_set_dev_name(fm_factory_mem_data->name,   LBOOT_SERIAL_SIZE);
        lboot_set_serial(fm_factory_mem_data->serial,   LBOOT_DEVNAME_SIZE);
        lboot_set_brd_rev(fm_factory_mem_data->brd_rev, LBOOT_REVISION_SIZE);
    }
}


#if START_SDIO_TEST
//!--------------------


static void sdio_wr_done_cb(void)
{
    //! test - irq (установить в прерывании)
    lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL9, true);
}



static void sdio_rd_done_cb(void)
{
    //! test - irq (установить в прерывании)
    lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL9, true);
}


#define SDIO_MAX_SIZE   300

static u32 sdio_tx_data[SDIO_MAX_SIZE];
static u32 sdio_rx_data[SDIO_MAX_SIZE];

static void sdio_test_init(void)
{
    for (int i = 0; i < SDIO_MAX_SIZE; i++)
    {
        sdio_tx_data[i] = 0;
        sdio_rx_data[i] = 0;
    }

    sdio_tx_data[0] = 0x11223344;
    sdio_tx_data[1] = 0x55667788;
    sdio_tx_data[2] = 0x99aabbcc;
    sdio_tx_data[3] = 0xddeeff00;
    sdio_tx_data[4] = 0x00000000;

    sdio_tx_data[5] = 0x11223344;
    sdio_tx_data[6] = 0x55667788;
    sdio_tx_data[7] = 0x99aabbcc;
    sdio_tx_data[8] = 0xddeeff00;
    sdio_tx_data[9] = 0x00000000;

    sdio_tx_data[10] = 0x11223344;
    sdio_tx_data[11] = 0x55667788;
    sdio_tx_data[12] = 0x99aabbcc;
    sdio_tx_data[13] = 0xddeeff00;
    sdio_tx_data[14] = 0x00000000;

    sdio_tx_data[15] = 0xAABBCCDD;

    lmcal_sdio_set_callback(LMCAL_SDIO_CHANNEL0, CB_MEM_WR_DONE_CALLBACK, sdio_wr_done_cb);
    lmcal_sdio_set_callback(LMCAL_SDIO_CHANNEL1, CB_MEM_RD_DONE_CALLBACK, sdio_rd_done_cb);
}



// SDIO - Тест чтения
static void sdio_read_test(void)
{
    // Используется лог. анализатор в ПЛИС, ножки нужно дергать для
    // срабатывания триггера
    fun_res_t err = ERR_OK;

    lmcal_ptim_delay(100);

    //! test - irq (установить в прерывании)
    lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL9, false);
    //! trig
    lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL8, true);

    sdio_rx_data[0] = 0x11;
    sdio_rx_data[1] = 0x11;
    sdio_rx_data[2] = 0x11;
    sdio_rx_data[3] = 0x11;
    sdio_rx_data[4] = 0x11;
    sdio_rx_data[5] = 0x11;
    sdio_rx_data[6] = 0x11;
    sdio_rx_data[7] = 0x11;

    // размер в словах!!! 64*4 = 256
    err = lmcal_sdio_read_buf(LMCAL_SDIO_CHANNEL1, (u32)0x11112222, sdio_rx_data, 64, NULL); 
    
    //! trig
    lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL8, false);
}



// SDIO - Тест записи
static void sdio_write_test(void)
{
    // Используется лог. анализатор в ПЛИС, ножки нужно дергать для
    // срабатывания триггера
    fun_res_t err = ERR_OK;

    lmcal_ptim_delay(100);

    //! test - irq
    lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL9, false);
    //! trig
    lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL8, true);
    
    // размер в словах!!! 64*4 = 256
    err = lmcal_sdio_write_buf(LMCAL_SDIO_CHANNEL0, (u32)0x99887766, sdio_tx_data, 64, NULL);

    //! trig
    lmcal_gpio_write_pin(LMCAL_GPIO_CHANNEL8, false);
}

#endif


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

fun_res_t bsp_init(void)
{
    fun_res_t err = ERR_OK;    
    
    // Пауза перед запуском (нужна только при отладке)
    #if (APP_START_DELAY == 1)
    for (u32 nStartDelay = 0U; nStartDelay < APP_START_DELAY_CNT; nStartDelay++){}
    #endif
    
    // Инициализация периферии МК
    char s_err_fun[40];
    char s_err_name[40];

    err = lmcal_init();
    if (err != ERR_OK)
    {
        // Получаем номер драйвера с ошибкой инициализации
        strcpy(s_err_fun, lmcal_err_get_last_fun_name());
        strcpy(s_err_name, lmcal_err_get_last_err_name());

        err = ERR_RUNTIME_ERROR;
    }
    
    lprintf_uart_init(0, 0, 0);
    lclock_init();
    tm_init();
    sdiag_init();

    PRINTF("Start ------------------------------------\n");
    if (err != ERR_OK) 
    {
        DBG_LOGE("fun= %s, err= %s\n", s_err_fun, s_err_name);
        L_GOTO_EXIT;
    }

    DBG_LOGI("Periph has initialized!\n");

    // Инициализация модулей приложения
    // Инициализация переменных TCP
    tcp_cmd_pre_init();

    // Обращение к ПЛИС
    err = fpga_init();
    if (err != ERR_OK)
    {
        DBG_LOGE("FPGA is not loaded!\n");
    }
        
    // Чтение встроенной flash памяти
    fm_read();
    f_update_lboot_struct();

    // Инициализация сетевого стека
    tcp_init();
    
    DBG_LOGI("Versions SW= %s, HW= %d\n", PROJECT_VERSION, fpga_prj_ver);
    DBG_LOGI("Initialization done!\n");

    // Добавление периодических задач
    tm_add_periodic_task(&task_1ms, f_task_1ms);
    tm_add_periodic_task(&task_1000ms, f_task_1000ms);
    tm_add_periodic_task(&task_100ms, f_task_100ms);
    tm_start_periodic_task(task_1ms, 1);
    tm_start_periodic_task(task_1000ms, 1000);
    tm_start_periodic_task(task_100ms, 100);

    #if START_SDIO_TEST
    //!----------
    sdio_test_init();
    #endif

    L_EXIT;
    return err;
}



void bsp_deinit(void)
{
    
}



bool bsp_loop(void)
{
    // Опрос драйверов
    lmcal_loop();

    // Менеджер периодических и асинхронный задач
    tm_loop();

    // Опрос сетевого стека
    tcp_loop();

    #if START_SDIO_TEST
    //!------
    sdio_read_test();
    sdio_write_test();
    #endif
    
    return b_req_to_boot;
}



void bsp_jump_to_boot(void)
{
    // todo
}



void bsp_set_boot_req(void)
{
    b_req_to_boot = true;
}



void bsp_set_reset_req(u16 tout)
{
    b_req_to_reset = true;
    n_reset_tout = tout;
}



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