/*
 * EEPROM_M24C64.c
 *
 * Created: Fri Dec 24 2021 12:24:26
 *  Author Chris
 */

// ============================================================================================
// Includes
#include "EEPROM_M24C64.h"
#include "I2C_Master.h"

#include "Hue.h"
#include "MIDI_Note_List.h"


// ============================================================================================
// Defines
#define EEPROM_I2C_ADDRESS			0b1010000
#define EEPROM_PAGE_SIZE			32
#define EEPROM_CONTENT_SIZE			sizeof(EEPROM_Content_t)
#define EEPROM_READ_WRITE_STEPS		(EEPROM_CONTENT_SIZE / EEPROM_PAGE_SIZE) + 1


// ============================================================================================
// Variables
volatile EEPROM_Content_t		_EEPROM_Content;
volatile bool					_Update_Triggered;
volatile uint8_t				_Write_Status;
volatile uint					_Read_Write_Counter;


// ============================================================================================
// Function Declarations
void EEPROM_Read_Data();
bool EEPROM_Write_Data(uint section);
void EEPROM_Check_Content_Valid();


/*******************************************************************
	Interrupt Service Routines
*******************************************************************/


/*******************************************************************
	Functions
*******************************************************************/
void EEPROM_Init(void)
{
	_Update_Triggered 	= false;
	_Write_Status 		= EEPROM_STATUS_NONE;
	_Read_Write_Counter	= 0;

	EEPROM_Read_Data();
	EEPROM_Check_Content_Valid();
}

void EEPROM_Tick(void)
{
	if(_Update_Triggered == true) {
		// Wait 1ms to let other I2C traffic to be completed
		sleep_ms(1);
	
		EEPROM_Write_Data(_Read_Write_Counter);
		_Read_Write_Counter++;

		if(_Read_Write_Counter == EEPROM_READ_WRITE_STEPS) {
			_Update_Triggered = false;
		}
	}
}

void EEPROM_Trigger_Update()
{
	_Read_Write_Counter	= 0;
	_Update_Triggered 	= true;
}

uint8_t EEPROM_Get_Write_Status()
{
	uint8_t Return_Value = _Write_Status;

	_Write_Status = EEPROM_STATUS_NONE;

	return Return_Value;
}

uint EEPROM_Get_Content_Size()
{
	return EEPROM_CONTENT_SIZE;
}

uint EEPROM_Get_Read_Write_Steps()
{
	return EEPROM_READ_WRITE_STEPS;
}


/*******************************************************************
	Internal Functions
*******************************************************************/
void EEPROM_Read_Data(void)
{
	for(uint i=0;i<EEPROM_READ_WRITE_STEPS;i++)
	{
		uint16_t EEPROM_Read_Address = i * EEPROM_PAGE_SIZE;

		uint8_t* Read_Address = ((uint8_t*)&_EEPROM_Content) + i * EEPROM_PAGE_SIZE;

		uint8_t Read_Size = EEPROM_CONTENT_SIZE - i * EEPROM_PAGE_SIZE;
		if(Read_Size > EEPROM_PAGE_SIZE) {
			Read_Size = EEPROM_PAGE_SIZE;
		}

		I2CM_Packet_Receive(EEPROM_I2C_ADDRESS, EEPROM_Read_Address, sizeof(EEPROM_Read_Address), Read_Address, Read_Size);
	}
}

bool EEPROM_Write_Data(uint section)
{
	uint16_t EEPROM_Write_Address = section * EEPROM_PAGE_SIZE;
	
	uint8_t* Write_Address = ((uint8_t*)&_EEPROM_Content) + section * EEPROM_PAGE_SIZE;

	uint8_t Write_Size = EEPROM_CONTENT_SIZE - section * EEPROM_PAGE_SIZE;
	if(Write_Size > EEPROM_PAGE_SIZE) {
		Write_Size = EEPROM_PAGE_SIZE;
	}

	int Bytes_Written = I2CM_Packet_Transmit(EEPROM_I2C_ADDRESS, EEPROM_Write_Address, sizeof(EEPROM_Write_Address), Write_Address, Write_Size);

	if(Bytes_Written == sizeof(EEPROM_Write_Address) + Write_Size) {
		_Write_Status = EEPROM_STATUS_WRITE_OK;
		return true;
	}

	_Write_Status = EEPROM_STATUS_WRITE_FAILED;
	return false;
}

