/*
 * $Id: user_config.c 464 2019-08-01 02:15:54Z true $
 * begin 20190615 true
 */

#include "user_config.h"

#include "driver/efm8ub2_userflash.h"
#include "math/checksum.h"

#include <string.h>


UserConf xdata uconf;
uint8_t sysflags;

uint8_t idata last_valid_offset;
uint8_t idata last_empty_offset;


static void uconf_defaults()
{
	uconf.ver			 = UCONF_VER;
	uconf.iter			 = 0;
	uconf.flags			 = UCONF_FLAGS_LEDS_ENABLE;
	uconf.framemod		 = UCONF_FRAMERATE_FULL;
	uconf.nameconf 		 = UCONF_NAME_DISP_CHAR_ROTATE | UCONF_NAME_MODE_AUTOROTATE | UCONF_NAME_MODE_COLOR_INVERT;
	uconf.font_idx		 = 3;
	uconf.char_spacing	 = 2;

	strcpy(uconf.name, "Anon");

	uconf.favcolor_hue   = 170;
	uconf.favcolor_sat   = 240;
	uconf.favcolor_val   = 32;

	uconf.altcolor_hue	 = 0;
	uconf.altcolor_sat   = 240;
	uconf.altcolor_val   = 32;

	uconf.ledprog_active = 13 | (6 << 4);

	uconf.checksum		 = checksum_gen((uint8_t *)&uconf, sizeof(uconf) - 1);
}

static uint16_t uconf_validate(uint8_t offset)
{
	uint8_t c;
	UserConf code * data flash;

	flash = (UserConf code *)(userflash_get_baseaddr() + (offset * FLASH_UCONF_BYTES));

	// version check
	if (flash->ver != UCONF_VER) {
		return 0;
	}

	// checksum
	c = checksum_gen((uint8_t *)flash, sizeof(uconf) - 1);
	if (flash->checksum != c) {
		return 0;
	}

	return flash->iter;
}

void uconf_load()
{
	uint8_t i;

	uint16_t test_iter;

	uint8_t  valid_offset;
	uint16_t valid_iter;

	uint16_t base;

	valid_offset = 0;
	last_valid_offset = 7;

	// set default reserved pages
	base = userflash_reserve_pages(FLASH_RSVD_PAGES);

	// we are assuming 128 byte data structure.
	// each flash page is 512 bytes. so, with the code as-is, we can use 1 or 2 pages.
	for (i = 0; i < ((FLASH_RSVD_PAGES - 3) * 4); i++) {
		test_iter = uconf_validate(i);
		if (test_iter && test_iter > valid_iter) {
			valid_offset = 0x80 | i;
			valid_iter = test_iter;
		}
	}

	if (valid_offset) {
		valid_offset &= 0x7f;
		last_valid_offset = valid_offset;
		userflash_read(base + (valid_offset * FLASH_UCONF_BYTES), (uint8_t *)&uconf, FLASH_UCONF_BYTES);
	} else {
		uconf_defaults();
	}
}

void uconf_write()
{
	uint8_t offset;
	uint16_t base;

	base = userflash_get_baseaddr();

	// make sure our target is empty (bugged writes, etc);
	// we check just the first byte for emptiness for now
	last_empty_offset = 0xff;
	for (offset = ((last_valid_offset + 1) % 8); offset < ((FLASH_RSVD_PAGES - 3) * 4); offset++) {
		if (*(uint8_t code *)(base + (offset * sizeof(uconf))) == 0xff) {
			last_empty_offset = offset;
			break;
		}
	}

	if (last_empty_offset == 0xff) {
		// we have no valid pages remaining
		offset = 0;
	} else {
		offset = last_empty_offset;
	}

	// check iteration and clear pages if necessary
	uconf.iter++;
	if (!uconf.iter) {
		// erase all pages
		for (offset = 0; offset < (FLASH_RSVD_PAGES - 3); offset++) {
			userflash_erase_page(base + (offset * 512));
		}
		// and set initial valid iter
		uconf.iter = 1;
		offset = 0;
	} else {
		// new page? erase it
		if ((offset % 4) == 0) {
			userflash_erase_page(base + ((offset / 4) * 512));
		}
	}

	// write new data
	uconf.checksum = checksum_gen((uint8_t *)&uconf, sizeof(uconf) - 1);
	userflash_write(base + (offset * FLASH_UCONF_BYTES), (uint8_t *)&uconf, FLASH_UCONF_BYTES);
}
