/*
 * Core1_Light_Controller.c
 *
 * Created: Sat Jan 21 2023 16:22:51
 *  Author Chris
 */
// ============================================================================================
// Includes
#include "Core1_Light_Controller.h"

#include "MIDI_Note_List.h"
#include "Core1_LED_Control.h"
#include "Command_Definition.h"


// ============================================================================================
// Defines
#define PAUSE_LIGHT_TIMEOUT_TICKS					(_EEPROM_Content.Pause_Light_Configuration[ch].Timeout * (1000 / TIMER_INTERVALL_LED_UPDATE_ms))

#define NOTE_COLOR_RED								_EEPROM_Content.Channel_MIDI_Configuration[ch].Note_Color_Red
#define NOTE_COLOR_GREEN							_EEPROM_Content.Channel_MIDI_Configuration[ch].Note_Color_Green
#define NOTE_COLOR_BLUE								_EEPROM_Content.Channel_MIDI_Configuration[ch].Note_Color_Blue

#define NOTE_COLOR_RED_ALT							NOTE_COLOR_RED + 1
#define NOTE_COLOR_GREEN_ALT						NOTE_COLOR_GREEN + 1
#define NOTE_COLOR_BLUE_ALT							NOTE_COLOR_BLUE + 1

#define NOTE_COLOR_COUNT_RESET_THRESHOLD_TICKS		(_EEPROM_Content.Channel_MIDI_Configuration[ch].Note_Reset_Timeout * (1000 / TIMER_INTERVALL_LED_UPDATE_ms))

// #define COUNT_APPLIED_NOTES


// ============================================================================================
// Datatypes


// ============================================================================================
// Variables
volatile bool							_MIDI_To_Light_Enabled;
extern volatile EEPROM_Content_t 		_EEPROM_Content;

Info_Last_Received_Note_t		_Info_Last_Received_Note[NUM_LED_CHANNELS];
Info_Last_Received_Note_t		_Info_Last_Applied_Note[NUM_LED_CHANNELS];
volatile Pause_Light_Timer_s	_Pause_Light_Timer[NUM_LED_CHANNELS];
volatile int					_NoteOn_Color_Counter[NUM_LED_CHANNELS][NUM_LED_COLORS];
volatile int					_NoteOn_Color_Reset_Counter;

                                // 1 LED Channel, 3 LED Colors, 2 Event Types (Note On & Off)
int32_t                         _Event_Received_Counter[NUM_LED_CHANNELS][NUM_LED_COLORS][2];

static const uint8_t _PWM_Lookup[NUM_LED_COLORS][128] = {
    {   // Red
        0x04, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08,
        0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0B,
        0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x11,
        0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x15, 0x16, 0x17, 0x18, 0x19, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
        0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2A, 0x2C, 0x2D, 0x2E, 0x30, 0x32,
        0x33, 0x35, 0x37, 0x38, 0x3A, 0x3C, 0x3E, 0x40, 0x42, 0x45, 0x47, 0x49, 0x4B, 0x4E, 0x50, 0x53,
        0x56, 0x58, 0x5B, 0x5E, 0x61, 0x64, 0x68, 0x6B, 0x6E, 0x72, 0x76, 0x79, 0x7E, 0x82, 0x86, 0x8B,
        0x8F, 0x94, 0x99, 0x9E, 0xA6, 0xAC, 0xB2, 0xB9, 0xBF, 0xC6, 0xCD, 0xD5, 0xDD, 0xE5, 0xED, 0xFF
    },
    {   // Green
        0x03, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08,
        0x08, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, 0x0D, 0x0E, 0x0E, 0x0F, 0x0F,
        0x10, 0x11, 0x12, 0x12, 0x13, 0x14, 0x15, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
        0x1E, 0x1F, 0x20, 0x21, 0x23, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2A, 0x2C, 0x2D, 0x2F, 0x30, 0x32,
        0x33, 0x35, 0x37, 0x38, 0x3A, 0x3C, 0x3D, 0x3F, 0x41, 0x43, 0x45, 0x47, 0x49, 0x4B, 0x4D, 0x4F,
        0x51, 0x53, 0x55, 0x57, 0x5A, 0x5C, 0x5E, 0x60, 0x63, 0x65, 0x68, 0x6B, 0x6F, 0x72, 0x75, 0x78,
        0x7A, 0x7D, 0x81, 0x84, 0x87, 0x8A, 0x8D, 0x90, 0x94, 0x98, 0x9B, 0x9F, 0xA2, 0xA6, 0xAA, 0xAE,
        0xB3, 0xB6, 0xBA, 0xBF, 0xC3, 0xC8, 0xCC, 0xD1, 0xD6, 0xDB, 0xE0, 0xE5, 0xE9, 0xEE, 0xF1, 0xFF
    },
    {   // Blue
        0x01, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08,
        0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D,
        0x0E, 0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x16, 0x17, 0x18,
        0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x25, 0x26, 0x27, 0x29, 0x2A,
        0x2B, 0x2D, 0x2E, 0x30, 0x31, 0x33, 0x35, 0x36, 0x38, 0x39, 0x3B, 0x3D, 0x3F, 0x40, 0x42, 0x44,
        0x46, 0x49, 0x4B, 0x4E, 0x50, 0x52, 0x55, 0x57, 0x59, 0x5C, 0x5E, 0x61, 0x63, 0x66, 0x69, 0x6C,
        0x6F, 0x72, 0x75, 0x77, 0x7B, 0x7E, 0x81, 0x85, 0x88, 0x8C, 0x90, 0x93, 0x97, 0x9B, 0x9F, 0xA3,
        0xA8, 0xAC, 0xB0, 0xB5, 0xBA, 0xBE, 0xC4, 0xC9, 0xCE, 0xD4, 0xD9, 0xDF, 0xE4, 0xEA, 0xEF, 0xFF
    }
};


