// ------------------------------------------------------------------------
/*
	author: Jaap Suter

	Copyright (C) Electronic Arts Canada Inc. 2002.  All rights reserved.	
*/
// ------------------------------------------------------------------------

/*!
    \file

    This is a meta-programming template used for preprocessing. Never include this file
    directly. Use the callback_n.h (where n is a number) files that use this template to implement
    different callbacks;
*/

// Note, no inclusion guards, this file is meant for multiple inclusion;

/*
    Portable implementation of preprocessor concatenation of two parameters;
    We need this wrapper around the ## operator because we use it to concatenate other indirectly
    and we can't directly do this using the ## operator because it would simply take the name. For example:

    #define A 1
    #define B 2
    #define C A ## B
    #define D REVEAL_APPEND( A, B )

    Now, C will equal AB and D will equal 12.

    This implementation is inspired by the CAT macro in the Boost Preprocessor library.

    The Metrowerks and MSVC checks are to deal with compiler problems.
*/
# if defined __MWERKS__ || defined __EA_GCCXML || defined __GNUC__
#    define REVEAL_APPEND(a, b) REVEAL_APPEND_I(a, b)
# else
#    define REVEAL_APPEND(a, b) REVEAL_APPEND_OO((a, b))
#    define REVEAL_APPEND_OO(par) REVEAL_APPEND_I ## par
# endif

#define REVEAL_APPEND_I(a, b) REVEAL_APPEND_II(a ## b)
#define REVEAL_APPEND_II(res) res

// Define the class names, appending the number of params to the names;
#define REVEAL_CALLBACK_NAME		 REVEAL_APPEND( Callback,				   REVEAL_CALLBACK_NUM_PARAMS )
#define REVEAL_CALLBACK_HANDLER_NAME REVEAL_APPEND( CallbackHandler,		   REVEAL_CALLBACK_NUM_PARAMS )
#define REVEAL_MEMFUN_HANDLER_NAME   REVEAL_APPEND( MemberFunctionHandler,	   REVEAL_CALLBACK_NUM_PARAMS )
#define REVEAL_FUN_HANDLER_NAME		 REVEAL_APPEND( FunctionAndFunctorHandler, REVEAL_CALLBACK_NUM_PARAMS )
#define REVEAL_NOOP_HANDLER_NAME	 REVEAL_APPEND( NoOp,					   REVEAL_CALLBACK_NUM_PARAMS )

#include "reveal/callback/impl/impl_pp_template.h"
#include "reveal/smart_ptr/scoped_ptr.h"
#include "reveal/type_traits/type_traits.h"

#include "reveal/global_enable.h" // It's really too bad we have to include a file outside of our source tree, but such is life, eh :).

namespace Reveal {
    namespace Callback {

// ------------------------------------------------------------------------
/*!
    \brief  Generic Callback Type

    A generic callback type able to call functions, functors and
    member to functions (methods).

    \param  Result Resulting type of calling the callback;

    \param  Param0, Param1, Param2, .... Param_n  Types of the parameters this callback supports. The number of parameters depends on the
            name of the class. Callback0 takes no parameters, Callback1 takes one parameter, Callback2 takes two parameters, etcetera.

	For a tutorial on how to use this callback library, see the
    accompanying unit test.

    \warning Using this callback instead of calling the function directly costs
             you a dynamic memory allocation (once on creation), and a virtual function call
             each time the callback is called.

    \warning C++ has (apart from the ANSI-C '...' system) no way of supporting var-arg function types, and hence the only
			 way to support callback systems with an arbitrary number of parameters, is by duplicating code. Andrei Alexandrescu uses
             a typelist mechanism in his book Modern C++ Design to mitigate some of it. Instead we use a system that preprocesses a
             template to instantiate callback classes with different names (a version for no parameters, a version for one parameter, a version
             for two parameters, etc.)

    \remark  For a detailed discussion on the implementation of this callback
			 library, see the book Modern C++ Design as most of the ideas implemented
			 here come straight from that book. Other inspiration (especially the preprocessing template technique)
             come from the Boost Function library.
*/
// ------------------------------------------------------------------------
template < REVEAL_CALLBACK_TEMPLATE_PARAMS >
class REVEAL_CALLBACK_NAME
{
public:

	/*!
		\brief Function application operator;

		Calls the stored callable entity;

		\pre            this->Valid() == true
		\param param	Parameter to pass to the callback;
		\returns		The callback return value;
	*/	
	Result operator () ( REVEAL_CALLBACK_PARAMS )
	{
	    assert( Valid() );
		assert( mCallBackHandler.Get() );
		return (*mCallBackHandler)( REVEAL_CALLBACK_ARGS );
	}

