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

#include <boost/spirit/home/x3/support/context.hpp>
#include <boost/spirit/home/x3/core/parser.hpp>
#include <boost/spirit/home/x3/core/skip_over.hpp>
#include <boost/concept_check.hpp>

namespace boost { namespace spirit { namespace x3
{
    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Parser, typename Attribute>
    inline bool
    parse_main(
        Iterator& first
      , Iterator last
      , Parser const& p
      , Attribute& attr)
    {
        // Make sure the iterator is at least a forward_iterator. If you got a
        // compilation error here, then you are using an input_iterator while
        // calling this function. You need to supply at least a forward_iterator
        // instead.
        BOOST_CONCEPT_ASSERT((ForwardIterator<Iterator>));

        // If you get an error no matching function for call to 'as_parser'
        // here, then p is not a parser or there is no suitable conversion
        // from p to a parser.
        return as_parser(p).parse(first, last, unused, unused, attr);
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Parser, typename Attribute>
    inline bool
    parse(
        Iterator& first
      , Iterator last
      , Parser const& p
      , Attribute& attr)
    {
        return parse_main(first, last, p, attr);
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Parser, typename Attribute>
    inline bool
    parse(
        Iterator const& first_
      , Iterator last
      , Parser const& p
      , Attribute& attr)
    {
        Iterator first = first_;
        return parse_main(first, last, p, attr);
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Parser>
    inline bool
    parse(
        Iterator& first
      , Iterator last
      , Parser const& p)
    {
        return parse_main(first, last, p, unused);
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Parser>
    inline bool
    parse(
        Iterator const& first_
      , Iterator last
      , Parser const& p)
    {
        Iterator first = first_;
        return parse_main(first, last, p, unused);
    }

    ///////////////////////////////////////////////////////////////////////////
    enum class skip_flag
    {
        post_skip,      // force post-skipping in phrase_parse()
        dont_post_skip  // inhibit post-skipping in phrase_parse()
    };

    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Parser, typename Skipper, typename Attribute>
    inline bool
    phrase_parse_main(
        Iterator& first
      , Iterator last
      , Parser const& p
      , Skipper const& s
      , Attribute& attr
      , skip_flag post_skip = skip_flag::post_skip)
    {
        // Make sure the iterator is at least a forward_iterator. If you got a
        // compilation error here, then you are using an input_iterator while
        // calling this function. You need to supply at least a forward_iterator
        // instead.
        BOOST_CONCEPT_ASSERT((ForwardIterator<Iterator>));
        
        static_assert(!std::is_same<Skipper, unused_type>::value,
            "Error! Skipper cannot be unused_type.");

        // If you get an error no matching function for call to 'as_parser'
        // here, for either p or s, then p or s is not a parser or there is
        // no suitable conversion from p to a parser.
        auto skipper_ctx = make_context<skipper_tag>(as_parser(s));
        bool r = as_parser(p).parse(first, last, skipper_ctx, unused, attr);
        if (post_skip == skip_flag::post_skip)
            x3::skip_over(first, last, skipper_ctx);
        return r;
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Parser, typename Skipper, typename Attribute>
    inline bool
    phrase_parse(
        Iterator& first
      , Iterator last
      , Parser const& p
      , Skipper const& s
      , Attribute& attr
      , skip_flag post_skip = skip_flag::post_skip)
    {
        return phrase_parse_main(first, last, p, s, attr, post_skip);
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Parser, typename Skipper, typename Attribute>
    inline bool
    phrase_parse(
        Iterator const& first_
      , Iterator last
      , Parser const& p
      , Skipper const& s
      , Attribute& attr
      , skip_flag post_skip = skip_flag::post_skip)
    {
        Iterator first = first_;
        return phrase_parse_main(first, last, p, s, attr, post_skip);
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Parser, typename Skipper>
    inline bool
    phrase_parse(
        Iterator& first
      , Iterator last
      , Parser const& p
      , Skipper const& s
      , skip_flag post_skip = skip_flag::post_skip)
    {
        return phrase_parse_main(first, last, p, s, unused, post_skip);
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename Iterator, typename Parser, typename Skipper>
    inline bool
    phrase_parse(
        Iterator const& first_
      , Iterator last
      , Parser const& p
      , Skipper const& s
      , skip_flag post_skip = skip_flag::post_skip)
    {
        Iterator first = first_;
        return phrase_parse_main(first, last, p, s, unused, post_skip);
    }
    
    ///////////////////////////////////////////////////////////////////////////
    template <typename Skipper>
    struct phrase_parse_context
    {
        typedef decltype(
            make_context<skipper_tag>(as_parser(std::declval<Skipper>())))
        type;
    };
}}}

#endif
