/*############################################################################################*/
/*############################################################################################*/
/**
 * \file    MemoryManager.c
 * \author  Thibaut LALUQUE 3D-Ouest
 * \date    24/11/2014
 * \version 1.0
 * \brief 	Source file for Memory management.
 * \details All Right Reserved.
 */
/*############################################################################################*/
/*############################################################################################*/


/*############################################################################################*/
/*## INCLUDES ################################################################################*/
/*############################################################################################*/
#include "MemoryManager.h"
/*############################################################################################*/
/*## END INCLUDES ############################################################################*/
/*############################################################################################*/


/*############################################################################################*/
/*## LOCAL DEFINES ###########################################################################*/
/*############################################################################################*/

/*############################################################################################*/
/*## END LOCAL DEFINES ######################################################################*/
/*############################################################################################*/


/*############################################################################################*/
/*## LOCAL FONCTIONS DECLARATION #############################################################*/
/*############################################################################################*/

/*############################################################################################*/
/*## END LOCAL FONCTIONS DECLARATION #########################################################*/
/*############################################################################################*/


/*############################################################################################*/
/*## LOCAL VARIABLES #########################################################################*/
/*############################################################################################*/

/*############################################################################################*/
/*## END LOCAL VARIABLES #####################################################################*/
/*############################################################################################*/

/*############################################################################################*/
/*## GLOBAL VARIABLES ########################################################################*/
/*############################################################################################*/
tMemoryManagerCtrl 			MemoryManagerCtrl;
tMemoryManagerDiving		DivingParameters;
tMemoryManagerSerial		SerialParameters;
/*############################################################################################*/
/*## END GLOBAL VARIABLES ####################################################################*/
/*############################################################################################*/


/*############################################################################################*/
/*## LOCAL FONCTIONS #########################################################################*/
/*############################################################################################*/

/*############################################################################################*/
/*## END LOCAL FONCTIONS #####################################################################*/
/*############################################################################################*/


/*############################################################################################*/
/*## EXTERNAL FONCTIONS ######################################################################*/
/*############################################################################################*/
void MemoryManagerInit (void) {

	FM25V20Init();
	WatchdogKick();

	MemoryManagerCtrl.isValid.all = 0;		//At start-up all is false

	signed short result = MemoryManagerReadDiving(&DivingParameters);

	if(result == 0) {
		MemoryManagerCheckDiving(&DivingParameters);
	} else {
		MemoryManagerCtrl.isValid.bits.diving = 0;
	}


	result = MemoryManagerReadSerial(&SerialParameters);

	if(result == 0) {
		MemoryManagerCheckSerial(&SerialParameters);
	} else {
		MemoryManagerCtrl.isValid.bits.serial = 0;
	}

	result =  MemoryManagerComputeNbrLog ();
	if(result != 0) {
		MemoryManagerCtrl.nbrLog = 0;
	}
}

signed short MemoryManagerReadDiving (tMemoryManagerDiving* divingParameters) {
	signed short result;

	tFM25V20_LPMState LPM = FM25V20IsInLowPower();

	/*=== Wake up device if it's in lpm ===*/
	if(LPM == FM25V20_LowPowerMode) {
		FM25V20WakeUp();
		WatchdogKick();
	}

	result = FM25V20ReadData(MEMORYMANAGER_ADDR_DIVING_START, (unsigned char*)divingParameters, MEMORYMANAGER_DIVING_BLOCK_SIZE);

	/*=== restore low power mode ===*/
	if(LPM == FM25V20_LowPowerMode) {
		FM25V20LPM();
	}

	if(result == 0) {
		return 0;
	} else {
		return -1;
	}
}

void MemoryManagerCheckDiving (tMemoryManagerDiving* divingParameters) {

	unsigned char checksumExpected = ChecksumByteCompute((unsigned char*)divingParameters, MEMORYMANAGER_DIVING_BLOCK_SIZE-1);

	if(checksumExpected != divingParameters->checksum || (divingParameters->depth == 0 && divingParameters->filterLSB == 0 && divingParameters->filterMSB == 0) ) {
		MemoryManagerCtrl.isValid.bits.diving = 0;
	} else {
		MemoryManagerCtrl.isValid.bits.diving = 1;
	}
}

