//----------------------------------------------------------------------------
//	Copyright (C) 2002, 2004 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 CondQ.cpp

	\author Ralf Gerstenberger

	\date created at 2002/07/11

	\brief Implementation of odemx::sync::CondQ

	\sa CondQ.h

	\since 1.0
*/

#include <odemx/synchronization/CondQ.h>
#include <odemx/base/Process.h>
#include <odemx/base/Simulation.h>

#include <cassert>
#include <string>

namespace odemx {
namespace synchronization {

CondQ::CondQ( base::Simulation& sim, const data::Label& label, CondQObserver* obs )
:	data::Producer( sim, label )
,	data::Observable< CondQObserver >( obs )
,	processes_( sim, getLabel() + ".queue" )
{
	ODEMX_TRACE << log( "create" ).scope( typeid(CondQ) );

	statistics << param( "queue", processes_.getLabel() ).scope( typeid(CondQ) );

	// observer
	ODEMX_OBS(CondQObserver, Create(this));
}

CondQ::~CondQ()
{
	ODEMX_TRACE << log( "destroy" ).scope( typeid(CondQ) );
}

bool CondQ::wait( base::Condition cond )
{
	// resource handling only implemented for processes
	if( ! getCurrentSched()	|| getCurrentSched()->getSchedType() != base::Sched::PROCESS )
	{
		error << log( "CondQ::wait(): called by non-Process object" )
				.scope( typeid(CondQ) );
		return false;
	}

	base::Process* currentProcess = getCurrentProcess();

	ODEMX_TRACE << log( "wait" ).detail( "partner", currentProcess->getLabel() )
			.scope( typeid(CondQ) );

	// observer
	ODEMX_OBS(CondQObserver, Wait(this, currentProcess));

	// wait for condition to become true
	processes_.inSort( currentProcess );

	// statistics
	base::SimTime waitStart = getTime();

	// check condition and block
	while( ! ( currentProcess->*cond )() )
	{
		currentProcess->sleep();

		if( currentProcess->isInterrupted() )
		{
			processes_.remove( currentProcess );

			return false;
		}
	}
	// block released

	processes_.remove( currentProcess );

	ODEMX_TRACE << log( "continue" ).detail( "partner", currentProcess->getLabel() )
			.scope( typeid(CondQ) );

	// statistics
	base::SimTime waitTime = getTime() - waitStart;
	statistics << update( "wait time", waitTime ).scope( typeid(CondQ) );
	statistics << count( "users" ).scope( typeid(CondQ) );

	// observer
	ODEMX_OBS(CondQObserver, Continue(this, currentProcess));

	return true;
}

void CondQ::signal()
{
	base::Process* current = getCurrentProcess();

	ODEMX_TRACE << log( "signal" )
			.detail( "partner", ( current ? current->getLabel() : "none" ) )
			.scope( typeid(CondQ) );

	// observer
	ODEMX_OBS(CondQObserver, Signal(this, current));

	statistics << count( "signals" ).scope( typeid(CondQ) );

	if( processes_.isEmpty() )
	{
		return;
	}

	// test conditions
	awakeAll( &processes_ );
}

const base::ProcessList& CondQ::getWaitingProcesses() const
{
	return processes_.getList();
}

base::SimTime CondQ::getTime() const
{
	return getSimulation().getTime();
}

base::Sched* CondQ::getCurrentSched()
{
	return getSimulation().getCurrentSched();
}

base::Process* CondQ::getCurrentProcess()
{
	return getSimulation().getCurrentProcess();
}

} } // namespace odemx::sync
