/*=============================================================================
    multi_pass.hpp

    Spirit V1.2
    URL: http://spirit.sourceforge.net/

    Copyright (c) 2001, Daniel C. Nuffer

    This software is provided 'as-is', without any express or implied
    warranty. In no event will the copyright holder be held liable for
    any damages arising from the use of this software.

    Permission is granted to anyone to use this software for any purpose,
    including commercial applications, and to alter it and redistribute
    it freely, subject to the following restrictions:

    1.  The origin of this software must not be misrepresented; you must
        not claim that you wrote the original software. If you use this
        software in a product, an acknowledgment in the product documentation
        would be appreciated but is not required.

    2.  Altered source versions must be plainly marked as such, and must
        not be misrepresented as being the original software.

    3.  This notice may not be removed or altered from any source
        distribution.
=============================================================================*/
#ifndef BOOST_MULTI_PASS
#define BOOST_MULTI_PASS

#include <deque>
#include <iterator>

#include <iostream>

namespace boost
{

// thrown if an instance of an iterator is used after another one has
// invalidated the queue
class illegal_backtracking
{
};


template <typename InputIteratorT>
class multi_pass
{
    public:
        typedef std::forward_iterator_tag iterator_category;
        typedef typename std::iterator_traits<InputIteratorT>::value_type value_type;
        typedef typename std::iterator_traits<InputIteratorT>::difference_type difference_type;
        typedef typename std::iterator_traits<InputIteratorT>::pointer pointer;
        typedef typename std::iterator_traits<InputIteratorT>::reference reference;

        typedef InputIteratorT iterator_type;

        explicit multi_pass(InputIteratorT input);
        
        ~multi_pass();

        multi_pass(multi_pass const&);
        multi_pass& operator=(multi_pass const&);

        
        multi_pass<InputIteratorT>::value_type operator*() const;
        pointer operator->() const;
        multi_pass& operator++();
        multi_pass operator++(int);

        void clear_queue();

        bool operator==(const multi_pass& y) const;

        bool operator<(const multi_pass& y) const;

    private: // data
        InputIteratorT* m_input;
        unsigned long* m_refCount;
        unsigned long* m_sharedBufId;
        unsigned long m_bufId;

        typedef typename std::deque<typename std::iterator_traits<InputIteratorT>::value_type> queue_type;
        queue_type * m_queuedElements;
        mutable typename queue_type::size_type m_queuePosition;