// ============================================================================================
// Function Declarations
bool Core1_Light_Controller_Check_Channel_Match	(enum LED_Channel channel, uint8_t midi_channel);
bool Core1_Light_Controller_Check_Octave_Match	(enum LED_Channel channel, uint midi_note);
void Core1_Light_Controller_Pause_Light_Trigger	(uint8_t midi_event, uint8_t midi_channel);
void Core1_Light_Controller_Reset_NoteOn_Counter();


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


/*******************************************************************
	Functions
*******************************************************************/
void Core1_Light_Controller_Init(void)
{
	_MIDI_To_Light_Enabled = true;
	
	for(uint32_t ch=0;ch<NUM_LED_CHANNELS;ch++)
	{
		_Info_Last_Received_Note[ch].Event 			= 0;
		_Info_Last_Received_Note[ch].Note 			= NO_NOTE;
		_Info_Last_Received_Note[ch].Note_In_Octave	= NO_NOTE;
		_Info_Last_Received_Note[ch].Velocity 		= 0;
		_Info_Last_Received_Note[ch].Timestamp_ms 	= 0;

		_Info_Last_Applied_Note[ch].Event 			= 0;
		_Info_Last_Applied_Note[ch].Note 			= NO_NOTE;
		_Info_Last_Applied_Note[ch].Note_In_Octave	= NO_NOTE;
		_Info_Last_Applied_Note[ch].Velocity 		= 0;
		_Info_Last_Applied_Note[ch].Timestamp_ms 	= 0;

        for(uint32_t col=0;col < NUM_LED_COLORS;col++) {
            _Event_Received_Counter[ch][col][MIDI_EVENT_NOTE_OFF - MIDI_EVENT_NOTE_OFF] = 0;
            _Event_Received_Counter[ch][col][MIDI_EVENT_NOTE_ON - MIDI_EVENT_NOTE_OFF] = 0;
        }
	}

	Core1_Light_Controller_Reset_NoteOn_Counter();
	_NoteOn_Color_Reset_Counter = 0;
}

void Core1_Light_Controller_Tick(void)
{
	if(_MIDI_To_Light_Enabled != true) {
		return;
	}
	
	if(Core1_LED_Control_Get_Timer_Fired() != true) {
		return;
	}

	for(uint ch=0;ch<NUM_LED_CHANNELS;ch++)
	{
		if(_EEPROM_Content.Pause_Light_Configuration[ch].Enabled == 0) {
			continue;
		}

		if(_Pause_Light_Timer[ch].Timer == PAUSE_LIGHT_TIMEOUT_TICKS && _Pause_Light_Timer[ch].Is_Active != true)
		{
			_Pause_Light_Timer[ch].Is_Active = true;

			Core1_LED_Control_Set_Fade_Speed(ch, _EEPROM_Content.Pause_Light_Configuration[ch].Fade_Speed);

			Core1_LED_Control_Set_DC_Target(ch, R, _EEPROM_Content.Pause_Light_Configuration[ch].Color.R);
			Core1_LED_Control_Set_DC_Target(ch, G, _EEPROM_Content.Pause_Light_Configuration[ch].Color.G);
			Core1_LED_Control_Set_DC_Target(ch, B, _EEPROM_Content.Pause_Light_Configuration[ch].Color.B);
		}

		if(_Pause_Light_Timer[ch].Timer < PAUSE_LIGHT_TIMEOUT_TICKS) {
			_Pause_Light_Timer[ch].Timer++;
		}

        if(_EEPROM_Content.Channel_MIDI_Configuration[0].Note_Reset_Enabled > 0)
        {
        	if(_NoteOn_Color_Reset_Counter < NOTE_COLOR_COUNT_RESET_THRESHOLD_TICKS) {
        		_NoteOn_Color_Reset_Counter++;
        	} 
        	else {
        		Core1_Light_Controller_Reset_NoteOn_Counter();
        	}
        }
	}
}

