//  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_SPLIT_DEQUE_POLICY_APR_06_2008_0138PM)
#define BOOST_SPIRIT_ITERATOR_SPLIT_DEQUE_POLICY_APR_06_2008_0138PM

#include <boost/spirit/home/support/iterators/multi_pass_fwd.hpp>
#include <boost/spirit/home/support/iterators/detail/multi_pass.hpp>
#include <boost/assert.hpp>
#include <vector>

namespace boost {
namespace spirit {
namespace iterator_policies {
///////////////////////////////////////////////////////////////////////////
//  class split_std_deque
//
//  Implementation of the StoragePolicy used by multi_pass
//  This stores all data in a std::vector (despite its name), and keeps an
//  offset to the current position. It stores all the data unless there is
//  only one iterator using the queue.
//
///////////////////////////////////////////////////////////////////////////
struct split_std_deque
{
    enum { threshold = 16 };

    ///////////////////////////////////////////////////////////////////////
    template <typename Value>
    class unique //: public detail::default_storage_policy
    {
        private:
        typedef std::vector<Value> queue_type;

        protected:
        unique() : queued_position(0) {}

        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 call advance_input and input_is_valid.
        template <typename MultiPass>
        static typename MultiPass::reference
        dereference(MultiPass const& mp)
        {
            queue_type& queue = mp.shared()->queued_elements;
            typename queue_type::size_type size = queue.size();

            BOOST_ASSERT(mp.queued_position <= size);

            if (mp.queued_position == size)
            {
                // check if this is the only iterator
                if (size >= threshold && MultiPass::is_unique(mp))
                {
                    // free up the memory used by the queue.
                    queue.clear();
                    mp.queued_position = 0;
                }

                return MultiPass::get_input(mp);
            }

            return queue[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 call is_unique and advance_input.
        template <typename MultiPass>
        static void
        increment(MultiPass& mp)
        {
            queue_type& queue = mp.shared()->queued_elements;
            typename queue_type::size_type size = queue.size();

            BOOST_ASSERT(mp.queued_position <= size);

            //                 // do not increment iterator as long as the current token is
            //                 // invalid
            //                 if (size > 0 && !MultiPass::input_is_valid(mp, queue[mp.queued_position-1]))
            //                     return;

            if (mp.queued_position == size)
            {
                // check if this is the only iterator
                if (size >= threshold && MultiPass::is_unique(mp))
                {
                    // free up the memory used by the queue. we avoid
                    // clearing the queue on every increment, though,
                    // because this would be too time consuming
                    queue.clear();
                    mp.queued_position = 0;
                }

                else
                {
                    queue.push_back(MultiPass::get_input(mp));
                    ++mp.queued_position;
                }

                MultiPass::advance_input(mp);
            }

            else
            {
                ++mp.queued_position;
            }
        }

        // called to forcibly clear the queue
        template <typename MultiPass>
        static void
        clear_queue(MultiPass& mp)
        {
            mp.shared()->queued_elements.clear();
            mp.queued_position = 0;
        }

        // 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.size()
                   && 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;
        }

        template <typename MultiPass>
        static void
        destroy(MultiPass&) {}

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

    ///////////////////////////////////////////////////////////////////////
    template <typename Value>
    struct shared
    {
        shared()
        {
            queued_elements.reserve(threshold);
        }

        typedef std::vector<Value> queue_type;
        queue_type queued_elements;
    };

}; // split_std_deque

}
}
}

#endif
