/*
 * Core1_MIDI_Receiver.c
 *
 * Created: Thu Jan 13 2022 15:35:28
 *  Author Chris
 */
// ============================================================================================
// Includes
#include "Core1_MIDI_Receiver.h"

#include "UART0.h"
#include "Command_Definition.h"

#include "pico/time.h"


// ============================================================================================
// Defines
#define NOTE_UNDEFINED		255


// ============================================================================================
// Variables
enum PARSING_STATE_e
{
	WAITING_FOR_COMMAND,
	WAITING_FOR_NOTE,
	WAITING_FOR_VALUE
};

struct PARSING_s
{
	enum PARSING_STATE_e State;
	uint8_t MIDI_Event;
	uint8_t MIDI_Channel;
	uint8_t MIDI_Note;
};

volatile struct PARSING_s _Parsing;

History_Buffer_t _MIDI_History_Buffer[RECEIVED_MIDI_HISTORY_BUFFER_SIZE];


// ============================================================================================
// Function Declarations
void Core1_MIDI_Receiver_Histroy_Update				(uint8_t data);
void Core1_MIDI_Receiver_Issue_Event_On_Off_Received(uint8_t midi_channel, uint8_t midi_event);
void Core1_MIDI_Receiver_Issue_Data_Received		(uint8_t midi_data);
void Core1_MIDI_Receiver_Issue_Full_Note_Received	(uint8_t midi_event, uint8_t midi_channel, uint8_t midi_note, uint8_t value);


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


/*******************************************************************
	Functions
*******************************************************************/
void Core1_MIDI_Receiver_Init(void)
{
	UART0_Init();

	for(uint i=0;i<RECEIVED_MIDI_HISTORY_BUFFER_SIZE;i++)
	{
		_MIDI_History_Buffer[i].Data 			= HISTORY_ENTRY_UNDEFINED;
		_MIDI_History_Buffer[i].Timestamp_ms 	= 0;
	}

	_Parsing.State 			= WAITING_FOR_COMMAND;
	_Parsing.MIDI_Event 	= MIDI_EVENT_NOTE_ON;
	_Parsing.MIDI_Channel 	= MIDI_CHANNEL_1;
	_Parsing.MIDI_Note 		= NOTE_UNDEFINED;
}

void Core1_MIDI_Receiver_Process(void)
{
	while(UART0_Data_Available() > 0)
	{
		uint8_t Data = UART0_Get_Byte();
		Core1_MIDI_Receiver_Histroy_Update(Data);

		switch(_Parsing.State)
		{
			case WAITING_FOR_COMMAND:
				if(MIDI_EVENT_FROM_COMMAND(Data) == MIDI_EVENT_NOTE_ON || MIDI_EVENT_FROM_COMMAND(Data) == MIDI_EVENT_NOTE_OFF)
				{
					_Parsing.MIDI_Event 	= MIDI_EVENT_FROM_COMMAND(Data);
					_Parsing.MIDI_Channel 	= MIDI_CHANNEL_FROM_COMMAND(Data);
					_Parsing.State 			= WAITING_FOR_NOTE;
					
					Core1_MIDI_Receiver_Issue_Event_On_Off_Received(_Parsing.MIDI_Channel, _Parsing.MIDI_Event);
					break;
				}
				
				Core1_MIDI_Receiver_Issue_Data_Received(Data);
				break;

			case WAITING_FOR_NOTE:
				if(IS_MIDI_DATA(Data))
				{
					_Parsing.MIDI_Note 	= Data; 
					_Parsing.State 		= WAITING_FOR_VALUE;
				}
				else
				{
					// Received another command after just receiving a command. This should not happen, but
					// in case it does, reset the parsing state machine
					_Parsing.State = WAITING_FOR_COMMAND;
				}
				break;

			case WAITING_FOR_VALUE:
				if(IS_MIDI_DATA(Data))
				{
					Core1_MIDI_Receiver_Issue_Full_Note_Received(_Parsing.MIDI_Event, _Parsing.MIDI_Channel, _Parsing.MIDI_Note, Data);
				}

				// Reset State Machine and wait for next MIDI Command
				_Parsing.State = WAITING_FOR_COMMAND;
				break;

			default:
				break;
		}
	}
}


/*******************************************************************
	Internal Functions
*******************************************************************/
void Core1_MIDI_Receiver_Histroy_Update(uint8_t data)
{
	for(uint i=RECEIVED_MIDI_HISTORY_BUFFER_SIZE-1;i>0;i--)
	{
		_MIDI_History_Buffer[i] = _MIDI_History_Buffer[i-1];
	}

	_MIDI_History_Buffer[0].Data 			= data;
	_MIDI_History_Buffer[0].Timestamp_ms 	= to_ms_since_boot(get_absolute_time());
}

void Core1_MIDI_Receiver_Issue_Event_On_Off_Received(uint8_t midi_channel, uint8_t midi_event)
{
	Core1_Light_Controller_MIDI_OnOff_Event_Received(midi_event, midi_channel);
}

void Core1_MIDI_Receiver_Issue_Data_Received(uint8_t midi_data)
{
	Core1_Light_Controller_MIDI_Other_Event_Received(midi_data);
}

void Core1_MIDI_Receiver_Issue_Full_Note_Received(uint8_t midi_event, uint8_t midi_channel, uint8_t midi_note, uint8_t value)
{
	Core1_Light_Controller_MIDI_Full_Note_Received(midi_event, midi_channel, midi_note, value);
}