//------------------------------------------------------------------------------
//	Copyright (C) 2008, 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 BinTImpl.h
 * @author Ronald Kluth
 * @date created at 2008/02/18
 * @brief Declaration of odemx::synchronization::BinT and observer
 * @note Do not include this header directly,
 * use odemx/synchronization/BinTemplate.h instead.
 * @sa BinTImpl.cpp
 * @since 2.1
 */

#ifndef ODEMX_SYNC_BINT_IMPL_INCLUDED
#define ODEMX_SYNC_BINT_IMPL_INCLUDED

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

#include <deque>
#include <memory>
#include <vector>

namespace odemx {

// forward declarations
namespace base {
class Process;
class Sched;
class Simulation;
}

namespace synchronization {

template < typename TokenT > class BinTObserver;

/** \class BinT

	\author Ralf Gerstenberger

	\brief BinT is a resource holding objects of type T.

	\note BinT supports Observation

	\note BinT supports Trace

	BinT is a low bounded (token number >= 0) resource for synchronizing
	Process objects. A process is blocked in take()	if the requested number
	of tokens is greater than the available tokens. It is reactivated
	when enough tokens are given (back) to the resource. If multiple process
	objects are waiting for reactivation process-priority and FIFO-strategy
	are used to choose the process.	BinT is generally used for producer and
	consumer synchronization.

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

	typedef TokenT TokenType;
	typedef std::vector< TokenType > TokenVec;
	typedef std::deque< TokenType > StorageType;
	typedef BinTObserver< TokenType > ObserverType;

	/**
		\brief Construction for user-defined Simulation, no initial tokens
		\param s
			pointer to simulation
		\param l
			label of BinT object
		\param o
			observer of BinT object
	*/
	BinT( base::Simulation& sim, const data::Label& label, ObserverType* obs = 0 );

	/**
		\brief Construction for user-defined Simulation
		\param s
			pointer to simulation
		\param l
			label of BinT object
		\param o
			observer of BinT object
	*/
	BinT( base::Simulation& sim, const data::Label& label, TokenVec& initialTokens,
			ObserverType* obs = 0 );

	/// Destruction
	virtual ~BinT();

	/**
		\name Token management

		@{
	*/
	/**
		\brief take \p n tokens

		Takes n tokens from BinT if possible. Otherwise, the
		current process is blocked until enough tokens become
		available.

		\note The returned vector is dynamically allocated with \c new,
		which means that the caller has to call delete on it.
	*/
	std::auto_ptr< TokenVec > take( std::size_t n );

	void give( const TokenType& token );

	/**
		\brief give \p n token back

		Gives n tokens to BinT. Blocked process objects
		could be activated by this call.
	*/
	void give( const TokenVec& tokens );

	/// Number of tokens available
	std::size_t getTokenNumber() const;

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

	/// get list of blocked  process objects
	const base::ProcessList& getWaitingProcesses() const;

private:
	/// token storage
	StorageType tokenStorage_;
	/// token count
	std::size_t tokenCount_;
	/// process management queue
	Queue takeQueue_;

	/// Helper for quick access to the current simulation time
	base::SimTime getCurrentTime() const;
	/// Helper for quick access to the currently running object
	base::Sched* getCurrentSched();
	/// Helper for quick access to the current process
	base::Process* getCurrentProcess();
};

/** \interface BinTObserver

	\author Ralf Gerstenberger

	\brief Observer for BinT specific events

	\sa BinT

	\since 2.1
*/
template < typename TokenT >
class BinTObserver {
public:

	typedef BinT< TokenT > BinType;

	virtual ~BinTObserver() {}

	/// Creation
	virtual void onCreate( BinType* sender ) {}

	/// Failed attempt to take n token
	virtual void onTakeFail( BinType* sender, std::size_t n ) {}
	/// Successfull attempt to take n token
	virtual void onTakeSucceed( BinType* sender, std::size_t n ) {}
	/// Return of n token
	virtual void onGive( BinType* sender, std::size_t n ) {}

	/// Change of token number
	virtual void onChangeTokenNumber( BinType* sender, std::size_t oldTokenCount,
		std::size_t newTokenCount ) {}
};

} } // namespace odemx::synchronization

#endif /* ODEMX_SYNC_BINT_IMPL_INCLUDED */