	/*!
		\brief Constructor for functors and free functions;

		Constructs a callback from a free function or an instantiated functor.

		\param callable	The callable entity, which is either a functor
						or a function-pointer.
		
		\remark This constructor is non-explicit on purpose, because we
				want implicit conversions from callable entities to callbacks
				for easy parameter passing. There is no risk involved with this
				conversion, and it allows the following syntax:
				\code
					void bar( int );
					void foo( Callback< void, int > someCallback );

					foo( &bar );
				\endcode
				Instead of:
				\code
					foo( Callback< void, int >( bar ) );
				\endcode
				In other words, it makes life easier without sacrificing risk;
	*/
#ifdef REVEAL
    template < class Callable >
    REVEAL_CALLBACK_NAME( const Callable& callable )
			: mCallBackHandler( Reveal::IsRevealGloballyEnabled() ?
									new Impl::REVEAL_FUN_HANDLER_NAME< Callable, REVEAL_CALLBACK_TEMPLATE_ARGS >( callable )
									:
									NULL
							  ) {}
#else
    template < class Callable >
    REVEAL_CALLBACK_NAME( const Callable& )
			: mCallBackHandler( NULL ) {}
#endif

    /*!
		\brief Constructor for member functions

		Constructs a callback from a member function and an object instantiation to apply the member function to.

		\param objectRef	The object to apply the callback on;
		\param ptrToMemFun	The address of the member function you want to apply;
	*/
#ifdef REVEAL
    template < class ObjectRef, class PtrToMemFun >
    REVEAL_CALLBACK_NAME( ObjectRef& objectRef, const PtrToMemFun& ptrToMemFun )
			: mCallBackHandler( Reveal::IsRevealGloballyEnabled() ?
									new Impl::REVEAL_MEMFUN_HANDLER_NAME< ObjectRef, PtrToMemFun, REVEAL_CALLBACK_TEMPLATE_ARGS >( objectRef, ptrToMemFun )
									:
									NULL
							  ) {}
#else
    template < class ObjectRef, class PtrToMemFun >
    REVEAL_CALLBACK_NAME( ObjectRef&, const PtrToMemFun&)
			: mCallBackHandler( NULL ) {}
#endif

    /*!
		\brief Default constructor for no-ops;

		Constructs a callback whose callback does nothing.

		This allows us to default construct callbacks that do (not yet) point
		to an actual callable entity. In turn, this allows us to do
		delayed assigment of callbacks.
    */
    REVEAL_CALLBACK_NAME() : mCallBackHandler( NULL ) {}

    /*!
		\brief Copy constructor

		Performs a deep copy of the callback;
	*/
	REVEAL_CALLBACK_NAME( const REVEAL_CALLBACK_NAME& callBack ) : mCallBackHandler( callBack.mCallBackHandler.Get() ? callBack.mCallBackHandler->Clone() : NULL ) {}

    /*!
		\brief Assigment operator
	*/
    REVEAL_CALLBACK_NAME& operator = ( const REVEAL_CALLBACK_NAME& callBack )
    {
        REVEAL_CALLBACK_NAME tmp( callBack );
        mCallBackHandler.Swap( tmp.mCallBackHandler );
        return (*this);
    }

	/*!
        \brief Tests whether the Callback currently has a callable entity that can be called;

        If you default construct a No-Op callback, it will not contain a callable entity. Hence it
        can't be called. Thus, if you call the callback, make sure that callback.Valid() is true.
    */
	bool Valid() const { return mCallBackHandler.Get() && mCallBackHandler->Valid(); }

private:

    SmartPtr::ScopedPtr< Impl::REVEAL_CALLBACK_HANDLER_NAME< REVEAL_CALLBACK_TEMPLATE_ARGS > > mCallBackHandler;
};

    } // Namespace Callback
} // Namespace Release


#undef REVEAL_CALLBACK_NAME
#undef REVEAL_CALLBACK_HANDLER_NAME
#undef REVEAL_MEMFUN_HANDLER_NAME
#undef REVEAL_FUN_HANDLER_NAME	
#undef REVEAL_NOOP_HANDLER_NAME	
#undef REVEAL_APPEND
#undef REVEAL_APPEND_OO
#undef REVEAL_APPEND_I
#undef REVEAL_APPEND_II
