// intellectual property is bullshit bgdc

#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
#include <assert.h>

#include <vector>
#include <etl/queue_spsc_locked.h>

#include <avrstuff/serial.hpp>
#include "midix.hpp"
#include "midi.hpp"
#include <avrstuff/ringbuf.hpp>

class serial_test: public serial
{
public:
	virtual int recv() override
	{
		uint8_t byte;
		if (m_data.pop(byte))
			return byte;
		else
			return EOF;
	}

	virtual void send(int b) override
	{
		(void) b;
	}

	// Put data into the internal buffer
	void put(uint8_t const * data, size_t len)
	{
		for (size_t i = 0; i < len; i++)
			assert(m_data.push(data[i]));
	}

private:
	ringbuf<uint8_t, 255> m_data;
};

void print_midi(struct midi_message * m)
{
	uint8_t cmd = MIDI_CMD(m->command);
	uint8_t chan = MIDI_CMD_CHAN(m->command);

	switch (cmd)
	{
	case MIDI_ICMD_NOP:            printf("I NOP            "); break;
	case MIDI_ICMD_SYSEX:          printf("I SYSEX          "); break;
	case MIDI_ICMD_SYSEX_OVERFLOW: printf("I SYSEX_OVERFLOW "); break;
	case MIDI_CMD_NOTE_OFF:        printf("NOTE_OFF     [%X]", chan); break;
	case MIDI_CMD_NOTE_ON:         printf("NOTE_ON      [%X]", chan); break;
	case MIDI_CMD_AFTERTOUCH:      printf("AFTERTOUCH   [%X]", chan); break;
	case MIDI_CMD_CC:              printf("CC           [%X]", chan); break;
	case MIDI_CMD_PATCH:           printf("PATCH        [%X]", chan); break;
	case MIDI_CMD_PRESSURE:        printf("PRESSURE     [%X]", chan); break;
	case MIDI_CMD_PITCHBEND:       printf("PITCHBEND    [%X]", chan); break;
	case MIDI_CMD_START_SYSEX:     printf("START_SYSEX      "); break;
	case MIDI_CMD_TUNE_REQ:        printf("TUNE_REQ         "); break;
	case MIDI_CMD_END_SYSEX:       printf("END_SYSEX        "); break;
	case MIDI_CMD_SYSRESET:        printf("SYSRESET         "); break;
	}

	if (cmd == MIDI_ICMD_SYSEX)
	{
		printf("%s", m->content);
	}
	else
	{
		for (int i = 0; i < midi_num_args(cmd); i++)
		{
			printf(" %02x", m->arguments[i]);
		}
	}
	printf("\n");
}

int main(int argc, char ** argv)
{
	if (argc == 1)
	{
		printf("test_midi HEX_BYTES...\n");
		printf("Parses MIDI data and prints out the message packets.\n");
		return 1;
	}

	serial_test serport;
	ringbuf<struct midi_message, 128> queue;
	midix m(serport, queue);

	for (int i = 0; i < argc; i++)
	{
		unsigned x = 0;
		sscanf(argv[i], "%X", &x);

		uint8_t b = x;
		serport.put(&b, 1);
		m.rxc();

		if (!queue.empty())
		{
			struct midi_message msg;
			queue.pop(msg);
			print_midi(&msg);
			if (msg.command == MIDI_ICMD_SYSEX)
				m.sysex_release_buffer(msg.content);
		}
	}

	return 0;
}
