//------------------------------------------------------------------------------
//	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.cpp
 * @author Ralf Gerstenberger
 * @date created at 2002/02/04
 * @brief Implementation of odemx::coroutine::CoroutineContext and DefaultContextutine
 * @sa CoroutineContext.h
 * @since 1.0
 */

#if defined(_MSC_VER) && defined(ODEMX_USE_SETJUMP)
#pragma warning(disable : 4530)
#endif

#include <odemx/coroutine/CoroutineContext.h>
#include <odemx/coroutine/Coroutine.h>

#include <memory.h> // memcpy
#include <stdexcept>

namespace odemx {
namespace coroutine {

//------------------------------------------------------construction/destruction

CoroutineContext::CoroutineContext( CoroutineContextObserver* o )
:	data::Observable< CoroutineContextObserver >( o )
,	activeCoroutine( 0 )
,	numberOfCoroutines( 0 )
{
	ODEMX_OBS(CoroutineContextObserver, Create(this));

	fiber = 0;
}

CoroutineContext::~CoroutineContext()
{
	ODEMX_OBS(CoroutineContextObserver, Destroy(this));

	// check number of coroutines ?!?
	// when the context is destroyed, the program should be finished anyway...

}

//-------------------------------------------------------------switch to context

void CoroutineContext::switchTo()
{
	ODEMX_OBS(CoroutineContextObserver, SwitchTo(this, getActiveCoroutine()));

	if( getActiveCoroutine() == 0 )
	{
		return; // ignore self-switch
	}

	setActiveCoroutine( 0 );
	FiberSwitch( fiber );
}

//-----------------------------------------------------------------------getters

Coroutine* CoroutineContext::getActiveCoroutine()
{
	return activeCoroutine;
}

unsigned int CoroutineContext::getNumberOfCoroutines()
{
	return numberOfCoroutines;
}

//--------------------------------------------------------------active coroutine

Coroutine* CoroutineContext::setActiveCoroutine( Coroutine* c )
{
	Coroutine* oldActiveCoroutine = activeCoroutine;
	activeCoroutine = c;

	ODEMX_OBS_ATTR(CoroutineContextObserver, ActiveCoroutine,
			oldActiveCoroutine, activeCoroutine);

	return oldActiveCoroutine;
}

//---------------------------------------------------coroutine (de-)registration

void CoroutineContext::beginCoroutine( Coroutine* cr )
{
	assert( cr->getContext() == this );
	++numberOfCoroutines;
}

void CoroutineContext::endCoroutine( Coroutine* cr )
{
	assert( cr->getContext() == this );
	--numberOfCoroutines;
}

//-----------------------------------------------------------------fiber storage

void CoroutineContext::saveFiber()
{
	// Initialize Fibers
	if( initFibers )
	{
		ConvertThreadToFiber( 0 );
		initFibers = false;
	}

	// get context
	fiber = GetCurrentFiber();

	if( fiber == 0 )
	{
		throw std::runtime_error(
				"CoroutineContext::saveFiber(): could not retrieve active fiber" );
	}
}

bool CoroutineContext::initFibers = true;

//---------------------------------------------------------------default context

/** \brief Get a default coroutine context
		\return pointer to the DefaultContext
		\relates DefaultContext
*/
CoroutineContext* getDefaultContext()
{
	static DefaultContext defaultContext;
	return &defaultContext;
}

DefaultContext::DefaultContext()
:	CoroutineContext()
{
}

} } // namespace odemx::base
