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

/*! \file */

#include "reveal/callback/unit_test.h"
#include "reveal/callback/callback_0.h"
#include "reveal/callback/callback_1.h"
#include "reveal/callback/callback_2.h"
#include "reveal/callback/callback_3.h"
#include "reveal/callback/assign_to_ref_adapter.h"
#include "reveal/callback/mem_fun_adapter.h"

#ifdef REVEAL_STD_CPP_AVAILABLE
	#include <functional>
#endif

namespace Reveal {
    namespace Callback {
		namespace UnitTest {
		
// ------------------------------------------------------------------------

// Just a test function;
int Function( int i ) { return i; }

// Just a test functor;
struct Functor { int operator () ( int i ) const { return i; } };

// Just a test object with a member function;
struct Object { int MemberFunction( int i ) const { return i; } };

// A few test functions of different type;
void FloatToVoid( float ) {}
bool StringToBool( const char* ) { return true; }
void ObjectToVoid( const Object& ) {}

// ------------------------------------------------------------------------
bool Test()
{
    // Test a callback that will call a function;
    Callback1< int, int > testFunction( &Function );
    if ( 0 != testFunction( 0 ) ) return false;
    
    // Test a callback that will call a functor;
	Functor f;
	Callback1< int, int > testFunctor( f );
    if ( 0 != testFunctor( 0 ) ) return false;
	
	// Test a callback that will call the memberfunction, applied to the given object;
    Object o;
    Callback1< int, int > testMemberFunction( o, &Object::MemberFunction );
	if ( 0 != testMemberFunction( 0 ) ) return false;

	// Test it the Standard C++ way;
	#ifdef REVEAL_STD_CPP_AVAILABLE
		Callback1< int, int > testMemberFunctionUsingStandard( std::bind1st( std::mem_fun( &Object::MemberFunction ), &o ) );
		if ( 0 != testMemberFunctionUsingStandard( 0 ) ) return false;	
	#endif

	// Test it the Callback::MemFunAdapter way;
	Callback1< int, int > testMemberFunctionUsingAdapter( MemFunAdapter1( o, &Object::MemberFunction ) );
	if ( 0 != testMemberFunctionUsingAdapter( 0 ) ) return false;	

    // Test a copy of an existing callback;
    Callback1< int, int > copiedCallback( testMemberFunction );
    if ( 0 != copiedCallback( 0 ) ) return false;

    // Test assign something else to it, and then calling the callback again;
    copiedCallback = testFunction;
    if ( 0 != copiedCallback( 0 ) ) return false;

    // Test a callback not pointing to anything;
    Callback1< int, int > defaultConstructedCallback;    
    // Would trigger an assertion, because it is not assigned yet: defaultConstructedCallback( 0 );

    // Assign somethign to it, and test it again;
    defaultConstructedCallback = testFunctor;
   	if ( 0 != defaultConstructedCallback( 0 ) ) return false;

	// Test instantiating a no-op;
	Callback0< void > callbackTakingVoidDoingNoOp;

    // Callback can have any parameter and return type of course, even objects of 
    // classtype (these lines are added just to make sure it compiles);
    Callback1< void, float       > someCallback0( &FloatToVoid  ); someCallback0( 0.0f );
    Callback1< bool, const char* > someCallback1( &StringToBool ); someCallback1( "Hello Callback" ); 
    Callback1< void, Object      > someCallback2( &ObjectToVoid ); someCallback2( Object() );

    // It is also possible to set callbacks directly to variables. For example;
    double someVariable = 0.0;
    Callback1< void, double > directAccessToVariable( AssignToRefAdapter( someVariable ) );
    directAccessToVariable( 1.0 ); 
    if ( someVariable != 1.0 ) return false;

    // What about implicit conversions;
    int integer = 0;
    Callback1< void, bool > intToBoolCallback( AssignToRefAdapter( integer ) );
    intToBoolCallback( true );
    if ( ! integer ) return false;

    // Unit test completed succesfully;
    return true;
}

	    } // Namespace UnitTest
    } // Namespace Callback
} // Namespace Reveal;
