/*=============================================================================
    Copyright (c) 2001-2007 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)
==============================================================================*/
#ifndef PHOENIX_CORE_ACTOR_HPP
#define PHOENIX_CORE_ACTOR_HPP

#include <boost/spirit/home/phoenix/core/limits.hpp>

#if !defined(BOOST_RESULT_OF_NUM_ARGS)
# define BOOST_RESULT_OF_NUM_ARGS PHOENIX_ACTOR_LIMIT
#elif (BOOST_RESULT_OF_NUM_ARGS < PHOENIX_ACTOR_LIMIT)
# error "BOOST_RESULT_OF_NUM_ARGS < PHOENIX_ACTOR_LIMIT"
#endif

#include <boost/spirit/home/phoenix/core/basic_environment.hpp>
#include <boost/mpl/min.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/type_traits/add_const.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/utility/result_of.hpp>

namespace boost { namespace phoenix
{
    // phoenix::void_ is the same as fusion::void_
    typedef fusion::void_ void_;

    namespace detail
    {
        //  Forward declarations. These will come in when we get to the
        //  operator module, yet, the actor's assignment operator and index
        //  operator are required to be members.

        template <typename T0, typename T1>
        struct make_assign_composite;

        template <typename T0, typename T1>
        struct make_index_composite;

        template <typename BaseT0, typename BaseT1>
        struct comma_result;

        // error no arguments supplied
        struct error_expecting_arguments
        {
            template <typename T>
            error_expecting_arguments(T const&) {}
        };
    }

    template <typename Eval, typename Env>
    struct eval_result
    {
        typedef typename Eval::template result<Env>::type type;
    };

    template <typename Eval>
    struct actor : Eval
    {
        typedef actor<Eval> self_type;
        typedef Eval eval_type;

        template <class Sig> struct result {};

        actor()
            : Eval() {}

        actor(Eval const& base)
            : Eval(base) {}

        template <typename T0>
        explicit actor(T0 const& _0)
            : Eval(_0) {}

        template <typename T0, typename T1>
        actor(T0 const& _0, T1 const& _1)
            : Eval(_0, _1) {}

        typedef typename
            mpl::eval_if<
                typename Eval::no_nullary // avoid calling eval_result when this is true
              , mpl::identity<detail::error_expecting_arguments>
              , eval_result<eval_type, basic_environment<> >
            >::type
        nullary_result;

        nullary_result
        operator()() const
        {
            return eval_type::eval(basic_environment<>());
        }

        template <class F, class A0>
        struct result<F(A0)>
          : eval_result<
                eval_type
              , basic_environment<
                    typename remove_reference<A0>::type
                >
            >
        {};

        template <typename T0>
        typename result<actor(T0&)>::type
        operator()(T0& _0) const
        {
            return eval_type::eval(basic_environment<T0>(_0));
        }

        template <class F, class A0, class A1>
        struct result<F(A0,A1)>
          : eval_result<
                eval_type
              , basic_environment<
                    typename remove_reference<A0>::type
                  , typename remove_reference<A1>::type
                >
            >
        {};

        template <typename T0, typename T1>
        typename result<actor(T0&,T1&)>::type
        operator()(T0& _0, T1& _1) const
        {
            return eval_type::eval(basic_environment<T0, T1>(_0, _1));
        }

        template <typename T1>
        typename detail::make_assign_composite<self_type, T1>::type
        operator=(T1 const& a1) const;

        template <typename T1>
        typename detail::make_index_composite<self_type, T1>::type
        operator[](T1 const& a1) const;

        //  Bring in the rest of the constructors and function call operators
        #include <boost/spirit/home/phoenix/core/detail/actor.hpp>

    private:
        // silence MSVC warning C4512: assignment operator could not be generated
        actor& operator= (actor const&);
    };

    //  Forward declaration: The intent to overload the comma must be
    //  stated early on to avoid the subtle problem that arises when
    //  the header file where the comma operator overload is defined,
    //  is not included by the client and the client attempts to use
    //  the comma anyway.

    namespace detail
    {
        template <typename BaseT0, typename BaseT1>
        struct comma_result;
    }

    template <typename BaseT0, typename BaseT1>
    typename detail::comma_result<BaseT0, BaseT1>::type
    operator,(actor<BaseT0> const& a0, actor<BaseT1> const& a1);
}}

namespace boost
{
    template <typename Eval>
    struct result_of<phoenix::actor<Eval>()>
    {
        typedef typename phoenix::actor<Eval>::nullary_result type;
    };

    template <typename Eval>
    struct result_of<phoenix::actor<Eval> const()>
        : result_of<phoenix::actor<Eval>()>
    {};
}

#endif
