#include "at32f403a_407.h" 
#include "settings_api.h"
#include "settings_dio.h"
#include "settings_ao.h"
#include "settings_ai.h"
#include "sys_api.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "common_config.h"
#include "common.h"
#include "at32_uid.h"
#include "hash.h"
#include "utility.h"
#include "log_api.h"
#include <math.h> 
#include <string.h>
#include <stdlib.h>
#include <stdio.h>


SemaphoreHandle_t flash_mutex;


// Флаг подтверждения новых сетевых параметров пользователем
bool fConfirmWebParams = false;
   
// Системные настройки
sys_settings_t sys_settings;

// Копия системных настроек
sys_settings_t temp_sys_settings;

// Общая структура настроек
settings_t settings;

// Настройки порта UART - RS485 (Modbus) в человеческом формате
modbus_t mb_port_settings;

//
void init_settings(void)
{
    flash_mutex = xSemaphoreCreateMutex();
}

// Загрузка структуры настроек из flash
void settings_load(settings_t *settings)
{
    uint32_t loadCRC;  // CRC из flash
    uint32_t newCRC;   // CRC загруженной структуры настроек
    bool need_default = false;
    
    settings_read_from_flash((uint8_t*)settings, sizeof(settings_t));
    
    // Считываем CRC из флеш памяти
    loadCRC = (*(uint32_t*)CRC_ADDRESS);
    
    // Рассчитываем CRC для структуры настроек
    newCRC = settings_get_crc(settings);

    // Если CRC не совпадают нужно прошивать дефолтные настройки
    if (loadCRC != newCRC) {
        need_default = true;
    }
    // CRC совпала, проверяем контрольное слово если слово не совпадает
    // то это значит, что поплыла структура нстроек, прошиваем дефолт
    else if (settings->control_word != SETTINGS_CONTROL_WORD)  
    {
        need_default = true;
    }
    // CRC и контрольное слово совпали, проверяем номер версии настроек.
    // Если версия в настройках и прошивке не совпадают
    // (при обновлении изменили структуру настроек), прошиваем дефолт 
    else if (settings->settings_version != SETTINGS_VERSION)
    {
        need_default = true;
    }
    
    // Прошиваем дефолтные настройки если нужно
    if (need_default) 
    {
        settings_set_all_default();
        settings_save(settings);
    }
#if 1    
    //SETTINGS_Print();
#endif    
}

//
void settings_read_from_flash(uint8_t *data, uint32_t size)
{
    uint32_t baseAddress = SETTINGS_SECTOR;
        
    for (uint32_t i = 0; i < size; i++)
        *data++ = (*(uint32_t*)baseAddress++);;
}

//
uint32_t settings_get_crc(settings_t *settings)
{
    crc_data_reset();
    return crc_block_calculate((uint32_t*)settings, sizeof(settings_t)/4 - 1);
}

//
uint32_t settings_get_crit_sec_crc(settings_t *settings)
{
    crc_data_reset();
    uint32_t critsec_len = (uint32_t)((uint8_t *)(&settings->critical_section_crc) - (uint8_t *)settings) / 4;
    return crc_block_calculate((uint32_t *)settings, critsec_len);
}

// Сброс всех настроек в значения по умолчанию
void settings_set_all_default(void)
{
    settings_set_modbus_def(&settings.com_settings.mb_port);

    // Безопасный режим
    settings.save_mode = true;
    
    // Время ожидания опроса (сек.)
    settings.save_delay = 60;

    // Пользовательская информация. Текстовая строка.
    memset(settings.info, 0, sizeof(settings.info));
    
    settings.settings_version = SETTINGS_VERSION;
    settings.critical_section_crc = settings_get_crit_sec_crc(&settings);
    settings.control_word = SETTINGS_CONTROL_WORD;
        
#if defined (MDIO_88)    
    settings_dio_set_all_default();
#endif
    
#if defined (MAI_12)
    settings_ai_set_all_default();
#endif
    
#if defined (MAO_4)    
    settings_ao_set_all_default();
#endif
}

// -------------------------------------------------------------------------- //
// Настройки по умолчанию

//
void settings_set_modbus_def(uint16_t *mb_port)
{
    modbus_t mb_settings;
    
    mb_settings.baud = BRD_115200;
	mb_settings.parity = NO_PAR;
	mb_settings.databits = DATABITS_8;
	mb_settings.stopbits = STOP_1;
    
    settings_conv_modbus_def(&mb_settings, mb_port);
}

