#include "main.h"
#include "common_config.h"


void soft_wdt(void *params);
void init_task(void *params);
void led_task(void *params);
bool jump_to_app(uint32_t address);
void (*pftarget)(void);

bool boot_failed = false;



int main()
{
    uint8_t load_mode;
	uint8_t boot_try;
	uint8_t fw_invalid = 0;
    
    nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
    
    crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_GPIOE_PERIPH_CLOCK, TRUE);
    
    crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_BPR_PERIPH_CLOCK, TRUE);
    pwc_battery_powered_domain_access(TRUE);
    
    system_clock_config();
    
    delay_init();

    uart_print_init(115200);
    
    // Флаг - нужно ли обновляться
    load_mode = bpr_data_read(BACKUP_IAP_LOAD_MODE);
    
    // Число попыток загрузки основного ПО
    boot_try = bpr_data_read(BACKUP_IAP_BOOT_TRY);
    
    DBG printf("[IAP] load_mode: %u, boot_try: %u\r\n", load_mode, boot_try);

    // Если есть попытки, то пытаемся загрузить FW.
	// Используем ограниченное число попыток загрузить основное FW.
    if (boot_try > 1)
    {
        boot_try--;
        bpr_data_write(BACKUP_IAP_BOOT_TRY, boot_try);
        
        if (!jump_to_app(USER_FLASH_FIRST_PAGE_ADDRESS))
        {
            // Флеш пустая. Нечего загружать
            fw_invalid = 1;
            boot_failed = false;
            DBG printf("FW empty! Bootloader starting...\n\r");
        }
    }
    else if (boot_try == 1)
    {
        boot_failed = true;
        DBG printf("FW boot failed. Bootloader starting...\r\n");
        
        load_mode = 1;
        boot_try = 0;
        bpr_data_write(BACKUP_IAP_LOAD_MODE, load_mode);
        bpr_data_write(BACKUP_IAP_BOOT_TRY, boot_try);
    }
    
    if  ((load_mode == 0) && (!fw_invalid))
    {
        DBG printf("Run main FW\r\n");
        bpr_data_write(BACKUP_IAP_BOOT_TRY, BOOT_TRY);
        DBG printf("Go to main FW...\r\n");
        jump_to_app(USER_FLASH_FIRST_PAGE_ADDRESS);
    }
        
    DBG printf("IAP starting...\r\n");

    // -------------------------------------------------------------------------- //    
    // Мультиплексор
    
    mux_gpio_init();

    
#if 0    
    // -------------------------------------------------------------------------
    // Для теста. Сброс.
    DBG printf("For test reset load_mode flag and jump to FW\r\n");
    bpr_data_write(BACKUP_IAP_LOAD_MODE, 0);
    jump_to_app(0x08021000); 
#endif
    
    gpio_wdt_init();
    gpio_mbaddr_init();

    taskENTER_CRITICAL();      
    
    xTaskCreate(soft_wdt, "soft_wdt", configMINIMAL_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);    
    
    xTaskCreate(init_task, "init_task", 10*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
    
    xTaskCreate(led_task, "led_task", 2*configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
    
    taskEXIT_CRITICAL();
    
    vTaskStartScheduler();

    while (1) {}
}


// 
bool jump_to_app(uint32_t address)
{
    uint32_t stkptr, jumpaddr;
    stkptr = *(uint32_t *)address;
    jumpaddr = *(uint32_t *)(address + sizeof(uint32_t));
 
    //if (((*(__IO uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS) & 0x20000000) == 0x20000000) 
    if  (*(__IO uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS != 0xFFFFFFFF) 
    {
        __set_MSP(stkptr);
        pftarget = (void (*) (void))jumpaddr;
        pftarget();
    }
    return false;
}


//
void soft_wdt(void *params)
{
    (void)params;
    
    for (;;)
    {
        extern_wdt_togle(); // extern WDT
        vTaskDelay(500);
    }
}

//
void init_task(void *params)
{
    (void)params;
    
    crm_periph_clock_enable(CRM_CRC_PERIPH_CLOCK, TRUE);
    
    init_settings();
    settings_load(&settings);    
    
    mb_init();
    
    for (;;)
    {
      
        vTaskDelay(500);
    }
}

//
void led_task(void *params)
{
    for (;;) {
        mux_led_proc();
    }
}

//
void USART1_IRQHandler(void)
{
    char data = 0;
      
    if (USART1->ctrl1_bit.rdbfien == SET)
    {
        if (usart_flag_get(USART1, USART_RDBF_FLAG) == SET) 
        {
            data = usart_data_receive(USART1);
        }
    }  
}