void Core1_Light_Controller_MIDI_OnOff_Event_Received(uint8_t midi_command_shifted_right, uint8_t midi_channel)
{
	if(_MIDI_To_Light_Enabled != true) {
		return;
	}
	
	Core1_Light_Controller_Pause_Light_Trigger(midi_command_shifted_right, midi_channel);
}

void Core1_Light_Controller_MIDI_Other_Event_Received(uint8_t midi_data)
{
	if(_MIDI_To_Light_Enabled != true) {
		return;
	}
	
	if(IS_MIDI_COMMAND_WITH_CHANNEL(midi_data)) {
		Core1_Light_Controller_Pause_Light_Trigger(MIDI_EVENT_FROM_COMMAND(midi_data), MIDI_CHANNEL_FROM_COMMAND(midi_data));
	} else {
		Core1_Light_Controller_Pause_Light_Trigger(midi_data, MIDI_CHANNEL_16 + 1);
	}
}

void Core1_Light_Controller_MIDI_Full_Note_Received(uint8_t midi_event, uint8_t midi_channel, uint8_t midi_note, uint8_t value)
{
	if(_MIDI_To_Light_Enabled != true) {
		return;
	}
	
	Core1_Light_Controller_Pause_Light_Trigger(midi_event, midi_channel);
	
	for(uint ch=0;ch<NUM_LED_CHANNELS;ch++)
	{		
		if(Core1_Light_Controller_Check_Channel_Match(ch, midi_channel) != true) {
			continue;
		}

		if(Core1_Light_Controller_Check_Octave_Match(ch, midi_note) != true) {
			continue;
		}
		
		uint8_t midi_note_in_octave = midi_note % NOTES_PER_OCTAVE;

		_Info_Last_Received_Note[ch].Event 			= midi_event;
		_Info_Last_Received_Note[ch].Note 			= midi_note;
		_Info_Last_Received_Note[ch].Note_In_Octave	= midi_note_in_octave;
		_Info_Last_Received_Note[ch].Velocity 		= value;
		_Info_Last_Received_Note[ch].Timestamp_ms 	= to_ms_since_boot(get_absolute_time());

		_NoteOn_Color_Reset_Counter = 0;

		bool Note_Applied = true;	

		if(midi_event == MIDI_EVENT_NOTE_ON)
		{
			if(midi_note_in_octave == NOTE_COLOR_RED || midi_note_in_octave == NOTE_COLOR_RED_ALT)
			{
				uint8_t DutyCycle = value;
				if(_EEPROM_Content.Device_Configuration.Use_Color_Correction > 0) {	DutyCycle = _PWM_Lookup[R][value]; } else { DutyCycle = value << 1; }
				
				Core1_LED_Control_Set_DC_Direct(ch, R, DutyCycle);
				_NoteOn_Color_Counter[ch][R]++;

#ifdef COUNT_APPLIED_NOTES
                _Event_Received_Counter[ch][R][MIDI_EVENT_NOTE_ON - MIDI_EVENT_NOTE_OFF]++;
#endif
			}
			else if(midi_note_in_octave == NOTE_COLOR_GREEN || midi_note_in_octave == NOTE_COLOR_GREEN_ALT)
			{
				uint8_t DutyCycle = value;
				if(_EEPROM_Content.Device_Configuration.Use_Color_Correction > 0) {	DutyCycle = _PWM_Lookup[G][value]; } else { DutyCycle = value << 1; }
				
				Core1_LED_Control_Set_DC_Direct(ch, G, DutyCycle);
				_NoteOn_Color_Counter[ch][G]++;

#ifdef COUNT_APPLIED_NOTES
                _Event_Received_Counter[ch][G][MIDI_EVENT_NOTE_ON - MIDI_EVENT_NOTE_OFF]++;
#endif
			}
			else if(midi_note_in_octave == NOTE_COLOR_BLUE || midi_note_in_octave == NOTE_COLOR_BLUE_ALT)
			{
				uint8_t DutyCycle = value;
				if(_EEPROM_Content.Device_Configuration.Use_Color_Correction > 0) {	DutyCycle = _PWM_Lookup[B][value]; } else { DutyCycle = value << 1; }

				Core1_LED_Control_Set_DC_Direct(ch, B, DutyCycle);
				_NoteOn_Color_Counter[ch][B]++;

#ifdef COUNT_APPLIED_NOTES
                _Event_Received_Counter[ch][B][MIDI_EVENT_NOTE_ON - MIDI_EVENT_NOTE_OFF]++;
#endif
			}
			else
			{
				Note_Applied = false;
			}
		}
		else if((midi_event == MIDI_EVENT_NOTE_OFF && _EEPROM_Content.Channel_MIDI_Configuration[ch].Skip_Note_Off_Event == 0))
		{
			if(midi_note_in_octave == NOTE_COLOR_RED || midi_note_in_octave == NOTE_COLOR_RED_ALT)
			{
				if(_NoteOn_Color_Counter[ch][R] > 0) {
                    _NoteOn_Color_Counter[ch][R]--;
                }

				if(_NoteOn_Color_Counter[ch][R] == 0) { Core1_LED_Control_Set_DC_Direct(ch, R, 0); }

#ifdef COUNT_APPLIED_NOTES
                _Event_Received_Counter[ch][R][MIDI_EVENT_NOTE_OFF - MIDI_EVENT_NOTE_OFF]++;
#endif
			}
			else if(midi_note_in_octave == NOTE_COLOR_GREEN || midi_note_in_octave == NOTE_COLOR_GREEN_ALT)
			{
				if(_NoteOn_Color_Counter[ch][G] > 0) {
                    _NoteOn_Color_Counter[ch][G]--;
                }
                
				if(_NoteOn_Color_Counter[ch][G] == 0) { Core1_LED_Control_Set_DC_Direct(ch, G, 0); }

#ifdef COUNT_APPLIED_NOTES
                _Event_Received_Counter[ch][G][MIDI_EVENT_NOTE_OFF - MIDI_EVENT_NOTE_OFF]++;
#endif
			}
			else if(midi_note_in_octave == NOTE_COLOR_BLUE || midi_note_in_octave == NOTE_COLOR_BLUE_ALT)
			{
				if(_NoteOn_Color_Counter[ch][B] > 0) {
                    _NoteOn_Color_Counter[ch][B]--;
                }

				if(_NoteOn_Color_Counter[ch][B] == 0) { Core1_LED_Control_Set_DC_Direct(ch, B, 0); }

#ifdef COUNT_APPLIED_NOTES
                _Event_Received_Counter[ch][B][MIDI_EVENT_NOTE_OFF - MIDI_EVENT_NOTE_OFF]++;
#endif
			}
			else
			{
				Note_Applied = false;
			}
		}
		else
		{
			Note_Applied = false;
		}

		if(Note_Applied == true) {
			_Info_Last_Applied_Note[ch].Event 			= midi_event;
			_Info_Last_Applied_Note[ch].Note 			= midi_note;
			_Info_Last_Applied_Note[ch].Note_In_Octave	= midi_note_in_octave;
			_Info_Last_Applied_Note[ch].Velocity 		= value;
			_Info_Last_Applied_Note[ch].Timestamp_ms 	= to_ms_since_boot(get_absolute_time());
		}
	}
}