//
void settings_conv_modbus_def(modbus_t *mb_settings, uint16_t *mb_port)
{
	uint16_t param = 0;

	// Количестро стоп бит (0-1 биты)
	if (mb_settings->stopbits == STOP_1)
		param = 0x00;
	else if (mb_settings->stopbits == STOP_2)
		param = 0x02; 		

	// Длина слова (2ой бит)
	param |= 0x00 << 2;

	// Контроль четности (3-4 биты)
	if (mb_settings->parity == NO_PAR)
		param |= 0x00 << 3;
	else if (mb_settings->parity == EVEN_PAR)
		param |= 0x02 << 3;
	else if (mb_settings->parity == ODD_PAR)
		param |= 0x03 << 3;

	// Скорость (5 - 7 биты)
	switch (mb_settings->baud)
	{
		case BRD_2400 :
			param |= 0x00 << 5;
		break;

		case BRD_4800 :
			param |= 0x01 << 5;
		break;

		case BRD_9600 :
			param |= 0x02 << 5;
		break;

		case BRD_19200 :
			param |= 0x03 << 5;
		break;

		case BRD_38400 :
			param |= 0x04 << 5;
		break;

		case BRD_57600 :
			param |= 0x05 << 5;
		break;

		case BRD_115200 :
			param |= 0x06 << 5;
		break;

		default : break;
	}

	*mb_port = param;
}

// Установка параметров Modbus
void settings_set_modbus_params(uint16_t mb_port)
{
	uint16_t param = 0;

	// Количестро стоп бит (0-1 биты)
	param = 0x0003 & mb_port; 

	if (param == 0x00)
		mb_port_settings.stopbits = STOP_1;
	else if (param == 0x02)
		mb_port_settings.stopbits = STOP_2;

	// Длина слова (2ой бит)
	param = (0x0004 & mb_port) >> 2;

	mb_port_settings.databits = DATABITS_8;
	
	// Контроль четности (3-4 биты)
	param = (0x0018 & mb_port) >> 3; 

	if (param == 0x00)
		mb_port_settings.parity = NO_PAR;
	else if (param == 0x02)
		mb_port_settings.parity = EVEN_PAR;
	else if (param == 0x03)
		mb_port_settings.parity = ODD_PAR;

	// Скорость (5 - 7 биты)
	param = (0x00E0 & mb_port) >> 5; 

	switch (param)
	{
		case 0x00 :
			mb_port_settings.baud = BRD_2400;
		break;

		case 0x01 :
			mb_port_settings.baud = BRD_4800;
		break;

		case 0x02 :
			mb_port_settings.baud = BRD_9600;
		break;

		case 0x03 :
			mb_port_settings.baud = BRD_19200;
		break;

		case 0x04 :
			mb_port_settings.baud = BRD_38400;
		break;

		case 0x05 :
			mb_port_settings.baud = BRD_57600;
		break;

		case 0x06 :
			mb_port_settings.baud = BRD_115200;
		break;

		default : break;
	}
}

//
uint32_t settings_get_mb_baud(modbus_t *mb_settings)
{
    switch (mb_settings->baud)
    {
        case BRD_1200 :
          return 1200;
        break;
        
        case BRD_2400 :
          return 2400;
        break;
        
        case BRD_4800 :
            return 4800;
        break;
        
        case BRD_9600 :
            return 9600;
        break;
        
        case BRD_19200 :
            return 19200;
        break;
        
        case BRD_38400 :
            return 38400;
        break;
        
        case BRD_57600 :
            return 57600;
        break;
        
        case BRD_115200 :
            return 115200;
        break;
        
        case BRD_230400 :
            return 230400;
        break;
        
        case BRD_460800 :
            return 460800;
        break;
        
        default :
            return 115200; 
        break;
    }
}

//
eMBParity settings_get_mb_par(modbus_t *mb_settings)
{
    switch (mb_settings->parity)
    {
        case NO_PAR :
            return MB_PAR_NONE;
        break;
        
        case ODD_PAR :
            return MB_PAR_ODD;
        break;
        
        case EVEN_PAR :
            return MB_PAR_EVEN;
        break;
        
        default :
            return MB_PAR_NONE;
        break;
    }
}

//
void settings_init_mb_port(uint8_t mb_addr)
{
    uint32_t baud = settings_get_mb_baud(&mb_port_settings);
    eMBParity par = settings_get_mb_par(&mb_port_settings);
    
    eMBInit(MB_RTU, mb_addr, 4, baud, par, mb_port_settings.stopbits);
}


// -------------------------------------------------------------------------- //

// Запись структуры настроек во flash
bool settings_save(settings_t *settings)
{
    bool ret = false;
    
    xSemaphoreTake(flash_mutex, portMAX_DELAY);
    
    settings->critical_section_crc = settings_get_crit_sec_crc(settings);
    
    ret = settings_write_to_flash((uint8_t*)settings, sizeof(settings_t));
    
    xSemaphoreGive(flash_mutex);
    
    return ret;
}

