//  Copyright (c) 2001-2009 Hartmut Kaiser
// 
//  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_KARMA_FUNCTOR_APR_01_2007_1038AM)
#define BOOST_SPIRIT_KARMA_FUNCTOR_APR_01_2007_1038AM

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once      // MS compatible compilers support #pragma once
#endif

#include <boost/spirit/home/support/component.hpp>
#include <boost/spirit/home/support/unused.hpp>
#include <boost/spirit/home/support/auxiliary/functor_holder.hpp>
#include <boost/spirit/home/support/auxiliary/meta_function_holder.hpp>
#include <boost/spirit/home/support/detail/values.hpp>
#include <boost/spirit/home/karma/delimit.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/lambda.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/remove_const.hpp>

///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit 
{ 
    namespace karma
    {
        template <typename Functor, typename ParameterMF = Functor>
        class functor_generator;
    }
    
    namespace result_of
    {
        template <typename Functor>
        struct as_generator
        {
            typedef karma::functor_generator<Functor> type;
        };

        template <typename Functor, typename ParameterMF>
        struct as_generator_mf
        {
            typedef karma::functor_generator<Functor, ParameterMF> type;
        };
    }

}}  // boost::spirit

///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace spirit { namespace karma
{
    ///////////////////////////////////////////////////////////////////////////
    //  This struct may be used as a base class for a user defined functor
    ///////////////////////////////////////////////////////////////////////////
    struct functor_base
    {
        ///////////////////////////////////////////////////////////////////////
        //  The return value of a karma functor is always bool
        ///////////////////////////////////////////////////////////////////////
        template <typename Parameter, typename OutputIterator>
        struct result
        {
            typedef bool type;
        };

// FIXME: It will be possible to specify the return value as a typedef, but for 
//        that Phoenix will have to be fixed.
//         typedef bool result_type;
        
        ///////////////////////////////////////////////////////////////////////
        //  The expected parameter type of a functor has to be defined using a
        //  embedded apply metafunction. Normally this will be overloaded by 
        //  the derived class, but the default is unused type.
        ///////////////////////////////////////////////////////////////////////
        template <typename Context>
        struct apply
        {
            typedef spirit::unused_type type;
        };
    };
    
    ///////////////////////////////////////////////////////////////////////////
    //  The functor generator template may be used to create new generators
    //  without having to dig into the implementation details of Karma
    ///////////////////////////////////////////////////////////////////////////
    template <typename Functor, typename ParameterMF>
    class functor_generator
      : public proto::extends<
            typename make_functor_holder<
                functor_generator<Functor, ParameterMF> const*, 
                functor_generator<Functor, ParameterMF>
            >::type,
            functor_generator<Functor, ParameterMF>
        > 
    {
    private:
        typedef functor_generator<Functor, ParameterMF> self_type;
        typedef typename 
            make_functor_holder<self_type const*, self_type>::type 
        functor_tag;
        typedef proto::extends<functor_tag, self_type> base_type;

    public:
        template <typename Context>
        struct result 
          : mpl::apply<ParameterMF, Context>
        {};

    private:
        // generate function just delegates to the functor supplied function
        template <typename OutputIterator, typename Context, typename Parameter>
        bool 
        generate (OutputIterator& sink, Context& ctx, Parameter const& p) const
        {
            // create an attribute if none is supplied
            typedef typename result<Context>::type parameter_type;
            typename mpl::if_<
                is_same<typename remove_const<Parameter>::type, unused_type>,
                parameter_type,
                Parameter const&
            >::type
            param = spirit::detail::make_value<parameter_type>::call(p);

            return functor(param, ctx, sink);
        }

        friend struct functor_director;
        
    public:
        explicit functor_generator()
          : base_type(make_tag())
        {
        }

        functor_generator(Functor const& functor_)
          : base_type(make_tag()), functor(functor_)
        {
        }

        functor_generator(Functor const& functor_, ParameterMF const& mf)
          : base_type(make_tag()), functor(functor_), mf_(mf)
        {
        }

    private:
        functor_tag make_tag() const
        {
            functor_tag xpr = {{ this }};
            return xpr;
        }
        
        Functor functor;
        meta_function_holder<Functor, ParameterMF> mf_;
    };

    ///////////////////////////////////////////////////////////////////////////
    //  The as_generator generator function may be used to create a functor 
    //  generator from a function object (some callable item).
    //  The supplied functor needs to expose
    // 
    //    - an embedded result meta function: 
    //
    //          template <typename Parameter, typename OutputIterator>
    //          struct result
    //          {
    //              typedef bool type;
    //          };
    //
    //      which declares 'bool' as the result type of the defined function
    //      operator and
    //
    //    - an embedded apply meta function:
    //
    //          template <typename Context>
    //          struct apply
    //          {
    //              typedef unspecified type;
    //          };
    //
    //      which declares the given type as the expected attribute type for 
    //      the generator to create.
    ///////////////////////////////////////////////////////////////////////////
    template <typename Functor>
    inline typename result_of::as_generator<Functor>::type
    as_generator(Functor const& func)
    {
        return functor_generator<Functor>(func);
    }

    ///////////////////////////////////////////////////////////////////////////
    //  The as_generator_mf generator function is equivalent to the function
    //  as_generator above except that the user has to explicitly specify a
    //  type exposing an embedded apply meta function declaring the expected
    //  parameter type for the generator to create.
    ///////////////////////////////////////////////////////////////////////////
    template <typename ParameterMF, typename Functor>
    inline typename result_of::as_generator_mf<Functor, ParameterMF>::type
    as_generator_mf(Functor const& func, ParameterMF const& mf)
    {
        return functor_generator<Functor, ParameterMF>(func, mf);
    }

    template <typename ParameterMF, typename Functor>
    inline typename result_of::as_generator_mf<Functor, ParameterMF>::type
    as_generator_mf(Functor const& func)
    {
        return functor_generator<Functor, ParameterMF>(func, ParameterMF());
    }

}}}

#endif
