/*=============================================================================
    Copyright (c) 2002 Juan Carlos Arevalo-Baeza
    Copyright (c) 2002-2003 Hartmut Kaiser
    Copyright (c) 2003 Giovanni Bajo
    http://spirit.sourceforge.net/

    Use, modification and distribution is subject to 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)
=============================================================================*/
#ifndef BOOST_SPIRIT_POSITION_ITERATOR_HPP
#define BOOST_SPIRIT_POSITION_ITERATOR_HPP

#include <string>
#include <boost/config.hpp>
#include <boost/concept_check.hpp>

namespace boost { namespace spirit {

///////////////////////////////////////////////////////////////////////////////
//
//  file_position_without_column
//
//  A structure to hold positional information. This includes the file,
//  and the line number
//
///////////////////////////////////////////////////////////////////////////////
struct file_position_without_column {
    std::string file;
    int line;

    file_position_without_column(std::string const& file_ = std::string(),
                  int line_ = 1):
        file    (file_),
        line    (line_)
    {}

    bool operator==(const file_position_without_column& fp) const
    { return line == fp.line && file == fp.file; }
};

///////////////////////////////////////////////////////////////////////////////
//
//  file_position
//
//  This structure holds complete file position, including file name,
//  line and column number
//
///////////////////////////////////////////////////////////////////////////////
struct file_position : public file_position_without_column {
    int column;

    file_position(std::string const& file_ = std::string(),
                  int line_ = 1, int column_ = 1):
        file_position_without_column (file_, line_),
        column                       (column_)
    {}

    bool operator==(const file_position& fp) const
    { return column == fp.column && line == fp.line && file == fp.file; }
};


///////////////////////////////////////////////////////////////////////////////
//
//  position_policy<>
//
//  This template is the policy to handle the file position. It is specialized
//  on the position type. Providing a custom file_position also requires
//  providing a specialization of this class.
//
//  Policy interface:
//
//    Default constructor of the custom position class must be accessible.
//    set_tab_chars(unsigned int chars) - Set the tabstop width
//    next_char(PositionT& pos)  - Notify that a new character has been
//      processed
//    tabulation(PositionT& pos) - Notify that a tab character has been
//      processed
//    next_line(PositionT& pos)  - Notify that a new line delimiter has
//      been reached.
//
///////////////////////////////////////////////////////////////////////////////
template <typename PositionT>
class position_policy;


// Forward declaration
template <typename ForwardIteratorT, typename PositionT, typename SelfT>
class position_iterator;

///////////////////////////////////////////////////////////////////////////////
}} /* namespace boost::spirit */


// This must be included here for full compatibility with old MSVC
#include "impl/position_iterator.ipp"

///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit {

///////////////////////////////////////////////////////////////////////////////
//
//  position_iterator
//
//  It wraps an iterator, and keeps track of the current position in the input,
//  as it gets incremented.
//
//  The wrapped iterator must be at least a Forward iterator. The position
//  iterator itself will always be a non-mutable Forward iterator.
//
//  In order to have begin/end iterators constructed, the end iterator must be
//  empty constructed. Similar to what happens with stream iterators. The begin
//  iterator must be constructed from both, the begin and end iterators of the
//  wrapped iterator type. This is necessary to implement the lookahead of
//  characters necessary to parse CRLF sequences.
//
//  In order to extract the current positional data from the iterator, you may
//  use the get_position member function.
//
//  You can also use the set_position member function to reset the current
//  position to something new.
//
//  The structure that holds the current position can be customized through a
//  template parameter, and the class position_policy must be specialized
//  on the new type to define how to handle it. Currently, it's possible
//  to choose between the file_position and file_position_without_column
//  (which saves some overhead if managing current column is not required).
//
///////////////////////////////////////////////////////////////////////////////

#if !defined(BOOST_ITERATOR_ADAPTORS_VERSION) || \
     BOOST_ITERATOR_ADAPTORS_VERSION < 0x0200
