//------------------------------------------------------------------------------
//	Copyright (C) 2007, 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 Event.h
 * @author Ronald Kluth
 * @date created at 2007/04/04
 * @brief Declaration of odemx::base::Event and observer
 * @sa Event.cpp
 * @since 2.0
 */

#ifndef ODEMX_BASE_EVENT_INCLUDED
#define ODEMX_BASE_EVENT_INCLUDED

#include <odemx/base/Sched.h>
#include <odemx/data/Observable.h>
#include <odemx/util/attributes.h>

namespace odemx {
namespace base {

// forward declaration
class Simulation;
class EventObserver;

/** \class Event

	\ingroup base

	\author Ronald Kluth

	\brief %Event is the base class for all user-defined events in a model.

	\note %Event supports Observation.
	\note %Event supports Trace.

	\sa Process, Simulation

	%Event is the base class for all user defined events in a model. Events,
	unlike processes do not have a life cycle consisting of a chain of actions.
	An event is a single action that changes the state of the system model.
	It can, however be executed more than once.	A user
	creates a derived class for every different type of event of their model.
	In their own classes users have to provide the event action by implementing
	the eventAction() method. The event action is executed according to an
	execution schedule (ExecutionList) at a certain point in simulation time.
	If multiple scheduled objects share the same execution time, the order of
	execution is defined by	the way	the processes and/or events have been
	scheduled. These objects can be scheduled with respect to their priority
	First-In-First-Out or Last-In-First-Out at a point in time.

	\since 2.0
*/
class Event
:	public Sched // is DataProducer, etc.
,	public data::Observable< EventObserver >
{
public:

	/**
		\brief Construction for user-defined Simulation
		\param sim
			Reference to the simulation object
		\param label
			Label of this object
		\param obs
			Initial observer
	*/
	Event( Simulation& sim, const data::Label& label, EventObserver* obs = 0 );

	/// Destruction
	virtual ~Event();


	/**
		\name Event Scheduling

		These functions are used to schedule a simulation event
		in simulation time. Events and processes are kept in one
		schedule and follow the same scheduling rules.

		@{
	*/
	/**
		\brief Trigger the event at current simulation time

		This function schedules the event at the current time before
		all other processes and events with the same priority (LIFO). Unlike
		high-priority processes, scheduling high-priority events at
		SimTime now will not suspend the execution of the current process
		- the process can either finish or reschedule itself, after which
		the event will then be triggered.
	*/
	void schedule();

	/**
		\brief Trigger the event in (relative) time \p t
		\param t
			delay for activation

		This function schedules the event at the simtime \c now + \p t
		before all other processes with the same priority (LIFO). If
		\p t is 0 this function is equal to schedule().

		\sa schedule()
	*/
	void scheduleIn( SimTime t );

	/**
		\brief Trigger the event at (absolute) time \p t
		\param t
			time for activation

		This function schedules the event at the simtime \p t
		before all other processes with the same priority (LIFO). If
		\p t is \c now this function is equal to schedule().

		\sa schedule()
	*/
	void scheduleAt( SimTime t ); // schedule at absolute simulation time t

	/**
		\brief Trigger the event at current simulation time (FIFO)

		This function schedules the event at the current time
		after all other scheduled objects with the same priority.
	*/
	void scheduleAppend();

	/**
		\brief Trigger the event in (relative) time \p t (FIFO)

		This function schedules the event at the time \c now + \p t
		after all other scheduled objects with the same priority. If \p t
		is 0 this function is equal to scheduleAppend().

		\sa scheduleAppend()
	*/
	void scheduleAppendIn( SimTime t );

	/**
		\brief Trigger the event at (absolute) time \p t (FIFO)

		This function schedules the event at simtime \p t
		after all other scheduled objects with the same priority. If \p t
		is \c now this function is equal to scheduleAppend().

		\sa scheduleAppend()
	*/
	void scheduleAppendAt( SimTime t );

	/**
		\brief Remove the event from the execution list

		This function finds this event in the execution list
		and deletes the entry.
	*/
	void removeFromSchedule();
	//@}

	/**
		\name Priority

		These functions can be used to adjust the priority of the event.
		A priority change causes an update of the execution list as scheduled
		objects may switch positions. By default, events have the same
		priority as processes.

		@{
	*/
	/**
		\brief Get priority
		\return
			event priority

		This function returns the current event priority.
	*/
	virtual Priority getPriority() const;

	/**
		\brief Set new priority
		\return
			previous event priority

		This function changes the event priority. The position
		in the execution schedule and in queues is affected by
		priority changes.
	*/
	virtual Priority setPriority( Priority newPriority );
	//@}

	/**
		\name Execution Time

		@{
	*/
	/**
		\brief Get execution time
		\return
			execution time; 0 if the event is not scheduled

		This function returns the execution time of the event.
		An event is scheduled to be triggered at its execution time.
	*/
	SimTime getExecutionTime() const;
	//@}

	/**
		\name Current System State

		@{
	*/
	/**
		\brief Get current simulation time
		\deprecated substituted by Sched::getTime ()
		\return
			current simulation time

		This function returns the current simulation time.
	*/
	DEPRECATED(SimTime getCurrentTime());
	//@}

	/** \cond PLEASE_DELETE_THIS_AND_CLOSE_#41
		\brief Get pointer to Trace
		\return
			pointer to Trace object
	*/
	//virtual data::Trace* getTrace() const;
	// \endcond

protected:
	/**
		\brief User defined event action

		This function must be implemented by the user to define the
		action the event triggers. It is executed by ODEMx when the event
		becomes the first, i.e. current, element in the schedule.
		If a scheduling	function is called (directly or indirectly,
		on itself or another process/event) the eventAction() function will still
		finish, unlike processes, which would then be suspended.
		The sequence in which different	processes and events are executed
		is determined by the execution schedule.

		\sa Process Scheduling
	*/
	virtual void eventAction() = 0;

private:

	friend class ExecutionList;

	/**
		\brief Set execution time
		\return
			previous execution time

	 	This function is used by the library. Don't
		call it directly! Use the scheduling functions
		instead.
	*/
	SimTime setExecutionTime( SimTime time );

	friend class Simulation;

	/**
		\brief Execution of this event

		This function is used by the library. Don't
		call it directly! The execution is managed
		by the scheduling mechanism.
	*/
	void execute();

private:

	SimTime executionTime_;		///< simulation time of next event execution
	Priority priority_;		///< priority for scheduling

	/**
		\brief Get currently running Sched object or simulation
		\return
			pointer to simulation or current Sched object
	*/
	const data::Label& getPartner();
};

/** \interface EventObserver

	\author Ronald Kluth

	\brief Observer for Event-specific calls.

	\sa Event

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

	virtual void onCreate( Event* sender ) {} ///< Construction

	/// Activation (LIFO)
	virtual void onSchedule( Event* sender ) {} ///< Immediate triggering
	virtual void onScheduleAt( Event* sender, SimTime t ) {} ///< Trigger at
	virtual void onScheduleIn( Event* sender, SimTime t ) {} ///< Trigger in

	/// Activation (FIFO)
	virtual void onScheduleAppend( Event* sender ) {} ///< Hold Triggering
	virtual void onScheduleAppendAt( Event* sender, SimTime t ) {} ///< Hold triggering at
	virtual void onScheduleAppendIn( Event* sender, SimTime t ) {} ///< Hold triggering in

	/// Removal / Execution
	virtual void onRemoveFromSchedule( Event* sender ) {} ///< Event de-scheduled
	virtual void onExecuteEvent( Event* sender ) {} ///< Event executed

	/// Event priority change
	virtual void onChangePriority( Event* sender, Priority oldPriority, Priority newPriority ) {}
	/// Event execution time change
	virtual void onChangeExecutionTime( Event* sender, SimTime oldExecutionTime, SimTime newExecutionTime ) {}
};

} } // namespace odemx::base

#endif /* ODEMX_BASE_EVENT_INCLUDED */