void EEPROM_Check_Content_Valid()
{
	if(_EEPROM_Content.Device_Configuration.Idle_Screen > IDLE_SCREEN_MODE_ACTIVITY) {
		_EEPROM_Content.Device_Configuration.Idle_Screen = IDLE_SCREEN_BLACK;
	}

	if(_EEPROM_Content.Device_Configuration.Screen_Timeout > SCREEN_TIMEOUT_MAX_s) {
		_EEPROM_Content.Device_Configuration.Screen_Timeout = SCREEN_TIMEOUT_MAX_s;
	}

	// Reverse Scrolling no need, as bool is compared with equal to 0

	if(_EEPROM_Content.Channel_MIDI_Configuration[LED_Channel_1].MIDI_Channel > MIDI_CHANNEL_16) {
		_EEPROM_Content.Channel_MIDI_Configuration[LED_Channel_1].MIDI_Channel = MIDI_CHANNEL_1;
	}

	if(	_EEPROM_Content.Channel_MIDI_Configuration[LED_Channel_1].Octave > _MIDI_Note_List[MIDI_NOTE_LIST_LENGTH-1].Octave || 
		_EEPROM_Content.Channel_MIDI_Configuration[LED_Channel_1].Octave < _MIDI_Note_List[0].Octave) {
		_EEPROM_Content.Channel_MIDI_Configuration[LED_Channel_1].Octave = _MIDI_Note_List[0].Octave;
	}

	// Note Colors no need, as full range of UINT8 is valid

	if(_EEPROM_Content.Channel_MIDI_Configuration[LED_Channel_1].Skip_Note_Off_Event > 1) {
		_EEPROM_Content.Channel_MIDI_Configuration[LED_Channel_1].Skip_Note_Off_Event = 1;
	}

	if(_EEPROM_Content.Pause_Light_Configuration[LED_Channel_1].Enabled > 1) {
		_EEPROM_Content.Pause_Light_Configuration[LED_Channel_1].Enabled = 1;
	}

	// Pause Light Color no need, as full range of UINT8 is valid

	if(_EEPROM_Content.Pause_Light_Configuration[LED_Channel_1].Timeout > PAUSE_LIGHT_DELAY_MAX_s) {
		_EEPROM_Content.Pause_Light_Configuration[LED_Channel_1].Timeout = PAUSE_LIGHT_DELAY_MIN_s;
	}

	if(_EEPROM_Content.Pause_Light_Configuration[LED_Channel_1].Reset_Condition > CHANNEL_AND_EVENT_MATCH) {
		_EEPROM_Content.Pause_Light_Configuration[LED_Channel_1].Reset_Condition = ANY_TRAFFIC;
	}

	if(_EEPROM_Content.Pause_Light_Configuration[LED_Channel_1].Fade_Speed > FADE_SPEED_MAX) {
		_EEPROM_Content.Pause_Light_Configuration[LED_Channel_1].Fade_Speed = FADE_SPEED_MIN;
	}

	// Pause Light Timer and Active no need here

	// Note Stack Enabled no need, as bool is compared with equal to 0

	if(_EEPROM_Content.Jam_Light_Configuration.Duration_Max_s > DURATION_MAX_s) {
		_EEPROM_Content.Jam_Light_Configuration.Duration_Max_s = DURATION_MAX_s;
	}

	if(_EEPROM_Content.Jam_Light_Configuration.Duration_Min_s > _EEPROM_Content.Jam_Light_Configuration.Duration_Max_s || _EEPROM_Content.Jam_Light_Configuration.Duration_Min_s < DURATION_MIN_s) {
		_EEPROM_Content.Jam_Light_Configuration.Duration_Min_s = DURATION_MIN_s;
	}

	if(_EEPROM_Content.Jam_Light_Configuration.Duration_Max_s <= _EEPROM_Content.Jam_Light_Configuration.Duration_Min_s) {
		_EEPROM_Content.Jam_Light_Configuration.Duration_Max_s = DURATION_MAX_s;
	}

	while(_EEPROM_Content.Jam_Light_Configuration.Hue_Angle_Start_Color < 0) {
		_EEPROM_Content.Jam_Light_Configuration.Hue_Angle_Start_Color += HUE_MAX_ANGLE;
	}
	
	_EEPROM_Content.Jam_Light_Configuration.Hue_Angle_Start_Color %= HUE_MAX_ANGLE;

	if(_EEPROM_Content.Jam_Light_Configuration.Color_Change > COLOR_CHANGE_MAX) {
		_EEPROM_Content.Jam_Light_Configuration.Color_Change = COLOR_CHANGE_MIN;
	}

	if(_EEPROM_Content.Jam_Light_Configuration.Fade_Speed > FADE_SPEED_MAX) {
		_EEPROM_Content.Jam_Light_Configuration.Fade_Speed = FADE_SPEED_MIN;
	}

	// Const Color no need, as full range of UINT8 is valid

	if(_EEPROM_Content.Const_Light_Configuration.Fade_Speed > FADE_SPEED_MAX) {
		_EEPROM_Content.Const_Light_Configuration.Fade_Speed = FADE_SPEED_MIN;
	}

	if(_EEPROM_Content.Device_Configuration.Transition_Type >= 0) {
		_EEPROM_Content.Device_Configuration.Transition_Type = 0;
	}

	if(_EEPROM_Content.Device_Configuration.Transition_Frames == 0) {
		_EEPROM_Content.Device_Configuration.Transition_Frames = 16;
	}
}