#error "Please use at least Boost V1.31.0 while compiling the position_iterator class!"
#else // BOOST_ITERATOR_ADAPTORS_VERSION < 0x0200

///////////////////////////////////////////////////////////////////////////////
//
//  Uses the newer iterator_adaptor version (should be released with
//  Boost V1.31.0)
//
///////////////////////////////////////////////////////////////////////////////
template <
    typename ForwardIteratorT,
    typename PositionT = file_position,
    typename SelfT = nil_t
>
class position_iterator
:   public iterator_::impl::position_iterator_base_generator<
        SelfT,
        ForwardIteratorT,
        PositionT
    >::type,
    public position_policy<PositionT>
{
private:

    typedef position_policy<PositionT> position_policy_t;
    typedef typename iterator_::impl::position_iterator_base_generator<
            SelfT,
            ForwardIteratorT,
            PositionT
        >::type base_t;
    typedef typename iterator_::impl::position_iterator_base_generator<
            SelfT,
            ForwardIteratorT,
            PositionT
        >::main_iter_t main_iter_t;

public:

    typedef PositionT position_t;

    position_iterator()
    :   _isend(true)
    {}

    position_iterator(
        const ForwardIteratorT& begin,
        const ForwardIteratorT& end)
    :   base_t(begin), _end(end), _pos(PositionT()), _isend(begin == end)
    {}

    template <typename FileNameT>
    position_iterator(
        const ForwardIteratorT& begin,
        const ForwardIteratorT& end,
        FileNameT fileName)
    :   base_t(begin), _end(end), _pos(PositionT(fileName)),
        _isend(begin == end)
    {}

    template <typename FileNameT, typename LineT>
    position_iterator(
        const ForwardIteratorT& begin,
        const ForwardIteratorT& end,
        FileNameT fileName, LineT line)
    :   base_t(begin), _end(end), _pos(PositionT(fileName, line)),
        _isend(begin == end)
    {}

    template <typename FileNameT, typename LineT, typename ColumnT>
    position_iterator(
        const ForwardIteratorT& begin,
        const ForwardIteratorT& end,
        FileNameT fileName, LineT line, ColumnT column)
    :   base_t(begin), _end(end), _pos(PositionT(fileName, line, column)),
        _isend(begin == end)
    {}

    position_iterator(
        const ForwardIteratorT& begin,
        const ForwardIteratorT& end,
        const PositionT& pos)
    :   base_t(begin), _end(end), _pos(pos), _isend(begin == end)
    {}

    position_iterator(const position_iterator& iter)
    :   base_t(iter.base()), position_policy_t(iter),
        _end(iter._end), _pos(iter._pos), _isend(iter._isend)
    {}

    position_iterator& operator=(const position_iterator& iter)
    {
        base_t::operator=(iter);
        position_policy_t::operator=(iter);
        _end = iter._end;
        _pos = iter._pos;
        _isend = iter._isend;
        return *this;
    }

    void set_position(PositionT const& newpos) { _pos = newpos; }
    PositionT& get_position() { return _pos; }
    PositionT const& get_position() const { return _pos; }

    void set_tabchars(unsigned int chars)
    {
        // This function (which comes from the position_policy) has a
        //  different name on purpose, to avoid messing with using
        //  declarations or qualified calls to access the base template
        //  function, which might break some compilers.
        this->position_policy_t::set_tab_chars(chars);
    }

private:
    friend class boost::iterator_core_access;

    void increment()
    {
        typename base_t::reference val = *(this->base());
        if (val == '\n' || val == '\r') {
            ++this->base_reference();
            if (this->base_reference() != _end) {
                typename base_t::reference val2 = *(this->base());
                if ((val == '\n' && val2 == '\r')
                    || (val == '\r' && val2 == '\n'))
                {
                    ++this->base_reference();
                }
            }
            this->next_line(_pos);
            static_cast<main_iter_t &>(*this).newline();
        }
        else if (val == '\t') {
            this->tabulation(_pos);
            ++this->base_reference();
        }
        else {
            this->next_char(_pos);
            ++this->base_reference();
        }

        // The iterator is at the end only if it's the same
        //  of the
        _isend = (this->base_reference() == _end);
    }

    template <
        typename OtherDerivedT, typename OtherIteratorT,
        typename V, typename C, typename R, typename D
    >
    bool equal(iterator_adaptor<OtherDerivedT, OtherIteratorT, V, C, R, D>
        const &x) const
    {
        OtherDerivedT const &rhs = static_cast<OtherDerivedT const &>(x);
        bool x_is_end = rhs._isend;

        return (_isend && x_is_end) ||
            (!_isend && !x_is_end && this->base() == rhs.base());
    }

protected:

    void newline(void)
    {}

    ForwardIteratorT _end;
    PositionT _pos;
    bool _isend;
};