void Core1_Light_Controller_MIDI_Full_CC_Received(uint8_t midi_event, uint8_t midi_channel, uint8_t controller_number, uint8_t controller_value)
{
    Core1_Light_Controller_Pause_Light_Trigger(midi_event, midi_channel);
	
	for(uint ch=0;ch<NUM_LED_CHANNELS;ch++)
	{		
		if(Core1_Light_Controller_Check_Channel_Match(ch, midi_channel) != true) {
			continue;
		}

        if(midi_event == MIDI_EVENT_CONTROL_CHANGE) {
            switch(controller_number) {
                case MIDI_CC_ALL_NOTES_OFF:
                    _NoteOn_Color_Counter[ch][R] = 0;
                    _NoteOn_Color_Counter[ch][G] = 0;
                    _NoteOn_Color_Counter[ch][B] = 0;

                    Core1_LED_Control_Set_DC_Direct(ch, R, 0);
                    Core1_LED_Control_Set_DC_Direct(ch, G, 0);
                    Core1_LED_Control_Set_DC_Direct(ch, B, 0);
                    break;

                default:
                    break;
            }
        }
    }
}

void Core1_Light_Controller_Set_MIDI_To_Light_Enabled(bool enabled)
{
	_MIDI_To_Light_Enabled = enabled;

	for(uint ch=0;ch<NUM_LED_CHANNELS;ch++) {
		_Pause_Light_Timer[ch].Timer 		= 0;
		_Pause_Light_Timer[ch].Is_Active 	= false;
	}
}