//
bool settings_save_with_log(void)
{
    bool ret;
  
    // Изменились ли настройки
    bool change = settings_is_changed(&settings);
                    
    // Сохранение настроек
    ret = settings_save(&settings);
                  
    if (change) {
        log_add_entry(LOG_CHANGE_CONFIG, (log_event_state_t)ret, 0, 0);
    }
    
    return ret;
}

//
bool settings_write_to_flash(uint8_t *data, uint32_t size)
{
    uint32_t baseAddress = SETTINGS_SECTOR;
    uint32_t checkCrc = 0;
    uint32_t crc = settings_get_crc((settings_t*)data);
    flash_status_type status;
    uint8_t *ptr = data;
    

        flash_unlock();
    
        settings_erase_flash_sector();

        for (uint32_t i = 0; i < size; i++)
            if ((status = flash_byte_program(baseAddress++, *data++)) != FLASH_OPERATE_DONE) {
                DBG printf("FLASH_ProgramByte error: status = %d\r\n", status);
                return false;
            }
    
        if ((status = flash_word_program((uint32_t)CRC_ADDRESS, crc)) != FLASH_OPERATE_DONE) {
            DBG printf("FLASH_ProgramWord error: status = %d\r\n", status);
        }
    
        flash_lock();
    
        /* Считываем что записали */
        settings_read_from_flash(ptr, sizeof(settings_t));
    
        checkCrc = settings_get_crc((settings_t*)ptr);
    
        /* Проверяем  CRC того что было записано */
        if ((checkCrc != crc) || (status != FLASH_OPERATE_DONE)) {
            return false;
        }
        
        return true;
}

// Очистка сектора настроек
void settings_erase_flash_sector(void)
{
    flash_status_type status;

    if ((status = flash_sector_erase(SETTINGS_SECTOR)) != FLASH_OPERATE_DONE) {
        DBG printf("SETTINGS_EraseFlashSector error: status = %d/r/n", status);
    }
}

//
bool settings_is_changed(settings_t *new_settings)
{
    uint32_t baseAddress = SETTINGS_SECTOR;
    uint32_t settings_size = sizeof(settings_t);
    uint8_t old_data;
    uint8_t new_data;
    uint8_t *ptr = (uint8_t*)new_settings;
    
    for (uint32_t i = 0; i < settings_size; i++)
    {
        old_data = (*(uint32_t*)baseAddress++);
        new_data = *ptr++;
        if (old_data != new_data)
            return true;
    }
    
    return false;
}


// -------------------------------------------------------------------------- //

void settings_print(void)
{
    uint16_t foo = settings.com_settings.mb_port;
    
    printf("\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n");
    printf("   Общие настройки для bootloader и FW");
    printf("\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\n");
    
    printf("Настройки порта (Modbus): 0x%X\r\n", settings.com_settings.mb_port);
    
    // stop bits
    foo = settings.com_settings.mb_port & MB_STOP_BIT_MASK;
    
    if (foo == MB_STOP_BIT_1)
        printf("\tStop bits: STOP_1\r\n");
    else if (foo == MB_STOP_BIT_2)
        printf("\tStop bits: STOP_2\r\n");
    
    // parity
    foo = settings.com_settings.mb_port & MB_PARITY_MASK;
    if (foo == MB_NO_PAR)
        printf("\tParity: NO_PAR\r\n");
    else if (foo == MB_EVEN_PAR)
        printf("\tParity: EVEN_PAR\r\n");
    else if (foo == MB_ODD_PAR)
        printf("\tParity: ODD_PAR\r\n");
    
    // baudrate
    foo = settings.com_settings.mb_port & MB_BRD_MASK;
    switch (foo) {
        case MB_BRD_2400 :
            printf("\tBaudrate: 2400\r\n"); break;
        case MB_BRD_4800 :
            printf("\tBaudrate: 4800\r\n"); break;
        case MB_BRD_9600 :
            printf("\tBaudrate: 9600\r\n"); break;
        case MB_BRD_19200 :
            printf("\tBaudrate: 19200\r\n"); break;
        case MB_BRD_38400 :
            printf("\tBaudrate: 38400\r\n"); break;
        case MB_BRD_57600 :
            printf("\tBaudrate: 57600\r\n"); break;
        case MB_BRD_115200 :
            printf("\tBaudrate: 115200\r\n"); break;
        default : break;
    }

    //printf("Модель: %s\r\n", settings.com_settings.model);
    
  
}
