//----------------------------------------------------------------------------
//	Copyright (C) 2002-2008 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 ResT.cpp

	\author Ronald Kluth

	\date created at 2008/02/18

	\brief Definition of odemx::ResT

	\sa ResT.h

	\since 2.1
*/

#include <odemx/base/Process.h>
#include <odemx/synchronization/res/ResT.h>

#include <vector>

namespace odemx {
namespace synchronization {

template < typename Token >
ResT< Token >::ResT( base::Simulation& sim,
					 const data::Label& label,
					 std::vector< Token >& initialResources,
					 ResTObserver< Token >* o /* = 0*/ )
:	ResTBase< Token >( sim, label, initialResources, o ),
	data::Observable< ResTObserver< Token > >( o )
{
	// trace and stats in base class

	// observer
	ODEMX_OBS_T(ResTObserver<Token>, Create(this));
}

template < typename Token >
ResT<Token>::~ResT()
{
	// observer
	ODEMX_OBS_T(ResTObserver<Token>, Destroy(this));
}

template < typename Token >
Token* ResT< Token >::acquire()
{
	// get a return vector using acquire( n ) below
	std::vector< Token* > v = acquire( 1 );

	// return the first element of the vector
	if( ! v.empty() )
		return v.front();

	// or 0, if nothing was returned (i.e. error or interrupt)
	return 0;
}

template < typename Token >
const std::vector< Token* > ResT< Token >::acquire( std::size_t n )
{
	if( ! getCurrentSched() || getCurrentSched()->getSchedType() != base::Sched::PROCESS )
	{
		// error: resource handling only implemented for processes
		error << this->log( "ResT::acquire(): called by non-Process object" ).scope( typeid(ResT<Token>) );
		return std::vector< Token* >();
	}

	if( n > ResTBase< Token >::tokenLimit )
	{
		warning << this->log( "ResT::acquire(): requesting more than max resources will always block" )
				.scope( typeid(ResT<Token>) );
	}

	base::Process* currentProcess = getCurrentProcess();
	base::SimTime waitTime = 0;

	// compute order of service
	ResTBase< Token >::acquireWait.inSort( currentProcess );

	if( n > ResTBase< Token >::tokenNumber ||
		ResTBase< Token >::acquireWait.getTop() != currentProcess )
	{
		// not enough tokens or not the first process in queue

		ODEMX_TRACE << this->log( "acquire failed" )
						.detail( "partner", currentProcess->getLabel() )
						.detail( "requested tokens", n )
						.scope( typeid(ResT<Token>) );

		// observer
		ODEMX_OBS_T(ResTObserver<Token>, AcquireFail(this, n));

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

		// block execution
		while( n > ResTBase< Token >::tokenNumber ||
			   ResTBase< Token >::acquireWait.getTop() != currentProcess )
		{
			currentProcess->sleep();

			// handle interrupt
			if( currentProcess->isInterrupted() )
			{
				ResTBase< Token >::acquireWait.remove( currentProcess );
				return std::vector< Token* >();
			}
		}

		// block released
		// statistics
		waitTime = getCurrentTime() - waitStart;
	}

	ODEMX_TRACE << this->log( "change token number" ).valueChange( ResTBase<Token>::tokenNumber, ResTBase<Token>::tokenNumber - n )
					.scope( typeid(ResT<Token>) );

	// observer
	ODEMX_OBS_ATTR_T(ResTObserver<Token>, TokenNumber, ResTBase< Token >::tokenNumber, ResTBase< Token >::tokenNumber-n);

	// acquire token
	ResTBase< Token >::tokenNumber -= n;

	// fill return vector with pointers to free resource objects
	typename std::map< Token*, base::Process* >::iterator i;
	std::vector< Token* > availableTokens;

	std::size_t acquired = 0;
	for( i = ResTBase< Token >::tokenInfo.begin();
		 i != ResTBase< Token >::tokenInfo.end() && acquired < n;
		 ++i )
	{
		// if resource available, store the current user in tokenInfo
		// and add the resource to the return vector
		if( i->second == 0 )
		{
			availableTokens.push_back( i->first );
			i->second = currentProcess;
			++acquired;
		}
	}

	ODEMX_TRACE << this->log( "acquire succeeded" )
			.detail( "partner", currentProcess->getLabel() )
			.detail( "requested tokens", n )
			.scope( typeid(ResT<Token>) );

	// statistics
	statistics << this->count( "users" ).scope( typeid(ResT<Token>) );
	statistics << this->update( "tokens", ResTBase< Token >::tokenNumber ).scope( typeid(ResT<Token>) );
	statistics << this->update( "wait time", waitTime ).scope( typeid(ResT<Token>) );

	// observer
	ODEMX_OBS_T(ResTObserver<Token>, AcquireSucceed(this, n));

	// remove process from queue
	ResTBase< Token >::acquireWait.remove( currentProcess );

	// awake next waiting process
	awakeFirst( ResTBase< Token >::getAcquireWait() );

	// return tokens
	return availableTokens;
}

//-----------------------------------------------------------------------helpers

//template < typename Token >
//base::SimTime ResT< Token >::getCurrentTime()
//{
//	return data::Producer::getSimulation().getTime();
//}
//
//template < typename Token >
//odemx::base::Process* ResT< Token >::getCurrentProcess()
//{
//	return data::Producer::getSimulation().getCurrentProcess();
//}
//
//template < typename Token >
//odemx::base::Sched* ResT< Token >::getCurrentSched()
//{
//	return data::Producer::getSimulation().getCurrentSched();
//}

} } // namespace odemx::sync
