#include "at32f403a_407.h" 
#include "settings_api.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 <string.h>
#include <stdlib.h>
#include <stdio.h>


SemaphoreHandle_t flash_mutex;

  
// Системные настройки
sys_settings_t sys_settings;

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

modbus_t mb_port_settings;


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

// Загрузка структуры настроек из flesh
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 = settings->critical_section_crc;
    
    // Рассчитываем CRC для структуры настроек
    newCRC = settings_get_crc(settings);

    // Если CRC не совпадают нужно прошивать дефолтные настройки
    if (loadCRC != newCRC) {
        need_default = true;
    }
    
    // Прошиваем дефолтные настройки если нужно
    if (need_default) 
    {
        settings_set_all_default();
        settings_save(settings);
    }
}

//
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.critical_section_crc = settings_get_crit_sec_crc(&settings);
}

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

//
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;
	}
}

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

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

//
void 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;
    
    for (uint8_t i = 0; i < 3; i++)
    {	
        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);
                break;
            }
    
        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)
            break;
    }
}

// Очистка сектора настроек
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);
    }
}

//
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;
    }
}