signed short MemoryManagerWriteDiving (tMemoryManagerDiving* divingParameters) {
	signed short result;

	tFM25V20_LPMState LPM = FM25V20IsInLowPower();

	/*=== Wake up device if it's in lpm ===*/
	if(LPM == FM25V20_LowPowerMode) {
		FM25V20WakeUp();
		WatchdogKick();
	}

	divingParameters->checksum = ChecksumByteCompute((unsigned char*)divingParameters, MEMORYMANAGER_DIVING_BLOCK_SIZE-1);

	result = FM25V20WriteData(MEMORYMANAGER_ADDR_DIVING_START, (unsigned char*)divingParameters, MEMORYMANAGER_DIVING_BLOCK_SIZE);

	/*=== restore low power mode ===*/
	if(LPM == FM25V20_LowPowerMode) {
		FM25V20LPM();
	}

	if(result == 0 && (divingParameters->depth != 0 && (divingParameters->filterLSB != 0 || divingParameters->filterMSB != 0)) ) {
		MemoryManagerCtrl.isValid.bits.diving = 1;
		return 0;
	} else {
		MemoryManagerCtrl.isValid.bits.diving = 0;
		return -1;
	}
}

signed short MemoryManagerReadSerial (tMemoryManagerSerial* serialParameters) {
	signed short result;

	tFM25V20_LPMState LPM = FM25V20IsInLowPower();

	/*=== Wake up device if it's in lpm ===*/
	if(LPM == FM25V20_LowPowerMode) {
		FM25V20WakeUp();
		WatchdogKick();
	}

	result = FM25V20ReadData(MEMORYMANAGER_ADDR_SERIAL_START, (unsigned char*)serialParameters, MEMORYMANAGER_SERIAL_BLOCK_SIZE);

	/*=== restore low power mode ===*/
	if(LPM == FM25V20_LowPowerMode) {
		FM25V20LPM();
	}

	if(result == 0) {
		return 0;
	} else {
		return -1;
	}
}

void MemoryManagerCheckSerial (tMemoryManagerSerial* serialParameters) {

	unsigned char checksumExpected = ChecksumByteCompute((unsigned char*)serialParameters, MEMORYMANAGER_SERIAL_BLOCK_SIZE-1);

	if(checksumExpected != serialParameters->checksum || (serialParameters->serial == 0)) {
		MemoryManagerCtrl.isValid.bits.serial = 0;
	} else {
		MemoryManagerCtrl.isValid.bits.serial = 1;
	}
}

signed short MemoryManagerWriteSerial (tMemoryManagerSerial* serialParameters) {
	signed short result;

	tFM25V20_LPMState LPM = FM25V20IsInLowPower();

	/*=== Wake up device if it's in lpm ===*/
	if(LPM == FM25V20_LowPowerMode) {
		FM25V20WakeUp();
		WatchdogKick();
	}

	serialParameters->checksum = ChecksumByteCompute((unsigned char*)serialParameters, MEMORYMANAGER_SERIAL_BLOCK_SIZE-1);

	result = FM25V20WriteData(MEMORYMANAGER_ADDR_SERIAL_START, (unsigned char*)serialParameters, MEMORYMANAGER_SERIAL_BLOCK_SIZE);

	/*=== restore low power mode ===*/
	if(LPM == FM25V20_LowPowerMode) {
		FM25V20LPM();
	}

	if(result == 0 && serialParameters->serial != 0) {
		MemoryManagerCtrl.isValid.bits.serial = 1;
		return 0;
	} else {
		MemoryManagerCtrl.isValid.bits.serial = 0;
		return -1;
	}
}

signed short MemoryManagerWriteLog(unsigned long date, tMemoryManagerRWOp opType, tSRDEeprom* data) {

	tMemoryManagerLog log;
	unsigned char i;

	if(MemoryManagerCtrl.nbrLog < MEMORYMANAGER_MAX_NBR_LOG) {

		log.date = date;
		log.logOp = opType;

		for(i=0 ; i<SRD_MEMORY_NBR_BYTE ; i++) {
			*( ((unsigned char*)&log) + 8 + i) = *( ( (unsigned char*)data ) + i );
		}

		unsigned long checksum = ChecksumByteOnShortCompute((unsigned char*)(&log),MEMORYMANAGER_LOG_SIZE);
		log.logOp |= (checksum<<16);

		signed short result;

		tFM25V20_LPMState LPM = FM25V20IsInLowPower();

		/*=== Wake up device if it's in lpm ===*/
		if(LPM == FM25V20_LowPowerMode) {
			FM25V20WakeUp();
			WatchdogKick();
		}

		unsigned long memAddr = MEMORYMANAGER_ADDR_LOG_START + (MemoryManagerCtrl.nbrLog * MEMORYMANAGER_LOG_SIZE);

		result = FM25V20WriteData(memAddr, (unsigned char*)(&log), MEMORYMANAGER_LOG_SIZE);

		/*=== restore low power mode ===*/
		if(LPM == FM25V20_LowPowerMode) {
			FM25V20LPM();
		}

		if(result == 0) {
			MemoryManagerCtrl.nbrLog++;
			return 0;
		} else {
			return -1;
		}
	} else {
		return -1;
	}
}