    private: // helper functions
        void dispose();
        void share(multi_pass const& x);
        void check_buf_id() const;
        bool is_eof() const;
};

template <typename InputIteratorT>
inline 
multi_pass<InputIteratorT>::multi_pass(InputIteratorT input)
    : m_input(new InputIteratorT(input))
    , m_refCount(new unsigned long(1))
    , m_sharedBufId(new unsigned long(0))
    , m_bufId(0)
    , m_queuedElements(new queue_type)
    , m_queuePosition(0)
{
}

template <typename InputIteratorT>
inline 
multi_pass<InputIteratorT>::~multi_pass()
{
    dispose();
}

template <typename InputIteratorT>
inline 
multi_pass<InputIteratorT>::multi_pass(
        multi_pass const& x)
    : m_input(x.m_input)
    , m_sharedBufId(x.m_sharedBufId)
    , m_bufId(x.m_bufId)
    , m_queuedElements(x.m_queuedElements)
    , m_queuePosition(x.m_queuePosition)
{
    ++*(m_refCount = x.m_refCount);
}

template <typename InputIteratorT>
inline multi_pass<InputIteratorT>&
multi_pass<InputIteratorT>::operator=(
        multi_pass const& x)
{
    share(x);
    return *this;
}

template <typename InputIteratorT>
inline typename multi_pass<InputIteratorT>::value_type
multi_pass<InputIteratorT>::operator*() const
{
    check_buf_id();
    if (m_queuePosition == m_queuedElements->size())
    {
        // check if this is the only iterator
        if (*m_refCount == 1)
        {
            // free up the memory used by the queue.
            if (m_queuedElements->size() > 0)
            {
                m_queuedElements->clear();
                m_queuePosition = 0;
            }
        }
        return **m_input;
    }
    else
    {
        return (*m_queuedElements)[m_queuePosition];
    }
}

template <typename InputIteratorT>
inline typename multi_pass<InputIteratorT>::pointer
multi_pass<InputIteratorT>::operator->() const
{
    return &(operator*());
}

template <typename InputIteratorT>
inline multi_pass<InputIteratorT>&
multi_pass<InputIteratorT>::operator++()
{
    check_buf_id();
    if (m_queuePosition == m_queuedElements->size())
    {
        // check if this is the only iterator
        if (*m_refCount == 1)
        {
            // free up the memory used by the queue.
            if (m_queuedElements->size() > 0)
            {
                m_queuedElements->clear();
                m_queuePosition = 0;
            }
        }
        else
        {
            m_queuedElements->push_back(**m_input);
            ++m_queuePosition;
        }
        ++*m_input;
    }
    else
    {
        ++m_queuePosition;
    }
    return *this;
}

template <typename InputIteratorT>
inline multi_pass<InputIteratorT>
multi_pass<InputIteratorT>::operator++(int)
{
    multi_pass<InputIteratorT> tmp = *this;

    ++*this;

    return tmp;
}

template <typename InputIteratorT>
inline void
multi_pass<InputIteratorT>::dispose()
{
    if (--*m_refCount == 0) 
    { 
        delete m_refCount; 
        m_refCount = 0;
        delete m_queuedElements; 
        m_queuedElements = 0;
        delete m_sharedBufId;
        m_sharedBufId = 0;
        delete m_input;
        m_input = 0;
        m_bufId = 0;
        m_queuePosition = 0;
    }
}

template <typename InputIteratorT>
inline void
multi_pass<InputIteratorT>::share(
        multi_pass const& x)
{
    if (this != &x) 
    {
        ++*x.m_refCount;
        dispose();
        m_queuedElements = x.m_queuedElements;
        m_refCount = x.m_refCount;
        m_sharedBufId = x.m_sharedBufId;
        m_bufId = x.m_bufId;
    }
    m_queuePosition = x.m_queuePosition;
    m_input = x.m_input;
}

template <typename InputIteratorT>
inline void 
multi_pass<InputIteratorT>::clear_queue()
{
    m_queuePosition = 0;
    m_queuedElements->clear();
    ++*m_sharedBufId;
    ++m_bufId;
}

template <typename InputIteratorT>
inline void 
multi_pass<InputIteratorT>::check_buf_id() const
{
    if (m_bufId != *m_sharedBufId)
    {
        throw illegal_backtracking();
    }
}

template <typename InputIteratorT>
inline bool 
multi_pass<InputIteratorT>::is_eof() const
{
    return m_queuePosition >= m_queuedElements->size() && *m_input == InputIteratorT();
}

///// Comparisons
template <typename InputIteratorT>
inline bool 
multi_pass<InputIteratorT>::operator==(
        const multi_pass<InputIteratorT>& y) const
{
    if (is_eof() && y.is_eof())
    {
        return true;  // both are EOF
    }
    else if (is_eof() ^ y.is_eof())
    {
        return false; // one is EOF, one isn't
    }
    else if (m_input != y.m_input)
    {
        return false;
    }
    else
    {
        return m_queuePosition == y.m_queuePosition;
    }
}

template <typename InputIteratorT>
inline bool 
multi_pass<InputIteratorT>::operator<(
        const multi_pass<InputIteratorT>& y) const
{
    return m_queuePosition < y.m_queuePosition;
}

template <typename InputIteratorT>
inline
bool operator!=(
        const multi_pass<InputIteratorT>& x,
        const multi_pass<InputIteratorT>& y)
{
    return !(x == y);
}

template <typename InputIteratorT>
inline
bool operator>(
        const multi_pass<InputIteratorT>& x,
        const multi_pass<InputIteratorT>& y)
{
    return y < x;
}

template <typename InputIteratorT>
inline
bool operator>=(
        const multi_pass<InputIteratorT>& x,
        const multi_pass<InputIteratorT>& y)
{
    return !(x < y);
}

template <typename InputIteratorT>
inline
bool operator<=(
        const multi_pass<InputIteratorT>& x,
        const multi_pass<InputIteratorT>& y)
{
    return !(y < x);
}



///// Generator function
template <typename InputIteratorT>
inline multi_pass<InputIteratorT>
make_multi_pass(InputIteratorT i)
{
    return multi_pass<InputIteratorT>(i);
}

} // namespace boost

#endif

