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

	\author Ronald Kluth

	\date created at 2008/02/18

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

	\note This header file is not meant for inclusion, use
	odemx/synchronization/ResTemplate.h instead.

	\sa ResTBase.cpp

	\since 2.1
*/

#ifndef ODEMX_RES_T_BASE_INCLUDED
#define ODEMX_RES_T_BASE_INCLUDED

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


#include <set>
#include <map>

namespace odemx {

//forward declaration
namespace base {
class Process;
class Simulation;
}

namespace synchronization {

template < typename > class ResTBaseObserver;

/** \class ResTBase

	\author Ronald Kluth

	\brief ResTBase is an abstract base class for resources holding
	objects of type Token

	\note ResTBase supports Observation
	\note ResTBase supports Trace

	\sa Res, ResT, ResTChoice

	ResTBase provides many functions needed in resource classes
	for synchronizing Process objects.

	\since 2.1
*/
template < typename TokenT >
class ResTBase
:	public data::Producer
,	public data::Observable< ResTBaseObserver< TokenT > >
{
public:

	using data::Producer::trace;
	using data::Producer::debug;
	using data::Producer::info;
	using data::Producer::warning;
	using data::Producer::error;
	using data::Producer::fatal;
	using data::Producer::statistics;

	typedef TokenT TokenType;
	typedef std::vector< TokenType > TokenVec;
	typedef std::map< TokenType*, base::Process* > TokenOwnerMap;
	typedef std::list< TokenType > StorageType;
	typedef ResTBaseObserver< TokenType > ObserverType;

	/**
		\brief Construction for user-defined Simulation
		\param s
			pointer to simulation
		\param l
			label of ResT object
		\param initialResources
			vector containing the initial tokens of the resource
		\param o
			observer of ResT object

		\note The Token type must be copyable! The initial resources will
		be copied into the internal token store of the object.

		\note The token limit is set by the size of the \c initialResources
		vector. The change it, use control(), or unControl().
	*/
	ResTBase( base::Simulation& sim, const data::Label& label,
			TokenVec& initialResources, ObserverType* obs = 0 );

	/// Destruction
	virtual ~ResTBase();

	/**
		\name Token usage

		These functions provide access for resource usage by Processes.
		Tokens can be requested and thus acquired when then are
		available. When the tokens are not needed anymore, the owning
		Process	can release them.

		\note There are different ways to select tokens,
		this base class only provides a declaration for acquire.

		\note Use the derived classes ResT or ResTChoice
		@{
	*/
	/**
		\brief forces implementation of acquire

		Pure virtual function which must be implemented in
		derived classes.
	*/
	virtual TokenType* acquire() = 0;

	/**
		\brief release a token

		Returns a previously acquired token to the resource
	*/
	void release( TokenType* e );

	/**
		\brief release token

		Returns previously acquired tokens to the resource
	*/
	void release( std::vector< TokenType* >& s );

	/**
		\brief transfer control of an acquired token to \p newOwner

		The \p newOwner is registered as holder of token \p e, meaning
		this info is changed in the internal token info map.
	*/
	void transfer( TokenType* t, base::Process* oldOwner,
			base::Process* newOwner );

	/**
		\brief transfer control of acquired token to \p newOwner

		The \p newOwner is registered as holder of the tokens in \p s.
	*/
	void transfer( std::vector< TokenType* >& v, base::Process* oldOwner,
			base::Process* newOwner);

	/**
		\brief add token \p e to ressource

		Add token \p e to the managed token set.
	*/
	void control( TokenType t );

	/**
		\brief add token to ressource

		Add token to the managed token set.
	*/
	void control( TokenVec& addVec );

	/**
		\brief remove \p n token from ressource

		Remove token from the managed token set. If there are not enough
		token left in the ressource, the current process is blocked.
	*/
	TokenVec unControl( std::size_t n = 1 );
	//@}

	/** \name ResT status information

		The following functions provide access to the ResT object's
		status during a simulation.

		@{
	*/
	/**
		\brief get number of available token

		Get number of currently avaliable token in ressource
	*/
	std::size_t getTokenNumber() const;

	/**
		\brief get number of managed token

		Get maximum number of tokens managed by this ressource
	*/
	std::size_t getTokenLimit() const;

	/**
		\brief Get token map

		Get info about all tokens and their owning processes.
	*/
	const TokenOwnerMap& getTokenInfo() const;

	/// Get a reference to the stored tokens
	const StorageType& getTokenStore() const;

	/// get list of blocked  process objects
	const std::list< base::Process* >& getWaitingProcesses() const;
	//@}

protected:

	/// current time
	base::SimTime getCurrentTime();
	/// current process
	base::Process* getCurrentProcess();
	/// current scheduled object
	base::Sched* getCurrentSched();
	/// get pointer to queue of processes waiting in acquire
	Queue* getAcquireWait();
	/// get label of the currently active context (process or sim)
	const data::Label& getPartner();
	/// current number of tokens
	std::size_t tokenNumber;
	/// maximum number of tokens
	std::size_t tokenLimit;

	/// token storage
	StorageType tokenStore;
	/// token management
	TokenOwnerMap tokenInfo;
	/// process management
	Queue acquireWait;
};

/** \interface ResTBaseObserver

	\author Ronald Kluth

	\brief Observer for ResT specific events

	\sa ResT
*/
template < typename Token >
class ResTBaseObserver
{
public:
	typedef ResTBase< Token > ResType;

	virtual ~ResTBaseObserver() {}

	/// Creation
	virtual void onCreate(ResType* sender) {}
	/// Destruction
	virtual void onDestroy(ResType* sender) {}

	/// Failed attempt to release n token
	virtual void onReleaseFail(ResType* sender, std::size_t n) {}
	/// Release of n token
	virtual void onReleaseSucceed(ResType* sender, std::size_t n) {}
	/// Transfer of n token
	virtual void onTransfer(ResType* sender, std::size_t n,
			base::Process* oldOwner, base::Process* newOwner) {}
	/// Add tokens
	virtual void onControl(ResType* sender, std::size_t n) {}
	/// Remove tokens
	virtual void onUnControl(ResType* sender, std::size_t n) {}
	/// Change of token number
	virtual void onChangeTokenNumber(ResType* sender,
			std::size_t oldTokenNumber, std::size_t newTokenNumber) {}
};

} } // namespace odemx::synchronization

#endif /*ODEMX_RES_T_BASE_INCLUDED*/

