//----------------------------------------------------------------------------
//	Copyright (C) 2002, 2003, 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 Monitor.h

 \author Michael Fiedler

 \date created at 2008/11/21

 \brief Declaration of Monitor

 \since 3.0
 */

#ifndef ODEMX_MONITOR_INCLUDED
#define ODEMX_MONITOR_INCLUDED

#include <odemx/base/Process.h>
#include <odemx/base/continuous/ODESolver.h>
#include <odemx/base/continuous/Continuous.h>
#include <odemx/base/continuous/StateEvent.h>
#include <odemx/base/continuous/VariableContainer.h>
#include <list>
#include <fstream>
#include <iostream>
#include <odemx/setup.h>
#include <odemx/data/Observable.h>

namespace odemx {
	namespace base {
		namespace continuous {

			/** Choice if integration is done by interval or by steps
			 */
			enum WorkingModes {
				stepwise, intervall
			};

			/** Choice how to search for StateEvents means if to work on a linear approximation or
				direct on the solution
			 */
			enum SearchModes {
				linearization, direct
			};

			//forward declaration
			class ODESolver;

			class VariableContainer;

			class Continuous;

			class StateEvent;

			class MonitorObserver;

			/** \class Monitor

				\ingroup base

				\brief A Monitor is used to integrate several continuous processes simultaneously,
				but independent from continuous processes in other Monitors of the simulation.

				\sa Continuous, ODEObject, ODESolver, StateEvent, Process

				\since 3.0
			 */
			class Monitor : public Process, public data::Observable<MonitorObserver> {
			public:
				/** \brief Construction
				 \param variablesArraySize
					 initial size of data structs
				\param s
					pointer to the Simulation object
				\param l
					label of this object
				\param o
					initial observer
				 \note If on runtime data structs are to small they will automatically be enlarged at cost of speed
				 */
				Monitor(Simulation& s, const data::Label& l, int variablesArraySize, MonitorObserver* o = 0);

				/** \brief Construction
				 \param variablesArraySize
					 initial size of data structs
				\param s
					pointer to the Simulation object
				\param l
					label of this object
				\param solver
					the solver for this monitor
				\param o
					initial observer
				 \note If on runtime data structs are to small they will automatically be enlarged at cost of speed
				 */
				Monitor(Simulation& s, const data::Label& l, int variablesArraySize, ODESolver* solver, MonitorObserver* o = 0);

				/// destruction
				~Monitor();

				/** \brief sets how monitor schedules integration
					\param mode
						If value is 'stepwise' the %Monitor will be scheduled after every step (as in older version).
						If value is 'interval' the %Monitor will be scheduled only on known synchronization points.

					\note Only the next scheduled time of Processes in wait list are known synchronization points.
				 */
				void setWorkingMode(WorkingModes mode);

				/** \brief sets the method how to search for StateEvents
					\param mode
						value is direct or linearization

					sets if monitor search for StateEvents direct (slower but more accurate) or
					on a linearization (faster)
				 */
				void setSearchMode(SearchModes mode);

				/** \brief synchronize to an other Monitor

					means the other Monitor will be synchronized to the time of this Monitor

					\note not implemented now
				 */
				void synchronizeToMonitor(Monitor *otherMonitor);

				/** \name handle used solver

					these functions are to control which solving method is used for integration

					@{
				 */
				/** \brief sets the solver for this Monitor
				 */
				void setODESolver(ODESolver *solver);

				/** \brief gives the actual solver of this Monitor
				 */
				ODESolver* getODESolver();
				//@}

				/** \name Process synchronization

					These functions controls with which processes the Monitor synchronizes.
					This means the Monitor stops integration at times where one of these processes is scheduled.
					The integration will be continued after these Process have been executed.

					\note This is necessary for time consistent exchange of values between processes and monitors.

					@{
				 */
				/** \brief adds a discrete process to which the Monitor will be synchronized

					means the Monitor will only integrate to the next event of one discrete process
					he waits for and than waits till this event is computed
				 */
				void addProcessToWaitList(Process *process);

				/** \brief removes a discrete process from the Monitor to synchronize
				 */
				void removeProcessFromWaitList(Process *process);
				//@}

				/** \name continuous processes and StateEvents

					This functions control which continuous processes are calculated by this monitor and
					which StateEvents will be checked will integration.

					@{
				 */
				/** \brief adds an continuous process to the Monitor

					which will be integrated by the Monitor simultaneously
					to other continuous processes of the Monitor
					\note this results in a change of the monitor
				 */
				void addContinuous(Continuous *continuous);

				/** \brief removes an continuous process to the Monitor

					\note this results in a change of the monitor
				 */
				void removeContinuous(Continuous *continuous);

				/** \brief adds an StateEvent to the Monitor to watch for

					means after every step the Monitor checks for all StateEvents
					if condition() off one evaluates to true the point of switching
					from false to true will be approximated and at this point action of the StateEvent
					will be executed and StateEvent will be removed from the Monitor afterwards

					\note If condition() evaluates to true while added, then action() is immediately called.
				 */
				void addStateEvent(StateEvent *stateEvent);

				/** \brief removes a StateEvent from the Monitor
				 */
				void removeStateEvent(StateEvent *stateEvent);
				//@}

				/** \brief calculating the evolution of the continuous processes

					\note this will be called by Simulation
				 */
				int main();

