#ifndef __PROCESSOR_BUFFER_HPP__
#define __PROCESSOR_BUFFER_HPP__

#include <stddef.h>
#include <string.h>
#include <exception>
#include <algorithm>

#ifdef PROCESSOR_BUFFER_USE_EXCEPTIONS
#define RETURN_OR_EXCEPT(code) throw Buffer::Exception(code)
#else
#define RETURN_OR_EXCEPT(code) return code
#endif

namespace Processor
{
class IBuffer
{
	public:
	virtual ~IBuffer() = default;	
};

template <typename T>
class Buffer : public IBuffer
{
	public:
	enum Result
	{
		Ok=0,
		Err_InvalidSize=1
	};

	class Exception : public std::exception
	{
		public:
		Exception(Result code)
		: std::exception()
		, m_code(code)
		{

		}
		const char* what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW
		{
			static std::string r = "Return code = " + std::to_string(m_code);
			return r.c_str();
		}
		Result m_code;

	};
	Buffer(size_t capacity=0);
	~Buffer();
	Buffer(const Buffer<T> &rhs) = delete;

	void resize(size_t size);
	void fill(T value);
	size_t len();
	size_t free();
	size_t capacity();
	void clear();

	T* data_w();
	T* data_r();

	Result write(const T* pData, size_t len);
	Result read(T* pData, size_t len);
	T readAt(size_t offset);
	Result consume(size_t len);
	Result produce(size_t len);
	void print(const char *prefix);
	void make_contigious()
	{
		if (m_len == 0)
		{
			return;
		}
		size_t cont_len = std::min(m_capacity - m_ri, m_len);
		if (cont_len < m_len)
		{
			m_wi = m_len;
			read(bufferStdBy(), m_len);
			m_ri = 0;
			m_len = m_wi;
			m_bid = !m_bid & 1;
		}
	}
	T *bufferActive()
	{
		return m_data[m_bid];
	}

	private:
	size_t m_capacity;
	size_t m_len;
	size_t m_wi;
	size_t m_ri;
	size_t m_bid;
	T *m_data[2];

	T *bufferStdBy()
	{
		return m_data[!m_bid & 1];
	}
};

#include "Buffer.tcc"
};

#endif // __PROCESSOR_BUFFER_HPP__