/*
 * ul_config.c
 *
 *  Created on: May 15, 2012
 *      Author: qwer1
 */

#include <string.h>
#include "ul_config.h"
#include "ul_crc.h"

struct config_struct config;
U8  config_update_cnt;
U16 config_record;

//calculate config CRC
U16 config_crc_calc(void) {
	return crc16_modbus_buf((U8 *)&config, CONFIG_SIZE-CONFIG_CRC_SIZE, CRC16_MODBUS_INIT);
}

//check config validity
U8 config_crc_check(void) {
	return (config.crc == config_crc_calc());
}

//clear configuration (fill with default values)
void config_init(void) {
	memset(&config, 0, CONFIG_SIZE);
	config_data_init();
}

//read record into config
void config_record_read(U16 record) {
	if (record < CONFIG_RECORDS) {
		#ifdef __AVR
			eeprom_read_block(&config, CONFIG_RECORD(record), CONFIG_SIZE);
		#endif
		#ifdef __STM32
			memcpy(&config, CONFIG_RECORD(record), CONFIG_SIZE);
		#endif
	}
}

//find last used (valid) record. If not found, returns 0xFFFF;
U16 config_record_last(void) {
	for (U16 i=0; i<CONFIG_RECORDS; i++) {
		config_record_read(i);
		if (!config_crc_check()) return i - 1;
	}
	return CONFIG_RECORDS - 1;
}

//read last valid configuration from EEPROM
U8 config_read(void) {
	//search last good record
	config_record = config_record_last();
	if (config_record == 0xFFFF) {
		config_record = CONFIG_RECORDS - 1;
		return 0;
	} else {
		//read last good config
		config_record_read(config_record);
		//check CRC
		if (!config_crc_check()) return 0;
		else return 1;
	}
}

void config_erase(void) {
	#ifdef __AVR
		for (U16 a=CONFIG_START_ADDR; a<CONFIG_END_ADDR; a+=4)
			eeprom_update_dword((U32 *)a, 0xFFFFFFFF);
	#endif
	#ifdef __STM32
		FLASH_Unlock();
		FLASH_ErasePage(CONFIG_START_ADDR);
		FLASH_Lock();
	#endif
}

//write config to next record
void config_write(void) {
	//update other data in config record
	config_data_copy();

	//calculate CRC
	config.crc = config_crc_calc();

	//last page ? Erase all.
	if (++config_record >= CONFIG_RECORDS) config_record = 0;
	if (config_record == 0) config_erase();

	//write block
	#ifdef __AVR
		eeprom_update_block(&config, CONFIG_RECORD(config_record), CONFIG_SIZE);
	#endif
	#ifdef __STM32
		U32 *source_addr = (void *)&config;
		U32 *dest_addr = (void *)CONFIG_RECORD(config_record);
		FLASH_Unlock();
		for (U16 i=0; i<CONFIG_WORDS; i++) {
			FLASH_ProgramWord((U32)dest_addr, *source_addr);
			source_addr++;
			dest_addr++;
		}
		FLASH_Lock();
	#endif
}

void setup_config(void) {
	//read last config, if error - init with defaults
	if (!config_read()) config_init();

	//start configuration update thread
	ptx_add(pt_config, NULL, PTX_FLAG_RUN | PTX_FLAG_SYNC_100MS);
}

PT_THREAD( pt_config( struct pt *pt, struct ptx *ptx ) ) {
	PT_BEGIN(pt);
	while (1) {
		if (config_update_cnt) {
			if (--config_update_cnt == 0) {
				config_write();
			}
		}
		PT_YIELD(pt);
	}
	PT_END(pt);
}
