//------------------------------------------------------------------------------
//	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 Coroutine.h
 * @author Ralf Gerstenberger
 * @date created at 2002/01/22
 * @brief Declaration of odemx::coroutine::Coroutine and observer
 * @sa Coroutine.cpp
 * @since 1.0
 */

#ifndef ODEMX_CORO_COROUTINE_INCLUDED
#define ODEMX_CORO_COROUTINE_INCLUDED

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

namespace odemx {
namespace coroutine {

// forward declaration
class CoroutineObserver;

/** \class Coroutine

	\ingroup coroutine

	\author Ralf Gerstenberger

	\brief Coroutine implements a function capable of switching execution to and back from another Coroutine inside function body.

	\note Coroutine supports Observation.

	Coroutine is a system dependable but portable coroutine implementation. A coroutine is
	a function capable of switching execution to a point inside another coroutine and back to
	a point inside its own function body.
	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) and 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.

	\warning Sun (Ultra)Sparc platform has a circular stack and is not supported at the moment.
	\warning IA-64 platform stores the return address of a function in a special branch register; not supported.

	\since 1.0
*/
class Coroutine
:	public data::Observable< CoroutineObserver >
{
public:
	/**
		\brief Coroutine states

		A Coroutine transits through 3 states during its lifetime.
		After creation it is in state CREATED. First time the Coroutine
		is activated it transits to RUNNABLE. When Coroutine returns
		it becomes TERMINATED.
	*/
	enum State
	{
		CREATED, ///< initial state
		RUNNABLE, ///< working state
		TERMINATED ///< final state
	};

	/**
		\brief start/continue execution of this Coroutine.
		\note The Coroutine will become the active Coroutine in its CoroutineContext.
		\note There is no more than one Coroutine active in its context at any time.
	*/
	void switchTo();

	/**
		\copydoc Coroutine::switchTo
	*/
	void operator()();

	/// Returns control to the parent coroutine, or - if none exists - to the context
	void yield();
	
public:
	/**
		\brief Construction
		\param c
			pointer to CoroutineContext of this Coroutine
			(if c==0 a default CoroutineContext is used)
		\param o
			pointer to a CoroutineObserver

		\sa DefaultContext
	*/
	Coroutine( CoroutineContext* c = 0, CoroutineObserver* o = 0 );

	virtual ~Coroutine(); ///< Destruction

	/**
		\brief coroutine state
		\return current state of Coroutine
		\sa Coroutine::State
	*/
	State getState() const;

	/**
		\brief get CoroutineContext of Coroutine
		\return CoroutineContext of this Coroutine
	*/
	CoroutineContext* getContext();

	/**
		\brief parent of coroutine
		\return parent (first caller) of coroutine
		\retval 0
			parent is CoroutineContext
		\retval !=0
			pointer to parent coroutine
	*/
	Coroutine* getParent();

	/**
		\brief last caller
		\return last caller of coroutine
		\retval 0
			last caller is CoroutineContext
		\retval !=0
			pointer to coroutine
	*/
	Coroutine* getCaller();

  /// @todo private & Scheduler as friend? -- only Scheduler will call this method!
  void freeStack ();

protected:
	// Coroutine entry point
	virtual void run() = 0; ///< define this method to implement Coroutine behaviour
	void clear(); ///< called after execution finished in switchTo(), or in destructor

	// Implementation
private:
	CoroutineContext* context; ///< CoroutineContext of this Coroutine
	Coroutine* parent; ///< return point after start() finished
	Coroutine* caller; ///< last caller of coroutine

	State state; ///< Coroutine state

	void initialize(); ///< called before first execution

	State setState( State newState ); ///< state transitions

	// System dependent
	/// \name Fiber based implementation
	//@{
	LPVOID myFiber;
	LPVOID fiber;
	//@}

	/// \name Fiber based implementation
	//@{
	/// prepare execution switch (save active fiber).
	void saveFiber();
	//@}

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

};

/** \interface CoroutineObserver

	\author RalfGerstenberger

	\brief Observer for Coroutine specific events.

	\sa Coroutine

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

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

	virtual void onInitialize( Coroutine* sender ) {} ///< initialisation
	virtual void onClear( Coroutine* sender ) {} ///< Coroutine is cleared

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

	/// state transition
	virtual void onChangeState( Coroutine* sender, Coroutine::State oldState,
			Coroutine::State newState ) {}
};

/**
	\internal
	\brief Fiber start function
	\param p
		pointer to a Coroutine
*/
VOID CALLBACK ExecFiber( PVOID p );

/**
	\internal
	\brief encapsulates SwitchToFiber() call
	\param fiber
		pointer to fiber
*/
void FiberSwitch( LPVOID fiber );

} } // namespace odemx::coroutine

#endif /* ODEMX_CORO_COROUTINE_INCLUDED */
