/*=============================================================================
    Copyright (c) 2001-2014 Joel de Guzman
    Copyright (c) 2013 Agustin Berge

    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_PARSER_OCTOBER_16_2008_0254PM)
#define BOOST_SPIRIT_X3_PARSER_OCTOBER_16_2008_0254PM

#include <boost/mpl/bool.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/utility/declval.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/spirit/home/x3/support/unused.hpp>
#include <boost/spirit/home/x3/support/context.hpp>
#include <boost/spirit/home/x3/support/traits/has_attribute.hpp>
#include <boost/spirit/home/x3/support/utility/sfinae.hpp>
#include <boost/core/ignore_unused.hpp>
#include <boost/assert.hpp>
#include <string>

#if !defined(BOOST_SPIRIT_X3_NO_RTTI)
#include <typeinfo>
#endif

namespace boost { namespace spirit { namespace x3
{
    template <typename Subject, typename Action>
    struct action;

    template <typename Subject, typename Handler>
    struct guard;

    struct parser_base {};
    struct parser_id;

    template <typename Derived>
    struct parser : parser_base
    {
        typedef Derived derived_type;
        static bool const handles_container = false;
        static bool const is_pass_through_unary = false;
        static bool const has_action = false;

        Derived const& derived() const
        {
            return *static_cast<Derived const*>(this);
        }

        template <typename Action>
        action<Derived, Action> operator[](Action f) const
        {
            return { this->derived(), f };
        }

        template <typename Handler>
        guard<Derived, Handler> on_error(Handler f) const
        {
            return { this->derived(), f };
        }
    };

    namespace detail {
        template <typename Parser>
        static void assert_initialized_rule(Parser const& p) {
            boost::ignore_unused(p);

            // Assert that we are not copying an unitialized static rule. If
            // the static is in another TU, it may be initialized after we copy
            // it. If so, its name member will be nullptr.
            //
            // Rather than hardcoding behaviour for rule-type subject parsers,
            // we simply allow get_info<> to do the check in debug builds.
#ifndef NDEBUG
            what(p); // note: allows get_info<> to diagnose the issue
#endif
        }
    }

    struct unary_category;
    struct binary_category;

    template <typename Subject, typename Derived>
    struct unary_parser : parser<Derived>
    {
        typedef unary_category category;
        typedef Subject subject_type;
        static bool const has_action = Subject::has_action;

        unary_parser(Subject const& subject)
            : subject(subject) { detail::assert_initialized_rule(subject); }

        unary_parser const& get_unary() const { return *this; }

        Subject subject;
    };

    template <typename Left, typename Right, typename Derived>
    struct binary_parser : parser<Derived>
    {
        typedef binary_category category;
        typedef Left left_type;
        typedef Right right_type;
        static bool const has_action =
            left_type::has_action || right_type::has_action;

        binary_parser(Left const& left, Right const& right)
            : left(left), right(right)
        {
            detail::assert_initialized_rule(left);
            detail::assert_initialized_rule(right);
        }

        binary_parser const& get_binary() const { return *this; }

        Left left;
        Right right;
    };

    ///////////////////////////////////////////////////////////////////////////
    // as_parser: convert a type, T, into a parser.
    ///////////////////////////////////////////////////////////////////////////
    namespace extension
    {
        namespace detail
        {
            namespace as_parser_guard
            {
                void as_spirit_parser(...);

                template<typename T, typename R =
                    decltype(as_spirit_parser(boost::declval<T const&>()))>
                struct deduce_as_parser
                {
                    typedef R type;
                    typedef typename
                        boost::remove_cv<
                            typename boost::remove_reference<R>::type
                        >::type
                    value_type;

                    static type call(T const& v)
                    {
                        return as_spirit_parser(v);
                    }
                };
                template<typename T>
                struct deduce_as_parser<T, void>
                {};
            }
            using as_parser_guard::deduce_as_parser;
        }

        template <typename T, typename Enable = void>
        struct as_parser : detail::deduce_as_parser<T> {};

        template <>
        struct as_parser<unused_type>
        {
            typedef unused_type type;
            typedef unused_type value_type;
            static type call(unused_type)
            {
                return unused;
            }
        };

        template <typename Derived>
        struct as_parser<Derived
            , typename enable_if<is_base_of<parser_base, Derived>>::type>
        {
            typedef Derived const& type;
            typedef Derived value_type;
            static type call(Derived const& p)
            {
                return p;
            }
        };

        template <typename Derived>
        struct as_parser<parser<Derived>>
        {
            typedef Derived const& type;
            typedef Derived value_type;
            static type call(parser<Derived> const& p)
            {
                return p.derived();
            }
        };
    }

    template <typename T>
    inline typename extension::as_parser<T>::type
    as_parser(T const& x)
    {
        return extension::as_parser<T>::call(x);
    }

    template <typename Derived>
    inline Derived const&
    as_parser(parser<Derived> const& p)
    {
        return p.derived();
    }

    ///////////////////////////////////////////////////////////////////////////
    // The main what function
    //
    // Note: unlike Spirit2, spirit parsers are no longer required to have a
    // "what" member function. In X3, we specialize the get_info struct
    // below where needed. If a specialization is not provided, the default
    // below will be used. The default "what" result will be the typeid
    // name of the parser if BOOST_SPIRIT_X3_NO_RTTI is not defined, otherwise
    // "undefined"
    ///////////////////////////////////////////////////////////////////////////
    template <typename Parser, typename Enable = void>
    struct get_info
    {
        typedef std::string result_type;
        std::string operator()(Parser const&) const
        {
#if !defined(BOOST_SPIRIT_X3_NO_RTTI)
            return typeid(Parser).name();
#else
            return "undefined";
#endif
        }
    };

    template <typename Parser>
    std::string what(Parser const& p)
    {
        return get_info<Parser>()(p);
    }
}}}

namespace boost { namespace spirit { namespace x3 { namespace traits
{
    template <typename Subject, typename Derived, typename Context>
    struct has_attribute<x3::unary_parser<Subject, Derived>, Context>
        : has_attribute<Subject, Context> {};

    template <typename Left, typename Right, typename Derived, typename Context>
    struct has_attribute<x3::binary_parser<Left, Right, Derived>, Context>
        : mpl::bool_<has_attribute<Left, Context>::value ||
                has_attribute<Right, Context>::value> {};
}}}}

#endif
