/*=============================================================================
    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/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;

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

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

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

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;

    constexpr
    unary_parser(Subject const& subject)
        : subject(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;

    constexpr
    binary_parser(Left const& left, Right const& right)
        : left(left), right(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 constexpr 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 constexpr type
    call(Derived const& p)
    {
        return p;
    }
};

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

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

template <typename Derived>
constexpr 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
