/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef INCLUDE_ECXX_CTL_RING_BUFFER_TPP
#error "this file cannot be included directly"
#endif
#undef INCLUDE_ECXX_CTL_RING_BUFFER_TPP


#ifndef ECXX_CTL_RING_BUFFER_TPP
#define ECXX_CTL_RING_BUFFER_TPP


//
// A simple ring buffer
//
template <typename ValueT, size_t MaxCapacity>
inline RingBuffer<ValueT, MaxCapacity>::RingBuffer() noexcept
: _rPos(0)
, _wPos(0)
{
    static_assert(
        eCxx::isPowerOfTwo<size_t>(MaxCapacity),
        "template argument substituting MaxCapacity is not a power of two"
    );

    static_assert(
        MaxCapacity <= eCxx::pow(2, sizeof(eCxx::uint8_t) * 8 - 1),
        "template argument substituting MaxCapacity is too large"
    );
}


template <typename ValueT, size_t MaxCapacity>
inline RingBuffer<ValueT, MaxCapacity>::~RingBuffer() noexcept
{}


template <typename ValueT, size_t MaxCapacity>
inline typename RingBuffer<ValueT, MaxCapacity>::SizeType RingBuffer<ValueT, MaxCapacity>::capacity() const noexcept
{ return Capacity; }


template <typename ValueT, size_t MaxCapacity>
inline typename RingBuffer<ValueT, MaxCapacity>::SizeType RingBuffer<ValueT, MaxCapacity>::size() const noexcept
{ return _wPos - _rPos; }


template <typename ValueT, size_t MaxCapacity>
inline bool RingBuffer<ValueT, MaxCapacity>::full() const noexcept
{ return size() == Capacity; }


template <typename ValueT, size_t MaxCapacity>
inline bool RingBuffer<ValueT, MaxCapacity>::empty() const noexcept
{ return _rPos == _wPos; }


template <typename ValueT, size_t MaxCapacity>
inline void RingBuffer<ValueT, MaxCapacity>::clear() noexcept
{ _rPos = _wPos = 0; }


template <typename ValueT, size_t MaxCapacity>
inline bool RingBuffer<ValueT, MaxCapacity>::put(const ValueType& value) noexcept
{
    if( full() ) return false;

    *_buffer( _wPos & (Capacity - 1) ) = value;
    ++_wPos;

    return true;
}


template <typename ValueT, size_t MaxCapacity>
inline bool RingBuffer<ValueT, MaxCapacity>::get(ValueType& value) noexcept
{
    if( empty() ) return false;

    value = *_buffer( _rPos & (Capacity - 1) );
    ++_rPos;

    return true;
}


template <typename ValueT, size_t MaxCapacity>
inline bool RingBuffer<ValueT, MaxCapacity>::peek(ValueType& value) const noexcept
{
    if( empty() ) return false;

    value = *_buffer( _rPos & (Capacity - 1) );

    return true;
}


template <typename ValueT, size_t MaxCapacity> template <uint8_t N>
inline bool RingBuffer<ValueT, MaxCapacity>::peekMatchFirstN(const ValueType (&ref)[N]) const noexcept
{
    if( size() < N ) return false;

    SizeType rPos = _rPos;

    for(SizeType i = 0; i < N; ++i) {
        // Compare the value
        if( *_buffer( rPos & (Capacity - 1) ) != ref[i] ) return false;
        // Increment the read position
        ++rPos;
    }

    return true;
}


template <typename ValueT, size_t MaxCapacity> template <uint8_t N>
inline bool RingBuffer<ValueT, MaxCapacity>::peekMatchLastN(const ValueType (&ref)[N]) const noexcept
{
    if( size() < N ) return false;

    SizeType rPos = (_wPos - N) & (Capacity - 1);

    for(SizeType i = 0; i < N; ++i) {
        // Compare the value
        if( *_buffer( rPos & (Capacity - 1) ) != ref[i] ) return false;
        // Increment the read position
        ++rPos;
    }

    return true;
}


template <typename ValueT, size_t MaxCapacity>
inline typename RingBuffer<ValueT, MaxCapacity>::ConstPointer RingBuffer<ValueT, MaxCapacity>::data() const noexcept
{ return _buffer(0); }


template <typename ValueT, size_t MaxCapacity>
inline typename RingBuffer<ValueT, MaxCapacity>::Pointer RingBuffer<ValueT, MaxCapacity>::_writeableData() noexcept
{ return _buffer(0); }


template <typename ValueT, size_t MaxCapacity>
inline void RingBuffer<ValueT, MaxCapacity>::_setWrittenDataSize(SizeType size) noexcept
{
    _rPos = 0;
    _wPos = size;
}


#endif