				/** \name stop monitor

					These functions are to set the time limit for the main method to work or give a stop command.

					\note The monitor will only integrate, if there is a time limit set or processes to synchronize with or StateEvents to check.
					\note If there are only StateEvents to check be sure that at least one will evaluate at some point to true or you will have an infinite loop.

					@{
				 */
				/** \brief sets an time limit where the monitor will finish the main method
					\param time
						time to stop monitor
				 */
				void setTimeLimit(SimTime time);

				/** \brief returns the time limit
					\param time
						the set time limit for this instance, which is only valid if returned true
				 */
				bool getTimeLimit(SimTime* time);

				/** \brief forget a set time limit
				 */
				void removeTimeLimit();

				/** \brief get the execution time of the next synchronized process
				 */
				SimTime getStepTimeLimit();

				/** \brief forces the monitor to finish the main method when next scheduled
				 */
				void stop();
				//@}

				/** \name internal Time

					To make ODEMx time representation independent of the time representation in the solver
					(numerical framework) the Monitor has an internal timing for conversion.
					This means the monitor has an internal reference time and a actual time,
					which is after the reference time.

					@{
				 */
				/** \brief sets internal reference time

					\note this means a change for the calculation and triggers a new initialization for the solver
				 */
				void setInternalTime(SimTime time);

				/** \brief gets internal reference time
				 */
				SimTime getInternalTime();

				/** \brief gets actual time of Monitor
				 */
				SimTime getActualInternalTime();

				/** \brief sets internal reference time to actual time

					sets internal reference time in a way that actual time and internal
					reference time are equal for this monitor at call of this function

					\note this means a change for the calculation and triggers a new initialization for the solver
				 */
				void adaptInternalTime();
				//@}

				/** \brief sets the monitor changed

					If the continuous system has a discrete change, the solver has to be newly initialized
					to guarantee consistency. So it causes solver->init() to be called before next calculation.

					\note As long a user access only objects by supplied functions there is no need to call this.
				 */
				void setChanged();

			protected:

				/** the solver used by the Monitor
				 */
				ODESolver *solver;

				/** the workingMode in which the monitor runs
				 */
				WorkingModes workingMode;

				/** the searchMode used by the monitor
				 */
				SearchModes searchMode;

				/** Lists the discrete processes which the Monitor synchronizes to
				 */
				std::list<Process*> waitForProcesses;

				/** Lists the continuous processes managed by the Monitor
				 */
				std::list<Continuous*> continuousList;

				/** Lists the StateEvents the Monitor watches for
				 */
				std::list<StateEvent*> stateEvents;

				/** actual size of the variable structs
				 */
				int variablesArraySize;

				/** actual number of variables used
				 */
				int variablesCount;

				/** VariableContainer used by Monitor for managing variables
				 */
				VariableContainer *variables;

				/** an internal reference time value
				 */
				SimTime internalTime;

				/** the time limit if set
				 */
				SimTime timeLimit;

				/** indicates if a time limit is set
				 */
				bool timeLimitSet;

				/** indicates if stop() was called
				 */
				bool hasToStop;

				/** actual time of Monitor is internalTime + diffTime
				 */
				double diffTime;

				/** indicates if the system to calculate have changed since last step
				 */
				bool changed;

				/** \brief integration to \p time

					\note internal function
				 */
				void integrateUntil(SimTime time);

				/** \brief integration till StateEvent
					\note internal function
				 */
				void integrate();

				/** Search the time when a StateEvent occurs for when is known condition is false
				 at internalTime + old_diffTime with values old_variable_values for all
				 StateEvents and in the actual state there is a StateEvent with condition true
				 of the Monitor and then triggers the event
				 */
				void searchStateEvent(double old_diffTime, double *old_variable_values);

				/** evaluates all equations of all continuous processes as one function
				 as sum of partial functions at time internalTime + time
				 */
				void evalF(double time);

				/** evaluates all jacobian of all continuous processes as one matrix
				 as sum of partial matrices at time internalTime + time
				 */
				void evalJacobian(double time);

				friend class Continuous;
				friend class ODESolver;
				friend class MonitorTrace;
			};

			//forward declaration
			class Monitor;

			class Continuous;

			/** \interface MonitorObserver

				\author Sascha Qualitz

				\brief Observer for Monitor specific events

				\sa Continuous

				\since 3.0
			*/
			class MonitorObserver:
				public ProcessObserver
			{
			public:
				virtual ~MonitorObserver(){};

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

				virtual void onAddProcessToWaitList( Monitor* sender, Process* peer ){} ///< Add peer
				virtual void onRemoveProcessFromWaitList( Monitor* sender, Process* peer ){} ///< Remove peer

				virtual void onBeginIntegrate( Monitor* sender ){} ///< Begin integrate
				virtual void onEndIntegrate( Monitor* sender){} ///< End integrate

				virtual void onBeginIntegrateUntil( Monitor* sender ){} ///< Begin integrate
				virtual void onEndIntegrateUntil( Monitor* sender){} ///< End integrate

				virtual void onNewValidState( Monitor* sender ){} ///< New valid state

				virtual void onAddContinuous( Monitor* sender, Continuous* continuous ){} ///< new continuous object added
				virtual void onRemoveContinuous( Monitor* sender, Continuous* continuous ){} ///< continuous object removed

				virtual void onAddStateEvent( Monitor* sender, StateEvent* event ){} ///< new state event added
				virtual void onRemoveStateEvent( Monitor* sender, StateEvent* event ){} ///< state event removed
			};
		}
	}
}

#endif