/*******************************************************************
	Internal Functions
*******************************************************************/
bool Core1_Light_Controller_Check_Channel_Match(enum LED_Channel channel, uint8_t midi_channel)
{
	if(channel >= NUM_LED_CHANNELS) {
		return false;
	}

	return (midi_channel == _EEPROM_Content.Channel_MIDI_Configuration[channel].MIDI_Channel);
}

bool Core1_Light_Controller_Check_Octave_Match(enum LED_Channel channel, uint midi_note)
{
	if(channel >= NUM_LED_CHANNELS) {
		return false;
	}
	
	if(midi_note >= MIDI_NOTE_LIST_LENGTH) { 
		return false; 
	}

	return (_MIDI_Note_List[midi_note].Octave == _EEPROM_Content.Channel_MIDI_Configuration[channel].Octave);
}

void Core1_Light_Controller_Pause_Light_Trigger(uint8_t midi_event, uint8_t midi_channel)
{
	for(uint ch=0;ch<NUM_LED_CHANNELS;ch++)
	{
		bool Match_Success = false;
		
		switch(_EEPROM_Content.Pause_Light_Configuration[ch].Reset_Condition)
		{
			case CHANNEL_MATCH:
				if(midi_channel == _EEPROM_Content.Channel_MIDI_Configuration[ch].MIDI_Channel) {
					Match_Success = true;
				}
				break;

			case EVENT_MATCH:
				if(midi_event == MIDI_EVENT_NOTE_ON || midi_event == MIDI_EVENT_NOTE_OFF) {
					Match_Success = true;
				}
				break;

			case CHANNEL_AND_EVENT_MATCH:
				if(	(midi_channel == _EEPROM_Content.Channel_MIDI_Configuration[ch].MIDI_Channel) &&
					(midi_event == MIDI_EVENT_NOTE_ON || midi_event == MIDI_EVENT_NOTE_OFF))
				{
					Match_Success = true;
				}
				break;

			case ANY_TRAFFIC:
			default:
				Match_Success = true;
				break;
		}

		if(Match_Success) {
			if(_Pause_Light_Timer[ch].Is_Active == true)
			{
				_Pause_Light_Timer[ch].Is_Active = false;

				Core1_LED_Control_Set_DC_Direct(ch, R, 0);
				Core1_LED_Control_Set_DC_Direct(ch, G, 0);
				Core1_LED_Control_Set_DC_Direct(ch, B, 0);

				Core1_LED_Control_Set_DC_Target(ch, R, 0);
				Core1_LED_Control_Set_DC_Target(ch, G, 0);
				Core1_LED_Control_Set_DC_Target(ch, B, 0);
			}

			_Pause_Light_Timer[ch].Timer = 0;
			Core1_LED_Control_Set_Fade_Speed(ch, 0);
		}
	}
}

void Core1_Light_Controller_Reset_NoteOn_Counter()
{
	for(uint ch=0;ch<NUM_LED_CHANNELS;ch++)
	{
		for(uint l=0;l<NUM_LED_COLORS;l++)
		{
			if(_NoteOn_Color_Counter[ch][l] > 0) {
				Core1_LED_Control_Set_DC_Direct(ch, l, 0);
			}

			_NoteOn_Color_Counter[ch][l] = 0;
		}
	}
}

Note_t Core1_Light_Controller_Get_Octave_Note_For_Channel(enum LED_Channel channel)
{
	if(channel >= NUM_LED_CHANNELS) {
		return NO_NOTE;
	}

	return _Info_Last_Applied_Note[channel].Note_In_Octave;
}

Value_t Core1_Light_Controller_Get_Note_Value_For_Channel(enum LED_Channel channel)
{
	if(channel >= NUM_LED_CHANNELS) {
		return 0;
	}

	return _Info_Last_Applied_Note[channel].Velocity;
}