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

	\author Ronald Kluth

	\date created at 2007/04/04

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

	\sa Memory.cpp

	\since 2.0
*/

#ifndef ODEMX_MEMORY_INCLUDED
#define ODEMX_MEMORY_INCLUDED

#include <odemx/data/Producer.h>
#include <odemx/base/TypeDefs.h>
#include <odemx/synchronization/IMemory.h>
#include <odemx/data/Observable.h>

#include <list>
#include <vector>

namespace odemx {

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

namespace synchronization {

class MemoryObserver;

/** \class Memory

	\ingroup synch

	\author Ronald Kluth

	\brief The base class for all structures that need to remember processes

	\sa Process, Timer, PortHead, PortTail, WaitCondition

	%Memory is an abstract base class for all user defined structures
	in a model that need to store a list of suspended processes in order to wake
	them when a certain event occurs. This can be the case for timers, but also
	for structures like buffered communication ports. %Memory offers a
	means to notify suspended processes of the availability of a resource and
	reschedule them at that point in simulation time when a resource becomes
	available or a timeout occurs. The recommended usage with processes is
	via their \p wait() function.

	\since 2.0
*/
class Memory
:	public IMemory // implements the memory interface
,	public data::Producer
,	public data::Observable< MemoryObserver >
{
public:
	friend class base::Process;

	/// Same type as the STL container's size_type
	typedef base::SchedList::size_type SizeType;

	/**
	 * @brief Construction for user-defined Simulation
	 *
	 * @param sim Reference to the simulation context
	 * @param label The object's label
	 * @param type Type of the memory object
	 * @param obs Pointer to the initial observer of the object
	*/
	Memory( base::Simulation& sim, const data::Label& label, Type type,
			MemoryObserver* obs = 0 );

	/// Destruction
	virtual ~Memory();

	/**
	 * @name Process interaction
	 *
	 * These functions can be used by processes to interact with
	 * Memory objects.
	 * @{
	 */
	/**
	 * @brief Remember a new schedulable object
	 *
	 * @param newObject Pointer to a schedulable object to be stored in memory
	 *
	 * @return @c true when successfully stored, @c false if @c newObject is
	 * already stored
	 *
	 * This function is used to add processes or, more generally,
	 * schedulable objects to the internal list.
	 *
	 * @sa forget()
	 */
	virtual bool remember( base::Sched* newObject );

	/**
	 * @brief Remove a stored schedulable object
	 *
	 * @param rememberedObject Pointer to the object to be removed
	 *
	 * @return @c true when successfully removed, @c false if @c rememberedObject
	 * not found in list
	 *
	 * This function is used to remove processes or, more generally,
	 * schedulable objects from the internal list.
	 *
	 * @sa remember()
	 */
	virtual bool forget( base::Sched* rememberedObject );

	/**
	 * @brief Check if a certain process is waiting for this memory object.
	 *
	 * @return @c true if the process is waiting, @c false otherwise
	 *
	 * @sa remember(), forget()
	 */
  bool processIsWaiting (base::Process& process) const;

	/**
	 * @brief Clear the internal list
	 *
	 * This function is used to remove all processes or, more generally,
	 * schedulable objects from the internal list.
	 *
	 * @sa remember(), forget()
	 */
	virtual void eraseMemory();
	//@}

	/**
	 * @name Memory status information
	 *
	 * These functions return information about the Memory object.
	 * @{
	 */
	/**
	 * @brief Check availability of an Memory object
	 *
	 * @return @c true if the memory object is available, @c false otherwise
	 *
	 * This virtual function should be implemented by all
	 * subclasses of %Memory. It is used to check the availability
	 * of a	resource before suspending the requesting process.
	 *
	 * @note Memory objects may always return false (like the default
	 * implementation) if they use some other means to alert and reschedule
	 * processes.
	 *
	 * @sa Process::wait(), PortHead, PortTail, Timer, WaitCondition
	 */
	virtual bool isAvailable();

	/// Check if this Memory object has waiting processes
	bool waiting() const;

	/// Get the number of waiting processes
	SizeType countWaiting() const;

	/// Get a reference to the list of waiting objects
	const base::SchedList& getWaiting() const;

	/**
	 * @brief Check the type of this Memory object
	 *
	 * The memory type is useful when a Memory pointer is returned
	 * but, depending on the type of this object, different reactions are
	 * required in the simulator. For example, it is quite a difference if
	 * a timer is available becuase it was not or a communication port because
	 * it contains data.
	 *
	 * @sa Process::wait()
	 */
	virtual Type getMemoryType() const;
	//@}

	/// Required implementation, simply calls <tt>alert( this )</tt>
	virtual void alert();

	/**
	 * @brief Alert and reschedule all stored schedulable objects
	 * @param caller Pointer to an IMemory object to be passed as process alerter
	 *
	 * By default, this function is designed to wake up suspended
	 * or newly created processes, i.e. reschedule them at the current
	 * simulation time. In case an IMemory interface is implemented in terms
	 * a Memory member, it makes sense to pass the owner as alerter in
	 * order to be able to check against the owner's address instead of the
	 * member's.
	 */
	void alert( IMemory* caller );


protected:
	/**
	 * @brief Helper for alert() which tests if object @c s is a sleeping Process
	 * @return @c true if object @c s can be alerted, @false otherwise
	 */
	bool checkSchedForAlert( base::Sched* s );

private:
	/// The object's memo type
	Type memoryType_;
	/// Storage for pointers to schedulable objects
	base::SchedList memoryList_;

	/// Helper to log an alert record with details of the internal memory list
	void traceAlert();
};

/** \interface MemoryObserver

	\author Ronald Kluth

	\brief Observer for Memory-specific calls.

	\sa Timer, PortHead, PortTail

	\since 2.0
*/
class MemoryObserver
{
public:
	virtual ~MemoryObserver() {}

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

	/// Observe addition of a new object to the memory
	virtual void onRemember( Memory* sender, base::Sched* s ) {}
	/// Observe removal of a previously stored object from the memory
	virtual void onForget( Memory* sender, base::Sched* s ) {}
	/// Observe alert of all stored schedulable objects
	virtual void onAlert( Memory* sender ) {}
};

} } // namespace odemx::synchronization

#endif /*ODEMX_MEMORY_INCLUDED*/
