//  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_POLICY_MAR_16_2007_1134AM)
#define BOOST_SPIRIT_ITERATOR_FIXED_SIZE_QUEUE_POLICY_MAR_16_2007_1134AM

#include <boost/spirit/home/support/iterators/detail/multi_pass.hpp>
#include <boost/spirit/home/support/iterators/detail/fixed_size_queue.hpp>
#include <boost/assert.hpp>
#include <cstdlib>

namespace boost {
namespace spirit {
namespace iterator_policies {
///////////////////////////////////////////////////////////////////////////
//  class fixed_size_queue
//  Implementation of the StoragePolicy used by multi_pass
//  fixed_size_queue keeps a circular buffer (implemented by
//  boost::spirit::fixed_size_queue class) that is size N+1 and stores N
//  elements.
//
//  It is up to the user to ensure that there is enough look ahead for
//  their grammar. Currently there is no way to tell if an iterator is
//  pointing to forgotten data. The leading iterator will put an item in
//  the queue and remove one when it is incremented. No dynamic allocation
//  is done, except on creation of the queue (fixed_size_queue constructor).
///////////////////////////////////////////////////////////////////////////
template <std::size_t N>
struct fixed_size_queue
{
    ///////////////////////////////////////////////////////////////////////
    template <typename Value>
  class unique : public detail::default_storage_policy
    {
        private:
        typedef detail::fixed_size_queue<Value, N> queue_type;

        protected:
        unique() {}

        unique(unique const& x)
            : queued_position(x.queued_position) {}

        void
        swap(unique& x)
        {
            boost::swap(queued_position, x.queued_position);
        }

        //  This is called when the iterator is dereferenced. It's a
        //  template method so we can recover the type of the multi_pass
        //  iterator and access the m_input data member.
        template <typename MultiPass>
        static typename MultiPass::reference
        dereference(MultiPass const& mp)
        {
            if (!mp.queued_position.get_position().is_initialized())
            {
                mp.queued_position.get_position().set_queue(&mp.shared()->queued_elements);
            }

            if (mp.queued_position == mp.shared()->queued_elements.end())
            {
                return MultiPass::get_input(mp);
            }

            return *mp.queued_position;
        }

        //  This is called when the iterator is incremented. It's a
        //  template method so we can recover the type of the multi_pass
        //  iterator and access the m_input data member.
        template <typename MultiPass>
        static void
        increment(MultiPass& mp)
        {
            if (!mp.queued_position.get_position().is_initialized())
            {
                mp.queued_position.get_position().set_queue(&mp.shared()->queued_elements);
            }

            if (mp.queued_position == mp.shared()->queued_elements.end())
            {
                // don't let the queue get larger than N
                if (mp.shared()->queued_elements.size() >= N)
                {
                    mp.shared()->queued_elements.pop_front();
                }

                mp.shared()->queued_elements.push_back(
                    MultiPass::get_input(mp));
                MultiPass::advance_input(mp);
            }

            ++mp.queued_position;
        }

        // clear_queue is a no-op

        // called to determine whether the iterator is an eof iterator
        template <typename MultiPass>
        static bool
        is_eof(MultiPass const& mp)
        {
            return mp.queued_position == mp.shared()->queued_elements.end() &&
                   MultiPass::input_at_eof(mp);
        }

        // called by operator==
        template <typename MultiPass>
        static bool
        equal_to(MultiPass const& mp, MultiPass const& x)
        {
            return mp.queued_position == x.queued_position;
        }

        // called by operator<
        template <typename MultiPass>
        static bool
        less_than(MultiPass const& mp, MultiPass const& x)
        {
            return mp.queued_position < x.queued_position;
        }

protected:
        mutable typename queue_type::iterator queued_position;
    };

    ///////////////////////////////////////////////////////////////////////
    template <typename Value>
    struct shared
    {
        typedef detail::fixed_size_queue<Value, N> queue_type;
        queue_type queued_elements;
    };
};

}
}
}

#endif
