//  Copyright (c) 2001 Daniel C. Nuffer
//  Copyright (c) 2001-2011 Hartmut Kaiser
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#if !defined(BOOST_SPIRIT_ITERATOR_FIXED_SIZE_QUEUE_MAR_16_2007_1137AM)
#define BOOST_SPIRIT_ITERATOR_FIXED_SIZE_QUEUE_MAR_16_2007_1137AM

#include <cstdlib>
#include <iterator>
#include <cstddef>

#include <boost/config.hpp>
#include <boost/assert.hpp>   // for BOOST_ASSERT
#include <boost/iterator_adaptors.hpp>

///////////////////////////////////////////////////////////////////////////////
//  Make sure we're using a decent version of the Boost.IteratorAdaptors lib
#if !defined(BOOST_ITERATOR_ADAPTORS_VERSION) || \
    BOOST_ITERATOR_ADAPTORS_VERSION < 0x0200
#error "Please use at least Boost V1.31.0 while compiling the fixed_size_queue class!"
#endif

///////////////////////////////////////////////////////////////////////////////
#define BOOST_SPIRIT_ASSERT_FSQ_SIZE                                          \
    BOOST_ASSERT(((m_tail + N + 1) - m_head) % (N+1) == m_size % (N+1))       \
    /**/

///////////////////////////////////////////////////////////////////////////////
namespace boost {
namespace spirit {
namespace detail {
///////////////////////////////////////////////////////////////////////////
template <typename Queue, typename T, typename Pointer>
class fsq_iterator
    :   public boost::iterator_facade<
        fsq_iterator<Queue, T, Pointer>, T,
        std::random_access_iterator_tag
        >
{
  public:
    typedef typename Queue::position_type position_type;
    typedef boost::iterator_facade<
    fsq_iterator<Queue, T, Pointer>, T,
                 std::random_access_iterator_tag
                 > base_type;

    fsq_iterator() {}
    fsq_iterator(position_type const &p_) : p(p_) {}

    position_type &
    get_position()
    {
        return p;
    }
    position_type const &
    get_position() const
    {
        return p;
    }

  private:
    friend class boost::iterator_core_access;

    typename base_type::reference
    dereference() const
    {
        return p.self->m_queue[p.pos];
    }

    void
    increment()
    {
        ++p.pos;

        if (p.pos == Queue::MAX_SIZE+1)
        {
            p.pos = 0;
        }
    }

    void
    decrement()
    {
        if (p.pos == 0)
        {
            p.pos = Queue::MAX_SIZE;
        }

        else
        {
            --p.pos;
        }
    }

    bool
    is_eof() const
    {
        return p.self == 0 || p.pos == p.self->size();
    }

    template <typename Q, typename T_, typename P>
    bool
    equal(fsq_iterator<Q, T_, P> const &x) const
    {
        if (is_eof())
        {
            return x.is_eof();
        }

        if (x.is_eof())
        {
            return false;
        }

        position_type const &rhs_pos = x.get_position();
        return (p.self == rhs_pos.self) && (p.pos == rhs_pos.pos);
    }

    template <typename Q, typename T_, typename P>
    typename base_type::difference_type
    distance_to(
        fsq_iterator<Q, T_, P> const &x) const
    {
        typedef typename base_type::difference_type difference_type;

        position_type const &p2 = x.get_position();
        std::size_t pos1 = p.pos;
        std::size_t pos2 = p2.pos;

        //  Undefined behavior if the iterators come from different
        //  containers
        BOOST_ASSERT(p.self == p2.self);

        if (pos1 < p.self->m_head)
        {
            pos1 += Queue::MAX_SIZE;
        }

        if (pos2 < p2.self->m_head)
        {
            pos2 += Queue::MAX_SIZE;
        }

        if (pos2 > pos1)
        {
            return difference_type(pos2 - pos1);
        }

        else
        {
            return -difference_type(pos1 - pos2);
        }
    }

    void
    advance(typename base_type::difference_type n)
    {
        //  Notice that we don't care values of n that can
        //  wrap around more than one time, since it would
        //  be undefined behavior anyway (going outside
        //  the begin/end range). Negative wrapping is a bit
        //  cumbersome because we don't want to case p.pos
        //  to signed.
        if (n < 0)
        {
            n = -n;

            if (p.pos < (std::size_t)n)
            {
                p.pos = Queue::MAX_SIZE+1 - (n - p.pos);
            }

            else
            {
                p.pos -= n;
            }
        }

        else
        {
            p.pos += n;

            if (p.pos >= Queue::MAX_SIZE+1)
            {
                p.pos -= Queue::MAX_SIZE+1;
            }
        }
    }

  private:
    position_type p;
};

///////////////////////////////////////////////////////////////////////////
template <typename T, std::size_t N>
class fixed_size_queue
{
  private:
    struct position
    {
        fixed_size_queue* self;
        std::size_t pos;

