/*=============================================================================
    Copyright (c) 2001-2009 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(SPIRIT_SKIP_JANUARY_26_2008_0422PM)
#define SPIRIT_SKIP_JANUARY_26_2008_0422PM

#if defined(_MSC_VER)
#pragma once
#endif

#include <boost/spirit/home/qi/meta_compiler.hpp>
#include <boost/spirit/home/qi/parser.hpp>
#include <boost/spirit/home/qi/auxiliary/lazy.hpp>
#include <boost/spirit/home/qi/operator/kleene.hpp>
#include <boost/spirit/home/qi/directive/lexeme.hpp>
#include <boost/spirit/home/qi/skip_over.hpp>
#include <boost/spirit/home/support/container.hpp>
#include <boost/spirit/home/support/common_terminals.hpp>
#include <boost/spirit/home/support/attributes.hpp>
#include <boost/spirit/home/support/info.hpp>
#include <boost/fusion/include/at.hpp>
#include <boost/fusion/include/vector.hpp>

namespace boost { namespace spirit
{
    ///////////////////////////////////////////////////////////////////////////
    // Enablers
    ///////////////////////////////////////////////////////////////////////////
    template <>
    struct use_directive<qi::domain, tag::skip>     // enables skip[p]
      : mpl::true_ {};

    template <typename T>
    struct use_directive<qi::domain
      , terminal_ex<tag::skip                       // enables skip(s)[p]
        , fusion::vector1<T> >
    > : boost::spirit::traits::matches<qi::domain, T> {};

    template <>                                     // enables *lazy* skip(s)[p]
    struct use_lazy_directive<
        qi::domain
      , tag::skip
      , 1 // arity
    > : mpl::true_ {};
}}

namespace boost { namespace spirit { namespace qi
{
    using spirit::skip;
    using spirit::skip_type;

    template <typename Skipper>
    struct unused_skipper;

    namespace detail
    {
        template <typename Skipper>
        inline Skipper const& get_skipper(unused_skipper<Skipper> const& u)
        {
            return u.skipper;
        }

        template <typename Skipper>
        inline Skipper const& get_skipper(Skipper const& u)
        {
            return u;
        }
    }

    template <typename Subject>
    struct reskip_parser : unary_parser<reskip_parser<Subject> >
    {
        typedef Subject subject_type;

        template <typename Context, typename Iterator>
        struct attribute
        {
            typedef typename
                traits::attribute_of<Subject, Context, Iterator>::type
            type;
        };

        reskip_parser(Subject const& subject)
          : subject(subject) {}

        template <typename Iterator, typename Context
          , typename Skipper, typename Attribute>
        bool parse(Iterator& first, Iterator const& last
          , Context& context, Skipper const& u // --> The skipper is reintroduced
          , Attribute& attr) const
        {
            return subject.parse(first, last, context, detail::get_skipper(u), attr);
        }

        template <typename Context>
        info what(Context& context) const
        {
            return info("skip", subject.what(context));
        }

        Subject subject;
    };

    template <typename Subject, typename Skipper>
    struct skip_parser : unary_parser<skip_parser<Subject, Skipper> >
    {
        typedef Subject subject_type;
        typedef Skipper skipper_type;

        template <typename Context, typename Iterator>
        struct attribute
        {
            typedef typename
                traits::attribute_of<Subject, Context, Iterator>::type
            type;
        };

        skip_parser(Subject const& subject, Skipper const& skipper)
          : subject(subject), skipper(skipper) {}

        template <typename Iterator, typename Context
          , typename Skipper_, typename Attribute>
        bool parse(Iterator& first, Iterator const& last
          , Context& context, Skipper_ const& //skipper --> bypass the supplied skipper
          , Attribute& attr) const
        {
            return subject.parse(first, last, context, skipper, attr);
        }

        template <typename Context>
        info what(Context& context) const
        {
            return info("skip", subject.what(context));
        }

        Subject subject;
        Skipper skipper;
    };

    ///////////////////////////////////////////////////////////////////////////
    // Parser generators: make_xxx function (objects)
    ///////////////////////////////////////////////////////////////////////////
    template <typename Subject, typename Modifiers>
    struct make_directive<tag::skip, Subject, Modifiers>
    {
        typedef reskip_parser<Subject> result_type;
        result_type operator()(unused_type, Subject const& subject, unused_type) const
        {
            return result_type(subject);
        }
    };

    template <typename Skipper, typename Subject, typename Modifiers>
    struct make_directive<
        terminal_ex<tag::skip, fusion::vector1<Skipper> >, Subject, Modifiers>
    {
        typedef typename
            result_of::compile<qi::domain, Skipper, Modifiers>::type
        skipper_type;

        typedef skip_parser<Subject, skipper_type> result_type;

        template <typename Terminal>
        result_type operator()(
            Terminal const& term, Subject const& subject, unused_type) const
        {
            return result_type(subject
              , compile<qi::domain>(fusion::at_c<0>(term.args)));
        }
    };

}}}

#endif
