
template<typename T>
Buffer<T>::Buffer(size_t capacity)
: m_capacity(capacity)
, m_len(0)
, m_wi(0)
, m_ri(0)
, m_bid(0)
, m_data{nullptr, nullptr}
{
	resize(capacity);
}

template<typename T>
Buffer<T>::~Buffer()
{
	resize(0);
}

template<typename T>
void Buffer<T>::resize(size_t size)
{
	const size_t N = sizeof(m_data)/sizeof(m_data[0]);
	for (int i=0; i < N; i++)
	{
		if (m_data[i] != nullptr)
		{
			delete m_data[i];
			m_data[i] = nullptr;
		}
	}
	m_capacity = size;
	clear();
	if (size == 0)
	{
		return;
	}

	for (int i=0; i < N; i++)
	{
		m_data[i] = new T[size];
	}

}

template<typename T>
void Buffer<T>::fill(T value)
{
	T *pData = data_w();

	for (m_wi=0; m_wi < capacity(); m_wi++)
	{
		pData[m_wi] = value;
		m_len++;
	}
}

template<typename T>
size_t Buffer<T>::len()
{
    return m_len;
}

template<typename T>
size_t Buffer<T>::free()
{
    return m_capacity-m_len;
}

template<typename T>
size_t Buffer<T>::capacity()
{
    return m_capacity;
}

template<typename T>
void Buffer<T>::clear()
{
    m_len = 0;
    m_wi = 0;
    m_ri = 0;
}

template<typename T>
T* Buffer<T>::data_w()
{
    return &bufferActive()[m_wi];
}

template<typename T>
T* Buffer<T>::data_r()
{
    return &bufferActive()[m_ri];
}

template<typename T>
typename Buffer<T>::Result Buffer<T>::write(const T *pData, size_t len)
{
	if (len > free())
	{
		RETURN_OR_EXCEPT(Result::Err_InvalidSize);
	}

	while(len)
	{
		size_t num_remain = m_capacity - m_wi;
		size_t actual_len = std::min(num_remain, len);

		memcpy(data_w(), pData, actual_len*sizeof(T));
		len -= actual_len;
		pData += actual_len;
		produce(actual_len);
	}
	return Result::Ok;
}

template<typename T>
typename Buffer<T>::Result Buffer<T>::read(T* pData, size_t len)
{
	if (len > m_len)
	{
		RETURN_OR_EXCEPT(Result::Err_InvalidSize);
	}

	while(len)
	{
		size_t num_remain = m_capacity - m_ri;
		size_t actual_len = std::min(num_remain, len);

		memcpy(pData, data_r(), actual_len*sizeof(T));
		len -= actual_len;
		pData += actual_len;
		consume(actual_len);
	}
	return Result::Ok;
}

template<typename T>
T Buffer<T>::readAt(size_t offset)
{
	if (m_len < (1+offset))
	{
		throw Buffer::Exception(Result::Err_InvalidSize);
	}
	size_t i = (m_ri + offset) % m_capacity;
	T result = bufferActive()[i];
	consume(1+offset);
	return result;
}

template<typename T>
typename Buffer<T>::Result Buffer<T>::consume(size_t len)
{
	if (len > m_len)
	{
		RETURN_OR_EXCEPT(Result::Err_InvalidSize);
	}
	m_len -= len;
	m_ri = (m_ri + len) % m_capacity;

	return Result::Ok;
}

template<typename T>
typename Buffer<T>::Result Buffer<T>::produce(size_t len)
{
	if (len > free())
	{
		RETURN_OR_EXCEPT(Result::Err_InvalidSize);
	}
	m_len += len;
	m_wi = (m_wi + len) % m_capacity;

	return Result::Ok;
}

template<typename T>
void Buffer<T>::print(const char *prefix)
{
	make_contigious();
	T *data = data_r();
	printf("%s", prefix);
	for (size_t i=0; i < m_len; i++)
	{
		printf("%f ", data[i]);
	}
	printf("\n");
}