#endif // BOOST_ITERATOR_ADAPTORS_VERSION < 0x0200

///////////////////////////////////////////////////////////////////////////////
//
//  position_iterator2
//
//  Equivalent to position_iterator, but it is able to extract the current
//  line into a string. This is very handy for error reports.
//
//  Notice that the footprint of this class is higher than position_iterator,
//  (how much depends on how bulky the underlying iterator is), so it should
//  be used only if necessary.
//
///////////////////////////////////////////////////////////////////////////////

template
<
    typename ForwardIteratorT,
    typename PositionT = file_position
>
class position_iterator2
    : public position_iterator
    <
        ForwardIteratorT,
        PositionT,
        position_iterator2<ForwardIteratorT, PositionT>
    >
{
    typedef position_iterator
    <
        ForwardIteratorT,
        PositionT,
        position_iterator2<ForwardIteratorT, PositionT> // JDG 4-15-03
    >  base_t;

public:
    typedef typename base_t::value_type value_type;
    typedef PositionT position_t;

    position_iterator2()
    {}

    position_iterator2(
        const ForwardIteratorT& begin,
        const ForwardIteratorT& end):
        base_t(begin, end),
        _startline(begin)
    {}

    template <typename FileNameT>
    position_iterator2(
        const ForwardIteratorT& begin,
        const ForwardIteratorT& end,
        FileNameT file):
        base_t(begin, end, file),
        _startline(begin)
    {}

    template <typename FileNameT, typename LineT>
    position_iterator2(
        const ForwardIteratorT& begin,
        const ForwardIteratorT& end,
        FileNameT file, LineT line):
        base_t(begin, end, file, line),
        _startline(begin)
    {}

    template <typename FileNameT, typename LineT, typename ColumnT>
    position_iterator2(
        const ForwardIteratorT& begin,
        const ForwardIteratorT& end,
        FileNameT file, LineT line, ColumnT column):
        base_t(begin, end, file, line, column),
        _startline(begin)
    {}

    position_iterator2(
        const ForwardIteratorT& begin,
        const ForwardIteratorT& end,
        const PositionT& pos):
        base_t(begin, end, pos),
        _startline(begin)
    {}

    position_iterator2(const position_iterator2& iter)
        : base_t(iter), _startline(iter._startline)
    {}

    position_iterator2& operator=(const position_iterator2& iter)
    {
        base_t::operator=(iter);
        _startline = iter._startline;
        return *this;
    }

    ForwardIteratorT get_currentline_begin(void) const
    { return _startline; }

    ForwardIteratorT get_currentline_end(void) const
    { return get_endline(); }

    std::basic_string<value_type> get_currentline(void) const
    {
        return std::basic_string<value_type>
            (get_currentline_begin(), get_currentline_end());
    }

protected:
    ForwardIteratorT _startline;

    friend class position_iterator<ForwardIteratorT, PositionT,
        position_iterator2<ForwardIteratorT, PositionT> >;

    ForwardIteratorT get_endline() const
    {
        ForwardIteratorT endline = _startline;
        while (endline != this->_end && *endline != '\r' && *endline != '\n')
        {
            ++endline;
        }
        return endline;
    }

    void newline(void)
    { _startline = this->base(); }
};

}} // namespace boost::spirit

#endif