signed short MemoryManagerClearLog(void) {
	signed short result = 0;

	tFM25V20_LPMState LPM = FM25V20IsInLowPower();

	/*=== Wake up device if it's in lpm ===*/
	if(LPM == FM25V20_LowPowerMode) {
		FM25V20WakeUp();
		WatchdogKick();
	}

	MemoryManagerCtrl.nbrLog = 0;

	unsigned char data[MEMORYMANAGER_LOG_SIZE] = {0};

	//Erase
	while(result == 0 && MemoryManagerCtrl.nbrLog < MEMORYMANAGER_MAX_NBR_LOG) {
		WatchdogKick();
		result = FM25V20WriteData(MEMORYMANAGER_ADDR_LOG_START + (MemoryManagerCtrl.nbrLog * MEMORYMANAGER_LOG_SIZE), data, MEMORYMANAGER_LOG_SIZE);
		MemoryManagerCtrl.nbrLog++;
	}

	MemoryManagerCtrl.nbrLog = 0;		//Clear log counter

	/*=== restore low power mode ===*/
	if(LPM == FM25V20_LowPowerMode) {
		FM25V20LPM();
	}

	if(result == 0) {
		return 0;
	} else {
		return -1;
	}
}

signed short MemoryManagerComputeNbrLog (void) {

	signed short result = 0;

	tFM25V20_LPMState LPM = FM25V20IsInLowPower();

	/*=== Wake up device if it's in lpm ===*/
	if(LPM == FM25V20_LowPowerMode) {
		FM25V20WakeUp();
		WatchdogKick();
	}

	MemoryManagerCtrl.nbrLog = 0;
	unsigned long data = 0xFFFFFFFF;


	while(result == 0 && MemoryManagerCtrl.nbrLog < MEMORYMANAGER_MAX_NBR_LOG && data != 0) {
		WatchdogKick();
		result = FM25V20ReadData(MEMORYMANAGER_ADDR_LOG_START + (MemoryManagerCtrl.nbrLog * MEMORYMANAGER_LOG_SIZE), (unsigned char*)&data, 4);
		MemoryManagerCtrl.nbrLog++;
	}

	/*=== restore low power mode ===*/
	if(LPM == FM25V20_LowPowerMode) {
		FM25V20LPM();
	}

	if(result == -1) {
		return -1;
	} else {
		return 0;
	}
}

signed short MemoryManagerReadLog(unsigned short addr, tMemoryManagerLog* data) {

	signed short result;

	if(addr < MemoryManagerCtrl.nbrLog) {
		tFM25V20_LPMState LPM = FM25V20IsInLowPower();

		/*=== Wake up device if it's in lpm ===*/
		if(LPM == FM25V20_LowPowerMode) {
			FM25V20WakeUp();
			WatchdogKick();
		}

		unsigned long memAddr = MEMORYMANAGER_ADDR_LOG_START + (addr * MEMORYMANAGER_LOG_SIZE);

		 result = FM25V20ReadData(memAddr, (unsigned char*)data, MEMORYMANAGER_LOG_SIZE);

		unsigned long checksumSave = data->logOp;
		checksumSave = (checksumSave >> 16) & 0x0000FFFF;

		data->logOp &= 0x0000FFFF;

		unsigned long checksum = ChecksumByteOnShortCompute((unsigned char*)data,MEMORYMANAGER_LOG_SIZE);

		data->logOp |= (checksumSave<<16);

		if(checksum != checksumSave) {
			result = -1;
		}

		/*=== restore low power mode ===*/
		if(LPM == FM25V20_LowPowerMode) {
			FM25V20LPM();
		}

		return result;

	} else {
		return -1;
	}
}
/*############################################################################################*/
/*## END EXTERNAL FONCTIONS ##################################################################*/
/*############################################################################################*/
