// intellectual property is bullshit bgdc

#ifndef BUFSERIAL_HPP
#define BUFSERIAL_HPP

#include <stdbool.h>
#include <stddef.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avrstuff/serial.hpp>
#include <avrstuff/ringbuf.hpp>

// Buffered interrupt-driven wrapper around a serial port. Transmit and receive
// are both optional - specify their buffer sizes as zero to omit.
template<unsigned Txsize, unsigned Rxsize>
class bufserial: public serial
{
public:
	virtual ~bufserial() = default;

	bufserial(serial & sp)
		: m_serial(&sp)
		, m_error(0)
	{
	}

	void init()
	{
		m_serial->rxcie(true);
	}

	// Receive Complete interrupt handler. Must be called from the relevant
	// vector if receive buffering is used.
	void rxc()
	{
		int c = m_serial->recv();

		if (c < 0)
			m_error = c;
		else if (!m_rxbuf.push(c))
			m_error = serial::BUF_OVERFLOW;
	}

	// Data Register Empty interrupt handler. Must be called from the
	// relevant vector if transmit buffering is used.
	void dre()
	{
		uint8_t c;
		bool has_c = m_txbuf.pop(c);

		if (has_c)
			m_serial->send(c);
		else
			m_serial->dreie(false);
	}

	virtual int recv() override
	{
		uint8_t c;

		if (m_error)
		{
			int e = m_error;
			m_error = 0;
			return e;
		}
		else if (m_rxbuf.pop(c))
			return c;
		else
			return EOF;
	}

	virtual void send(int b) override
	{
		while (!m_txbuf.push(b));
		m_serial->dreie(true);
	}

private:
	serial * m_serial;
	int      m_error;
	ringbuf<uint8_t, Txsize> m_txbuf;
	ringbuf<uint8_t, Rxsize> m_rxbuf;
};

#endif // !defined(BUFSERIAL_HPP)
