/*=============================================================================
    Copyright (c) 2014 Joel de Guzman

    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_X3_POSITION_TAGGED_MAY_01_2014_0321PM)
#define BOOST_SPIRIT_X3_POSITION_TAGGED_MAY_01_2014_0321PM

#include <boost/range/iterator_range_core.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/core/enable_if.hpp>

namespace boost {
namespace spirit {
namespace x3 {
struct position_tagged
{
    // Use this to annotate an AST with the iterator position.
    // These ids are used as a key to the position_cache (below)
    // and marks the start and end of an AST node.
    int id_first = -1;
    int id_last = -1;
};

template <typename Container>
class position_cache
{
  public:

    typedef typename Container::value_type iterator_type;

    position_cache(
        iterator_type first
        , iterator_type last)
        : first_(first), last_(last) {}

    // This will catch all nodes inheriting from position_tagged
    boost::iterator_range<iterator_type>
    position_of(position_tagged const& ast) const
    {
        return
            boost::iterator_range<iterator_type>(
                positions.at(ast.id_first) // throws if out of range
                , positions.at(ast.id_last)  // throws if out of range
            );
    }

    // This will catch all nodes except those inheriting from position_tagged
    template <typename AST>
    typename boost::enable_if_c<
    (!is_base_of<position_tagged, AST>::value)
    , boost::iterator_range<iterator_type>
    >::type
    position_of(AST const& /* ast */) const
    {
        // returns an empty position
        return boost::iterator_range<iterator_type>();
    }

    // This will catch all nodes except those inheriting from position_tagged
    template <typename AST>
    void
    annotate(AST& /* ast */, iterator_type /* first */, iterator_type /* last */, mpl::false_)
    {
        // (no-op) no need for tags
    }

    // This will catch all nodes inheriting from position_tagged
    void
    annotate(position_tagged& ast, iterator_type first, iterator_type last, mpl::true_)
    {
        ast.id_first = int(positions.size());
        positions.push_back(first);
        ast.id_last = int(positions.size());
        positions.push_back(last);
    }

    template <typename AST>
    void
    annotate(AST& ast, iterator_type first, iterator_type last)
    {
        annotate(ast, first, last, is_base_of<position_tagged, AST>());
    }

    Container const&
    get_positions() const
    {
        return positions;
    }

    iterator_type
    first() const
    {
        return first_;
    }
    iterator_type
    last() const
    {
        return last_;
    }

  private:

    Container positions;
    iterator_type first_;
    iterator_type last_;
};

}
}
}

#endif
