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

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

/*! \file */

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

#if defined (_MSC_VER)
#pragma warning( disable : 4512 )   // assignment operator could not be generated
#endif

#include <assert.h>

#ifdef __EA_GCCXML
#define assert      // allows the cpp-xml parser to parse this header without issue
#endif

#ifdef REVEAL_EXCEPTIONS
	#include <memory>
	#include <exception>
#endif

#include "reveal/callback/custom_memory.h"
#include "reveal/type_traits/type_traits.h"

namespace Reveal {
    namespace Callback {
		namespace Impl {

// ------------------------------------------------------------------------
/*!
    \brief  Callback Handler Interface

    Abstract base class for callback handlers.

    \param Result   Result type of the callback;
    \param Param0, Param1, ... Param_n   Parameter types of the callback;

    \warning        The virtual copy constructor "Clone()" returns a
                    raw pointer. The caller becomes the owner, thus if no ownership
                    is taken a memory leak is the result. A better solution would be to use
                    the 'transfer-of-ownership-ptr' named std::auto_ptr or some future Electronic
                    Arts-specific transfering smart pointer.
*/
// ------------------------------------------------------------------------
template < REVEAL_CALLBACK_TEMPLATE_PARAMS >
class REVEAL_CALLBACK_HANDLER_NAME
{
public:
    //! Virtual copy constructor;
    virtual REVEAL_CALLBACK_HANDLER_NAME* Clone() const = 0;

	virtual ~REVEAL_CALLBACK_HANDLER_NAME() {};

    virtual Result operator () ( REVEAL_CALLBACK_PARAMS ) = 0;
	
	virtual bool Valid() const = 0;

	//! Custom new, using the allocator;
	void* operator new( size_t size )
	{
		for ( ;; )
		{
			void* p = Memory::CallbackMalloc( size );			
			if ( p )
			{
				return p;
			}
			else
			{
				#ifdef REVEAL_EXCEPTIONS
					std::new_handler globalHandler = std::set_new_handler( NULL );
					std::set_new_handler( globalHandler );
					if (globalHandler) (*globalHandler)();
					else throw std::bad_alloc();
				#else
					assert( !"Out of memory, no exception handler enabled, fatal error." );
				#endif
			}
		}
	}

	//! Custom delete, using the allocator;
	void operator delete( void* ptr ) { Memory::CallbackFree( ptr ); }

	//! Custom array new, using the allocator;
	void* operator new [] ( size_t size ) { return operator new( size ); }
	
	//! Custom array delete, using the allocator;
	void operator delete [] ( void* ptr ) { operator delete( ptr ); }
};

// ------------------------------------------------------------------------
/*!
    \brief  Implementation of CallbackHandler interface for functions and functors;

    \param Callable Type for which instances are callable (that means, we can
                    apply operator () on it).
    \param Result   Result type of the callback;
    \param Param0, Param1, ... Param_n   Parameter types of the callback;
*/
// ------------------------------------------------------------------------
template < class Callable, REVEAL_CALLBACK_TEMPLATE_PARAMS >
class REVEAL_FUN_HANDLER_NAME : public REVEAL_CALLBACK_HANDLER_NAME< REVEAL_CALLBACK_TEMPLATE_ARGS >
{
public:
    // Default copy ctor and assigment operator are fine;

    //! Virtual copy constructor;
    virtual REVEAL_CALLBACK_HANDLER_NAME< REVEAL_CALLBACK_TEMPLATE_ARGS >* Clone() const
    {
        return new REVEAL_FUN_HANDLER_NAME( *this );
    }

	explicit REVEAL_FUN_HANDLER_NAME( const Callable& callable ) : mCallable( callable ) {}
    virtual ~REVEAL_FUN_HANDLER_NAME() {};

    virtual Result operator () ( REVEAL_CALLBACK_PARAMS )
	{
		return mCallable( REVEAL_CALLBACK_ARGS );
	}

	virtual bool Valid() const { return true; }

private:

    Callable mCallable;
};

// ------------------------------------------------------------------------
/*!
    \brief Implementation of CallbackHandler interface for memberfunctions

    \param PtrToObj     Type of which the function is a member;
    \param PtrToMemFun  Pointer to member function;
    \param Result       Result type of the callback;
    \param Param0, Param1, ... Param_n   Parameter types of the callback;
*/
// ------------------------------------------------------------------------
template < class ObjectRef, class PtrToMemFun, REVEAL_CALLBACK_TEMPLATE_PARAMS >
class REVEAL_MEMFUN_HANDLER_NAME : public REVEAL_CALLBACK_HANDLER_NAME< REVEAL_CALLBACK_TEMPLATE_ARGS >
{
	// Default copy ctor is ok, assigment operator is disabled (references can't be reseated);
	REVEAL_MEMFUN_HANDLER_NAME& operator = ( const REVEAL_MEMFUN_HANDLER_NAME& );

public:

    //! Virtual copy constructor;
    virtual REVEAL_CALLBACK_HANDLER_NAME< REVEAL_CALLBACK_TEMPLATE_ARGS >* Clone() const
    {
        return new REVEAL_MEMFUN_HANDLER_NAME( *this );
    }

    REVEAL_MEMFUN_HANDLER_NAME( ObjectRef& objectRef, const PtrToMemFun& ptrToMemFun )
        : mObjectRef( objectRef ), mPtrToMemFun( ptrToMemFun ) {}
    virtual ~REVEAL_MEMFUN_HANDLER_NAME() {};

	virtual Result operator () ( REVEAL_CALLBACK_PARAMS )
	{
	    return (mObjectRef.*mPtrToMemFun)( REVEAL_CALLBACK_ARGS );
	}
		
	virtual bool Valid() const { return true; }

private:
    ObjectRef& mObjectRef;
    PtrToMemFun mPtrToMemFun;
};

        } // Namespace Impl
    } // Namespace Callback;
} // Namespace Release
