/**
 * @file TestCoroutine.h
 *
 * @brief Contains all helper classes required for testing ODEMx module \b Coroutine.
 */

#ifndef TESTCOROUTINE_H_
#define TESTCOROUTINE_H_

#include <UnitTest++.h>
#include "../Main/Globals.h"

#include <odemx/data/SimRecordFilter.h>

#include <odemx/base/DefaultSimulation.h>
#include <odemx/coroutine/Coroutine.h>
#include <odemx/coroutine/CoroutineContext.h>
#include <odemx/util/StringConversion.h>

/**
 * @namespace Coro
 *
 * @brief Contains helpers and tests for module ODEMx Coroutine
 *
 * The UnitTest++ macro SUITE( Coro ) declares namespace Coro.
 * Consequently, all helpers for this test suite should be placed in
 * the same namespace. The canonical name Coroutine could not be used here
 * because it is already used as a type name.
 */
namespace SuiteCoro {

//---------------------------------------------------------------------coroutine

using namespace odemx;
using namespace odemx::coroutine;
using namespace std;

/**
 * @class CoroutineTest
 * @brief Simple Coroutine class that switches execution with a partner
 */
class CoroutineTest:
	public Coroutine,
	public NamedElement
{
public:
	CoroutineTest* partner;	///< Partner CoroutineTest object for switching
	bool isInitializer;		///< Control different flow for starter Coroutine
	bool isStarted;			///< Shows whether a Coroutine has been started
	bool isFinished;		///< Shows whether a Coroutine is done with execution
	unsigned int loopCount;	///< Counts the number of reactivations

	/// Constructor that can be used with a default context
	CoroutineTest( const data::Label& l, CoroutineContext* c = 0, CoroutineObserver* o = 0 ):
		Coroutine( c, o ),
		NamedElement( getDefaultSimulation(), l ),
		partner( 0 ),
		isInitializer( false ),
		isStarted( false ),
		isFinished( false ),
		loopCount( 0 )
		{}

protected:

	/**
	 * @brief Implementation of coroutine behavior
	 *
	 * First, \c isStarted will be set to \c true. If this coroutine is the
	 * initializer, it will first return control to its context (where its
	 * partner should be set).
	 *
	 * Once it receives control again, it enters the main loop, where it
	 * switches execution with its partner coroutine, thereby counting the
	 * reactivations.
	 *
	 * Finally, after 10 switches, \c isFinished will be set to \c true.
	 */
	virtual void run()
	{
		isStarted = true;

		if( isInitializer )
			getContext()->switchTo();

		for( int i = 0; i < 10; ++i )
		{
			++loopCount;
			if( partner ) partner->switchTo();
		}

		if( isInitializer )
			if( partner ) partner->switchTo();

		isFinished = true;
	}
};

/**
 * @class CoroutineTestObserver
 * @brief Implements CoroutineObserver to validate execution of methods
 */
class CoroutineTestObserver:
	public ObserverBase,
	public CoroutineObserver
{
public:
	/**
	 * @brief Constructor
	 * @param output determines whether Observations are sent to stdout
	 */
	CoroutineTestObserver( bool output ):
		ObserverBase( output )
		{}

	/**
	 * @name Implementation of virtual Observer functions
	 *
	 * Most ODEMx classes define an Observer interface with empty
	 * implementation. These virtual functions are re-implemented here in
	 * order to observe the behavior of test objects. All of them call
	 * ObserverBase::saw() to store events with parameters in the
	 * observation history which can then be used to verify expected
	 * behavior of objects.
	 *
	 * @{
	 */
	virtual void onCreate( Coroutine* sender ){ ObserverBase::saw( "onCreate", toString( sender ) ); }
	virtual void onDestroy( Coroutine* sender ){ ObserverBase::saw( "onDestroy", toString( sender ) ); }
	virtual void onInitialize( Coroutine* sender ){ ObserverBase::saw( "onInitialize", toString(sender) ); }
	virtual void onSwitchTo( Coroutine* sender, Coroutine* previousActive )
	{ ObserverBase::saw( "onSwitchTo", toString(sender), toString(previousActive) ); }
	virtual void onSwitchTo( Coroutine* sender, CoroutineContext* previousActive )
	{ ObserverBase::saw( "onSwitchTo", toString(sender), toString(previousActive) ); }
	virtual void onClear( Coroutine* sender ) { ObserverBase::saw( "onClear", toString(sender) ); }
	virtual void onChangeState( Coroutine* sender, Coroutine::State oldState, Coroutine::State newState )
	{
		string fromState, toState;
		switch( oldState )
		{
		case Coroutine::CREATED: fromState = "CREATED";	break;
		case Coroutine::RUNNABLE: fromState = "RUNNABLE"; break;
		case Coroutine::TERMINATED: fromState = "ERROR"; break;
		}

		switch( newState )
		{
		case Coroutine::CREATED: toState = "CREATED"; break;
		case Coroutine::RUNNABLE: toState = "RUNNABLE"; break;
		case Coroutine::TERMINATED: toState = "TERMINATED";	break;
		}

		ObserverBase::saw( "onChangeState", toString(sender), fromState, toState );
	}
	//@}
};

//-------------------------------------------------------------coroutine context

/**
 * @class CoroutineContextTest
 * @brief Simple subclass of CoroutineContext, makes a few functions public
 */
class CoroutineContextTest:
	public NamedElement,
	public CoroutineContext
{
public:
	/// Constructor
	CoroutineContextTest( const data::Label& l = "", CoroutineContextObserver* o = 0 ):
		NamedElement( getDefaultSimulation(), l ),
		CoroutineContext( o )
		{}
	/// makes the protected base class function publicly available
	Coroutine* getActiveCoroutine()
	{
		return CoroutineContext::getActiveCoroutine();
	}
	/// makes the protected base class function publicly available
	unsigned int getNumberOfCoroutines()
	{
		return CoroutineContext::getNumberOfCoroutines();
	}
};

/**
 * @class CoroutineContextTestObserver
 * @brief Implements CoroutineContextObserver to validate execution of methods
 */
class CoroutineContextTestObserver:
	public ObserverBase,
	public CoroutineContextObserver
{
public:
	/**
	 * @brief Constructor
	 * @param output determines whether Observations are sent to stdout
	 */
	CoroutineContextTestObserver( bool output ):
		ObserverBase( output )
		{}

	/**
	 * @name Implementation of virtual Observer functions
	 *
	 * Most ODEMx classes define an Observer interface with empty
	 * implementation. These virtual functions are re-implemented here in
	 * order to observe the behavior of test objects. All of them call
	 * ObserverBase::saw() to store events with parameters in the
	 * observation history which can then be used to verify expected
	 * behavior of objects.
	 *
	 * @{
	 */
	virtual void onCreate( CoroutineContext* sender ){ ObserverBase::saw( "onCreate", toString(sender) ); }
	virtual void onDestroy( CoroutineContext* sender ){ ObserverBase::saw( "onDestroy", toString(sender) ); }
	virtual void onSwitchTo( CoroutineContext* sender, Coroutine* previousActive )
	{ ObserverBase::saw( "onSwitchTo", toString(sender), toString(previousActive) ); }
	virtual void onChangeActiveCoroutine( CoroutineContext* sender, Coroutine* oldActive, Coroutine* newActive )
	{ ObserverBase::saw( "onChangeActiveCoroutine", toString(sender), toString(oldActive), toString(newActive) );	}
	//@}
};

} // namespace SuiteCoro

#endif /*TESTCOROUTINE_H_*/
