//----------------------------------------------------------------------------
//	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 Res.h

	\author Ralf Gerstenberger

	\date created at 2002/03/21

	\brief Declaration of odemx::synchronization::Res and observer

	\sa Res.cpp

	\since 1.0
*/

#ifndef ODEMX_RES_INCLUDED
#define ODEMX_RES_INCLUDED

#include <odemx/data/Producer.h>
#include <odemx/synchronization/Queue.h>
#include <odemx/data/Observable.h>

namespace odemx {
namespace synchronization {

// forward declaration
class ResObserver;

/** \class Res

	\ingroup synch

	\author Ralf Gerstenberger

	\brief %Res is a double (n>=0 && n<=max number of token) bounded
	token abstract resource.

	\note Res supports Observation
	\note Res supports Trace
	\note Res supports Report

	\sa ResT, ResTChoice

	Res is a double bounded (n>=0 && n<=max number of token) token abstract
	resource for synchronising Process objects. A process is blocked if
	it tries to acquire more token than are left. It is reactivated when
	enough token are released or acquired. If multiple processes are waiting
	for reactivation process-priority and FIFO-strategy are used to
	choose the process.
	Res is generally used for strong limited resource modelling or producer
	consumer synchronisation with limited storage capacity.

	\since 1.0
*/
class Res
:	public data::Producer
,	public data::Observable< ResObserver >
{
public:

	/**
		\brief Construction for user-defined Simulation
		\param s
			pointer to Simulation object
		\param l
			label of this object
		\param startTokenNumber
			initial number of token in Res
		\param maxTokenNumber
			max number of token in Res
		\param o
			initial observer
	*/
	Res( base::Simulation& sim, const data::Label& label, std::size_t initialTokens,
			std::size_t maxTokens, ResObserver* obs = 0 );

	/// Destruction
	~Res();

	/**
		\name Token management
		@{
	*/
	/**
		\brief Acquire \p n token

		If there aren't enough token in Res the current
		process is blocked. If a blocked process is interrupted,
		it is reactivated and acquire() returns 0. Otherwise the
		function returns \p n.
	*/
	std::size_t acquire( std::size_t n );

	/**
		\brief Release \p n token

		Returns \p n token to resource. If there is not enough
		room in this resource left an error message is created.
	*/
	void release( std::size_t n );


	/**
		\brief Add token to resource

		Add token to the managed token set.
	*/
	void control( std::size_t n );

	/**
		\brief Remove \p n token from resource

		Remove token from the managed token set. If there are not enough
		token left in the resource, the current process is blocked. When
		a blocked process is interrupted, the attempt to take token is
		cancelled and the function returns 0.
	*/
	std::size_t unControl( std::size_t n );

	/// Current number of token available
	std::size_t getTokenNumber() const;

	/// Maximum number of token
	std::size_t getTokenLimit() const;
	//@}

	/// Get list of blocked processes
	const base::ProcessList& getWaitingProcesses() const;

private:
	/// current number of tokens
	std::size_t tokens_;
	/// max number of tokens
	std::size_t tokenLimit_;
	/// process management
	Queue acquireQueue_;

	/// Helper for quick access to the current simulation time
	base::SimTime getTime() const;
	/// Helper for quick access to the currently running object
	base::Sched* getCurrentSched();
	/// Helper for quick access to the current process
	base::Process* getCurrentProcess();
	/// Get the label of the currently active context (process or sim)
	const data::Label& getPartner();
};

/** \interface ResObserver

	\author Ralf Gerstenberger

	\brief Observer for Res specific events

	\sa Res

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

	/// Observe construction
	virtual void onCreate( Res* sender ) {}

	/// Observe blocking of acquire
	virtual void onAcquireFail( Res* sender, std::size_t n ) {}
	/// Observe successful acquiration of n token
	virtual void onAcquireSucceed( Res* sender, std::size_t n ) {}
	/// Blocking release of n token
	virtual void onReleaseFail( Res* sender, std::size_t n ) {}
	/// Successful release of n token
	virtual void onReleaseSucceed( Res* sender, std::size_t n ) {}
	/// Increased token limit
	virtual void onControl( Res* sender, std::size_t n ) {}
	/// Decreased toekn limit
	virtual void onUnControl( Res* sender, std::size_t n ) {}
	/// Change of token number
	virtual void onChangeTokenNumber( Res* sender, std::size_t oldTokenNumber,
			std::size_t newTokenNumber ) {}
};

} } // namespace odemx::synchronization

#endif