        position() : self(0), pos(0) {}

        // The const_cast here is just to avoid to have two different
        //  position structures for the const and non-const case.
        // The const semantic is guaranteed by the iterator itself
        position(const fixed_size_queue* s, std::size_t p)
            : self(const_cast<fixed_size_queue*>(s)), pos(p)
        {}

        bool
        is_initialized() const
        {
            return self != 0;
        }
        void
        set_queue(fixed_size_queue* q)
        {
            self = q;
        }
    };

  public:
    // Declare the iterators
    typedef fsq_iterator<fixed_size_queue<T, N>, T, T*> iterator;
    typedef
    fsq_iterator<fixed_size_queue<T, N>, T const, T const*>
    const_iterator;
    typedef position position_type;

    friend class fsq_iterator<fixed_size_queue<T, N>, T, T*>;
    friend class fsq_iterator<fixed_size_queue<T, N>, T const, T const*>;

    fixed_size_queue();
    fixed_size_queue(const fixed_size_queue& x);
    fixed_size_queue&
    operator=(const fixed_size_queue& x);
    ~fixed_size_queue();

    void
    push_back(const T& e);
    void
    push_front(const T& e);
    void
    serve(T& e);
    void
    pop_front();

    bool
    empty() const
    {
        return m_size == 0;
    }

    bool
    full() const
    {
        return m_size == N;
    }

    iterator
    begin()
    {
        return iterator(position(this, m_head));
    }

    const_iterator
    begin() const
    {
        return const_iterator(position(this, m_head));
    }

    iterator
    end()
    {
        return iterator(position(0, m_tail));
    }

    const_iterator
    end() const
    {
        return const_iterator(position(0, m_tail));
    }

    std::size_t
    size() const
    {
        return m_size;
    }

    T&
    front()
    {
        return m_queue[m_head];
    }

    const T&
    front() const
    {
        return m_queue[m_head];
    }

  private:
    // Redefine the template parameters to avoid using partial template
    //  specialization on the iterator policy to extract N.
    BOOST_STATIC_CONSTANT(std::size_t, MAX_SIZE = N);

