//------------------------------------------------------------------------------
//	Copyright (C) 2002, 2004, 2009 Humboldt-Universitaet zu Berlin
//
//	This library is free software; you can redistribute it and/or
//	modify it under the terms of the GNU Lesser General Public
//	License as published by the Free Software Foundation; either
//	version 2.1 of the License, or (at your option) any later version.
//
//	This library is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//	Lesser General Public License for more details.
//
//	You should have received a copy of the GNU Lesser General Public
//	License along with this library; if not, write to the Free Software
//	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/**
 * @file CoroutineContext.h
 * @author Ralf Gerstenberger
 * @date created at 2002/02/04
 * @brief Declaration of odemx::coroutine::CoroutineContext, observer, and DefaultContext
 * @sa CoroutineContext.cpp
 * @since 1.0
 */

#ifndef ODEMX_CORO_COROUTINECONTEXT_INCLUDED
#define ODEMX_CORO_COROUTINECONTEXT_INCLUDED

#include <odemx/coroutine/System.h>
#include <odemx/data/Observable.h>

#include <cassert>
#include <typeinfo>
#include <exception>

namespace odemx {
namespace coroutine {

// forward declaration
class Coroutine;
class CoroutineContextObserver;

/** \class CoroutineContext

	\ingroup coroutine

	\author Ralf Gerstenberger

	\brief Context for executing Coroutines.

	\note CoroutineContext supports Observation.

	\sa CoroutineContextObserver.

	A CoroutineContext encapsulates the main program. It is required for the use of Coroutine.
	Execution can be switched between Coroutine and CoroutineContext.
	The implementation is stack-based for Linux (based on concepts from Stroustrup "Task-Library",
	AT&T 1991 and Hansen "The C++ -Answer Book", Addison Wesley 1990) or Fiber-based
	for Microsoft Windows 98 and later (based on process implementation in ODEM by Martin von L�wis).

	The stack-based implementation should be portable to any platform using linear, continuous stacks
	(growing bottom up or top down) with a C++ (and exception handling) compatible setjmp()/longjmp()
	implementation.

	\since 1.0
*/
class CoroutineContext
:	public data::Observable< CoroutineContextObserver >
{
public:
	/// Construction
	CoroutineContext( CoroutineContextObserver* o = 0 );
	/// Destruction
	virtual ~CoroutineContext();

	/// Call this method to continue execution in the coroutine context
	void switchTo();

	friend class Coroutine;

protected:
	/// Get a pointer to the currently running coroutine
	Coroutine* getActiveCoroutine();
	/// Get the number of registered coroutines of this context
	unsigned int getNumberOfCoroutines();

private:
	Coroutine* activeCoroutine; ///< active Coroutine
	unsigned int numberOfCoroutines; ///< counter of Coroutines in this context

	/** \brief set active Coroutine
		\param c
			pointer to next active Coroutine

		\return previous active Coroutine
		\note There is no more than one Coroutine active in a context at any time.
	*/
	Coroutine* setActiveCoroutine( Coroutine* c );

	/**
		\brief register new Coroutine in this context
		\param cr
			pointer to new Coroutine
	*/
	void beginCoroutine( Coroutine* cr );

	/**
		\brief Coroutine in this context has finished
		\param cr
			pointer to finished Coroutine
	*/
	void endCoroutine( Coroutine* cr );

	/// \name Fiber based implementation
	//@{
	LPVOID fiber; ///< pointer to context Fiber
	static bool initFibers; ///< ConvertThreadToFiber called
	//@}

	/// \name Fiber based implementation
	//@{
	void saveFiber(); ///< save current execution point
	//@}

	friend VOID CALLBACK ExecFiber( PVOID );
	friend void FiberSwitch( LPVOID fiber );
};

/** \class CoroutineContextObserver

	\author RalfGerstenberger

	\brief Observer for CoroutineContext specific events.

	\sa CoroutineContext

	\since 1.0
*/
class CoroutineContextObserver {
public:
	virtual ~CoroutineContextObserver() {}

	virtual void onCreate( CoroutineContext* sender ) {} ///< creation
	virtual void onDestroy( CoroutineContext* sender ) {} ///< destruction

	/// execution switches from Coroutine to CoroutineContext
	virtual void onSwitchTo( CoroutineContext* sender, Coroutine* previousActive ) {}

	/// active Coroutine changed (oldActive and newActive might be 0)
	virtual void onChangeActiveCoroutine( CoroutineContext* sender,
			Coroutine* oldActive, Coroutine* newActive ) {}
};

/**
 * @brief Get a default coroutine context
 * @return	pointer to the DefaultContext
 * @relates DefaultContext
 */
extern CoroutineContext* getDefaultContext();

/** \class DefaultContext

	\author Ralf Gerstenberger

	\brief Default CoroutineContext for convenience.

	The DefaultContext is provided for convenience. You can
	use this context if you don't want to define your own.

	The DefaultContext is returned by getDefaultContext().

	\since 1.0
*/
class DefaultContext
:	public CoroutineContext
{
private:
	/// Construction only via getDefaultContext
	DefaultContext();
	friend CoroutineContext* getDefaultContext();
};

} } // namespace odemx::coroutine

#endif /* ODEMX_CORO_COROUTINECONTEXT_INCLUDED */
