#include "fpaa.h"

#include "logger_hw_defs.h"
#include "rprintf.h"
#include "timer.h"
#include "dosfs.h"
#include "storage.h"
#include "global.h"
#include "inttypes.h"
#include <avr/eeprom.h>
#include "stdlib.h"

void fpaa_loadConfig(const char *fpaa_config_data, uint32_t size) {
	uint32_t i;
	u16 wait;

	sbi(PORTA, LED6);
	#ifdef FPAA_DEBUG
	if (FPAA_ACTIVATE_PORT & (1 << FPAA_ACTIVATE)) {
		rprintf("FPAA was already configured\r\n");
	}
	#endif

	// toggle power-on-reset pin
	cbi(FPAA_POR_PORT, FPAA_POR);
	sbi(FPAA_POR_PORT, FPAA_POR);

	// wait 30ms
	for (i = 0; i < 120; i++) {
		delay_us(250);
	}

	// assert cs
	cbi(FPAA_CS_PORT, FPAA_CS_PIN);				

	
	// provide at least 40 clocks for power-up-sequence
	for (i = 0; i < 5; i++)	{
		spiTransferByte(0x00);
	}

	// send header- and datablocks
	for (i = 0; i < size; i++) {
		spiTransferByte(fpaa_config_data[i]);
	}

	// provide 8 clocks for state machine to finish config
	spiTransferByte(0x00);

	// deassert CS
	sbi(FPAA_CS_PORT, FPAA_CS_PIN);		

	// check if the device was configured, by polling Activate pin
	while (!(FPAA_ACTIVATE_PORT & (1 << FPAA_ACTIVATE)) && wait < 0xFFE) {
		wait++;
	}

	if (wait == 0xFFE) {
		rprintf("Configuration of FPAA failed\r\n");
	}
	#ifdef FPAA_DEBUG
	else
		rprintf("FPAA configured after %d retries\r\n", wait);
	#endif	
	cbi(PORTA, LED6);	
}

// set's up an empty config index for fpaa
void fpaa_initConfigIndex() {
	int i;
	FPAA_CONFIG_t configIndex_RAM[FPAA_CONFIGINDEX_SIZE];

	for (i = 0; i < FPAA_CONFIGINDEX_SIZE; i++) {
		configIndex_RAM[i].eeprom_addr = (uint8_t *) (FPAA_CONFIGBLOCK_STARTBLOCK_ADDR + i * FPAA_CONFIGBLOCK_SIZE);
		configIndex_RAM[i].size = 0;
		//rprintf("eeprom_addr %u\r\n",configIndex_RAM[i].eeprom_addr);
	}

	// put the configuration index into the eeprom
	eeprom_write_block(configIndex_RAM, FPAA_CONFIGINDEX_EEPROM_ADDR, sizeof(configIndex_RAM));

	#ifdef FPAA_DEBUG
	rprintf("fpaa_initConfigIndex() - empty index placed in eeprom\r\n");
	#endif

}

int fpaa_storeConfigEEP(char *buffer, uint32_t size) {
	int i;
	FPAA_CONFIG_t configIndex_RAM[FPAA_CONFIGINDEX_SIZE];

	// read the config from the eeprom into ram so we can work on it
	eeprom_read_block(configIndex_RAM, FPAA_CONFIGINDEX_EEPROM_ADDR, sizeof(configIndex_RAM));

	// find an empty block for the config
	for (i = 0; i < FPAA_CONFIGINDEX_SIZE; i++) {
		if (configIndex_RAM[i].size == 0)
			break;	// found an empty block
	}

	// check if we reached the end
	if (i >= FPAA_CONFIGINDEX_SIZE) {
		#ifdef FPAA_DEBUG
		rprintf("fpaa_storeConfigEEP() - FPAA configuration buffer full!\r\n");
		#endif
		return 0;
	}
	
	if (size <= FPAA_CONFIGBLOCK_SIZE) {
		// copy the binary configuration to the eeprom
		eeprom_write_block(buffer, configIndex_RAM[i].eeprom_addr,size);
		#ifdef FPAA_DEBUG
		rprintf("fpaa_storeConfigEEP() - config stored in eeprom, size %u\r\n",size);
		#endif

		// update the configuration index
		configIndex_RAM[i].size = size;

		// write it back to eeprom
		eeprom_write_block(configIndex_RAM, FPAA_CONFIGINDEX_EEPROM_ADDR, sizeof(configIndex_RAM));

		#ifdef FPAA_DEBUG
		rprintf("fpaa_storeConfigEEP() - config stored, 0-based index %u\r\n",i);
		#endif
		return i + 1;
	}
	else {
		#ifdef FPAA_DEBUG
		rprintf("fpaa_storeConfigEEP() - config too large!\r\n");
		#endif
		return 0;
	}
	
}

void fpaa_loadConfigEEP(int configBlock_1indexed) {
	FPAA_CONFIG_t configIndex_RAM[FPAA_CONFIGINDEX_SIZE];
	uint8_t *buffer;
	int configBlock0;

	// compensate for 1-indexed
	configBlock0 = configBlock_1indexed - 1;

	// read the config from the eeprom into ram so we can work on it
	eeprom_read_block(configIndex_RAM, FPAA_CONFIGINDEX_EEPROM_ADDR, sizeof(configIndex_RAM));

	if (configBlock0 <= FPAA_CONFIGINDEX_SIZE && configBlock0 >= 0) {
		if (configIndex_RAM[configBlock0].size > 0) {
			uint32_t blockSize = configIndex_RAM[configBlock0].size;
			// allocate space on the heap for the configuration
			if ((buffer = (uint8_t *) malloc(blockSize)) != NULL) {
				eeprom_read_block(buffer, configIndex_RAM[configBlock0].eeprom_addr, blockSize);

				// send the configuration to the fpaa
				fpaa_loadConfig((char *)buffer, blockSize);

				free(buffer);
			}
			else {
				#ifdef FPAA_DEBUG
				rprintf("fpaa_loadConfigEEP() - Could not malloc!\r\n");
				#endif
			}	
		}
		else {
			#ifdef FPAA_DEBUG
			rprintf("fpaa_loadConfigEEP() - configuration block empty\r\n");
			#endif
		}
	}
	else {
		#ifdef FPAA_DEBUG
		rprintf("fpaa_loadConfigEEP() - config block out of bounds!\r\n");
		#endif 
	}
}

int readBinaryConfig(char *filepath) {
	//uint32_t res;
	int res;
	FILEINFO fi;
	uint32_t bytesread;
	uint8_t *buffer;

	if (!storageInitialized) {
		rprintf("Storage not initialized!");
		return 0;
	}

	if (DFS_OpenFile(&vi, filepath, DFS_READ, fatscratch, &fi)) {
		rprintf("error opening file for read\r\n");
	}
	else if (fi.filelen > 0) {
		if ((buffer = (uint8_t *) malloc(fi.filelen)) != NULL) {
			//rprintf("Malloc succeeded, pointer=%u\r\n",buffer);
			
			DFS_ReadFile(&fi, fatscratch, buffer, &bytesread, SECTOR_SIZE);
	
			res = fpaa_storeConfigEEP((char *)buffer,bytesread);

			free(buffer);
		}
		else {
			rprintf("Could not malloc for reading file");
		}
	}
	return res;
}