    std::size_t m_head;
    std::size_t m_tail;
    std::size_t m_size;
    T m_queue[N+1];
};

template <typename T, std::size_t N>
inline
fixed_size_queue<T, N>::fixed_size_queue()
    : m_head(0)
    , m_tail(0)
    , m_size(0)
{
    BOOST_ASSERT(m_size <= N+1);
    BOOST_SPIRIT_ASSERT_FSQ_SIZE;
    BOOST_ASSERT(m_head <= N+1);
    BOOST_ASSERT(m_tail <= N+1);
}

template <typename T, std::size_t N>
inline
fixed_size_queue<T, N>::fixed_size_queue(const fixed_size_queue& x)
    : m_head(x.m_head)
    , m_tail(x.m_tail)
    , m_size(x.m_size)
{
    copy(x.begin(), x.end(), begin());
    BOOST_ASSERT(m_size <= N+1);
    BOOST_SPIRIT_ASSERT_FSQ_SIZE;
    BOOST_ASSERT(m_head <= N+1);
    BOOST_ASSERT(m_tail <= N+1);
}

template <typename T, std::size_t N>
inline fixed_size_queue<T, N>&
fixed_size_queue<T, N>::operator=(const fixed_size_queue& x)
{
    if (this != &x)
    {
        m_head = x.m_head;
        m_tail = x.m_tail;
        m_size = x.m_size;
        copy(x.begin(), x.end(), begin());
    }

    BOOST_ASSERT(m_size <= N+1);
    BOOST_SPIRIT_ASSERT_FSQ_SIZE;
    BOOST_ASSERT(m_head <= N+1);
    BOOST_ASSERT(m_tail <= N+1);

    return *this;
}

template <typename T, std::size_t N>
inline
fixed_size_queue<T, N>::~fixed_size_queue()
{
    BOOST_ASSERT(m_size <= N+1);
    BOOST_SPIRIT_ASSERT_FSQ_SIZE;
    BOOST_ASSERT(m_head <= N+1);
    BOOST_ASSERT(m_tail <= N+1);
}

template <typename T, std::size_t N>
inline void
fixed_size_queue<T, N>::push_back(const T& e)
{
    BOOST_ASSERT(m_size <= N+1);
    BOOST_SPIRIT_ASSERT_FSQ_SIZE;
    BOOST_ASSERT(m_head <= N+1);
    BOOST_ASSERT(m_tail <= N+1);

    BOOST_ASSERT(!full());

    m_queue[m_tail] = e;
    ++m_size;
    ++m_tail;

    if (m_tail == N+1)
    {
        m_tail = 0;
    }

    BOOST_ASSERT(m_size <= N+1);
    BOOST_SPIRIT_ASSERT_FSQ_SIZE;
    BOOST_ASSERT(m_head <= N+1);
    BOOST_ASSERT(m_tail <= N+1);
}

template <typename T, std::size_t N>
inline void
fixed_size_queue<T, N>::push_front(const T& e)
{
    BOOST_ASSERT(m_size <= N+1);
    BOOST_SPIRIT_ASSERT_FSQ_SIZE;
    BOOST_ASSERT(m_head <= N+1);
    BOOST_ASSERT(m_tail <= N+1);

    BOOST_ASSERT(!full());

    if (m_head == 0)
    {
        m_head = N;
    }

    else
    {
        --m_head;
    }

    m_queue[m_head] = e;
    ++m_size;

    BOOST_ASSERT(m_size <= N+1);
    BOOST_SPIRIT_ASSERT_FSQ_SIZE;
    BOOST_ASSERT(m_head <= N+1);
    BOOST_ASSERT(m_tail <= N+1);
}

template <typename T, std::size_t N>
inline void
fixed_size_queue<T, N>::serve(T& e)
{
    BOOST_ASSERT(m_size <= N+1);
    BOOST_SPIRIT_ASSERT_FSQ_SIZE;
    BOOST_ASSERT(m_head <= N+1);
    BOOST_ASSERT(m_tail <= N+1);

    e = m_queue[m_head];
    pop_front();
}

template <typename T, std::size_t N>
inline void
fixed_size_queue<T, N>::pop_front()
{
    BOOST_ASSERT(m_size <= N+1);
    BOOST_SPIRIT_ASSERT_FSQ_SIZE;
    BOOST_ASSERT(m_head <= N+1);
    BOOST_ASSERT(m_tail <= N+1);

    ++m_head;

    if (m_head == N+1)
    {
        m_head = 0;
    }

    --m_size;

    BOOST_ASSERT(m_size <= N+1);
    BOOST_SPIRIT_ASSERT_FSQ_SIZE;
    BOOST_ASSERT(m_head <= N+1);
    BOOST_ASSERT(m_tail <= N+1);
}

}
}
}

#undef BOOST_SPIRIT_ASSERT_FSQ_SIZE

#endif
