// intellectual property is bullshit bgdc

#ifndef MIDIX_HPP
#define MIDIX_HPP

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

#include <etl/queue_spsc_locked.h>
#include <avrstuff/serial.hpp>
#include <avrstuff/ringbuf.hpp>
#include "midi.hpp"

#define MIDIX_SYSEX_LEN 64
#define MIDIX_SYSEX_OVERHEAD 2
// Must at least equal LEN + OVERHEAD, should be a power of 2
#define MIDIX_SYSEX_OUT_LEN 128

typedef iringbuf<struct midi_message> midi_iqueue;

// MIDI transceiver. MIDI messages are received from the RXC ISR and placed
// into a queue. Outgoing MIDI messages are transmitted from an internal
// queue.
class midix {
public:
	midix(serial & serport, midi_iqueue & mq)
		: m_serport(serport)
		, m_sysex_buf_avail(true)
		, m_message_pos(0)
		, m_receiving_sysex(false)
		, m_queue(mq)
	{
		m_message.source  = this;
		m_message.command = MIDI_ICMD_NOP;
	}

	// Receive Complete interrupt. Call within the serial port's RXC ISR.
	void rxc();

	// Data Register Empty interrupt. Call within the port's DRE ISR.
	void dre();

	// Transmit a raw message. Message is NUL terminated.
	void send(uint8_t const * msg);

	// Transmit a sysex message, adding the correct guard bytes. Message
	// is NUL terminated.
	void send_sysex(uint8_t const * msg);

	// Release the sysex buffer. When a message is enqueued containing
	// sysex content, it contains a reference to this midix's internal
	// sysex buffer. This can only be used once, so it must be released
	// when no longer needed. If another sysex is received while the buffer
	// is held, it is rejected and becomes a MIDI_ICMD_SYSEX_OVERFLOW.
	//
	// buf: buffer to release. Must be the original buffer given in
	//   midi_message.content
	void sysex_release_buffer(uint8_t * buf);

private:
	// Send the current message and initialize it for the next one
	void send()
	{
		m_queue.push(m_message);
		m_message_pos = 0;
		m_message.command = MIDI_ICMD_NOP;
		m_receiving_sysex = false;
	}

	serial & m_serport;
	uint8_t  m_sysex_buf[MIDIX_SYSEX_LEN];
	bool     m_sysex_buf_avail;

	struct midi_message m_message;
	size_t              m_message_pos;
	bool                m_receiving_sysex;
	midi_iqueue &       m_queue;

	ringbuf<uint8_t, 128> m_outbuf;
};

#endif // !defined(MIDIX_HPP)
