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

 \author Michael Fiedler

 \date created at 2008/11/21

 \brief Declaration of odemx::base::Continuous

 \sa continuous/Continuous.cpp

 \since 3.0
 */

#ifndef ODEMX_CONTINUOUS_INCLUDED
#define ODEMX_CONTINUOUS_INCLUDED

#include <odemx/base/continuous/ODEObject.h>
#include <odemx/base/continuous/VariableContainer.h>

#include <odemx/data/Observable.h>

#include <string>
#include <vector>
#include <list>

namespace odemx {
	namespace base {
		namespace continuous {

			// forward declarations
			class Monitor;

			class ODEObject;

			class VariableContainer;

			class ContinuousObserver;

			class ContinuousTrace;

			class MonitorObserver;

			/** \class Continuous

			 \ingroup base

			 \author Michael Fiedler

			 \brief %Continuous is the class for describing values of a continuous process in the system

			 \note since 3.0 the values and the equations of a continuous process are split in Continuous and ODEObject

			 \sa ODEObject, Monitor, StateEvent, ODESolver

			 The user creates an %Continuous instance for each continuous object in a simulation.
			 This instance holds dimension continuous values.

			 \since 3.0

			 */
			class Continuous : public odemx::data::Producer, public data::Observable<ContinuousObserver>
			{
			public:
				/** \brief Construction
				 \param dimension
					count of continuous values for the instance (dedicated)
				 */
				Continuous(Simulation& sim, const data::Label& label, int dimension);

				/** \brief Construction
				 \param monitor the monitor were the continuous object belongs to
				 \param dimension
				 \param first equation, in the life cycle of the object this can change and other equations can be add via addODE(...).
					count of continuous values for the instance
				 */
				Continuous(Simulation& sim, const data::Label& label, Monitor* monitor, int dimension, ODEObject* equation = 0);

				/** \brief Construction
				 \param monitor the monitor were the continuous object belongs to
				 \param dimension
				 \param List of equations, in the life cycle of the object this can change and other equations can be add via addODE(...).
					count of continuous values for the instance
				 */
				Continuous(Simulation& sim, const data::Label& label, Monitor* monitor, int dimension, std::list<ODEObject*> equationList);

				/// Destruction
				~Continuous();

				/** \name Equation handling

					These functions are for managing which equations are currently influencing the continuous values.

					@{
				 */
				/** \brief Adds an equation
					\param equation
						pointer to an ODEObject describing an equation on the values
					\note all equations which are added will be used for integration simultaneously and summed up
					\note this results in a change of the associated monitor
				 */
				void addODE(ODEObject *equation);

				/** \brief Adds a list of equations
					\param equation
						list of pointer to an ODEObject describing an equation on the values
					\note all equations which are added will be used for integration simultaneously and summed up
					\note this results in a change of the associated monitor
				 */
				void addODEList(std::list<ODEObject*> equation);

				/** \brief Remove an equation
					\param
						pointer to the ODEObject to remove
					\note the equation is only removed from calculation the object will not be destroyed
					\note this results in a change of the associated monitor
				 */
				void removeODE(ODEObject *equation);

				//@}

				/** \name Monitor handling

					These functions are for setting the %Monitor, which calculates the evolution of the
					continuous values of this instance described by the added equations.

					@{
				 */
				/** \brief sets the Monitor in which the Continuous %Process will be integrated
					\param monitor
						pointer to the monitor which has to handle this instance
					\note The instance will automatically be transferred from an other %Monitor, if one was assigned
					\note to remove from a %Monitor pass NULL as value
					\note this results in a change to the involved monitors
				 */
				void setMonitor(Monitor *monitor);

				/** \brief gives the Monitor responsible for this instance
				 */
				Monitor* getMonitor();
				//@}

				/** \brief gives the dimension of the Continuous %Process

					means the number of variables the %Process have
				 */
				int getDimension();

				/** \name Access to Values between Integration

					Values should only be changed, when the responsible Monitor is not integrating.

					@{
				 */
				/** \brief sets the value of an variable
					\param i
						index of the variable to set (range 0..(Dimension-1))
					\param value
						the value which the variable will be set
					\note should only be used when responsible %Monitor is not running(means is integrating)
					\note this results in a change of the associated monitor
				 */
				void setValue(int i, double value);

				/** \brief gets the value of an variable
					\param i
						index of the variable to get (range 0..(Dimension-1))
					\note should only be used when responsible %Monitor is not running(means is integrating)
					\return the value of the variable accessed by index \p i
				 */
				double getValue(int i);
				//@}

				/**	\name Access to Values in Integration

					These functions are for the evaluation of the differential equations.
					Which means they are used in implementations of the functions derivates and jacobi of ODEObject.
					The user must use getValueForDerivative to access the value of a variable in such a implementation.

					\note These functions should only be used while Integration.

					@{
				 */
				/** \brief gets the value of an variable while integrating
					\param i
						index of the variable to get (range 0..(Dimension-1))
					\return the value of the i-th variable at an possible intermediate step

					\note The user must use this function to access the value of a variable in an implementation of derivates or
					jacobi in ODEObject
				 */
				double getValueForDerivative(int i);

				/** \brief sets the value for y' = f(y) in the i-th component
					\param i
						index of the variable to set (range 0..(Dimension-1))
					\param value
						set y'[i] = \p value

					\note multiple calls will result in sum up the values
				 */
				void setDerivative(int i, double value);

				/** \brief gets the value of y' = f(y) in the i-th component
					\param i
						index of the variable to get (range 0..(Dimension-1))

					means returns y'[i]
				 */
				double getDerivative(int i);

				/** \brief sets the value for Df exactly f_i \partial y[j] = value
					\param i
						index of the component to differentiate (range 0..(Dimension-1))
					\param j
						index of the variable by which will be differentiated (range 0..(Dimension-1))

					For an equation given as y' = f(y) set the result \p value for differentiating
					the i-th component of f by the j-th component of y.

					\note multiple calls will result in sum up the values
				 */
				void setJacobi(int i, int j, double value);

				/** \brief sets the value for Df exactly f_i \partial continuous->y[j] = value
					\param i
						index of the component to differentiate (range 0..(Dimension-1))
					\param continuous
						pointer to a continuous instance which influences the right hand side
					\param j
						index of the variable by which will be differentiated (range 0..(continuous->getDimension()-1))

					For an equation given as y' = f(y, x) set the result \p value for differentiating
					the i-th component of f by the j-th component of x.

					\note multiple calls will result in sum up the values
				 */
				void setJacobi(int i, Continuous* continuous, int j, double value);

				/** \brief gets the value of Df returns f_i \partial y[j]
					\param i
						index of the component to get (range 0..(Dimension-1))
					\param j
						index of the variable by which will be differentiated (range 0..(Dimension-1))

					For an equation given as y' = f(y) gives the intermediate result for differentiating
					the i-th component of f by the j-th component of y. The value of result is determined
					by previous calls to setJacobi.

					\note to work correct this instance and \p continuous need to have the same %Monitor
				 */
				double getJacobi(int i, int j);

				/** \brief gets the value of Df returns f_i \partial continuous->y[j]
					\param i
						index of the component to differentiate (range 0..(Dimension-1))
					\param continuous
						pointer to a Continuous instance which influences the right hand side
					\param j
						index of the variable by which will be differentiated (range 0..(continuous->getDimension()-1))

					For an equation given as y' = f(y, x) set the result \p value for differentiating
					the i-th component of f by the j-th component of x. The value of result is determined
					by previous calls to setJacobi.
				 */
				double getJacobi(int i, Continuous* continuous, int j);

				/** \brief sets the value for DfDt exactly f_i \partial t = value
					\param i
						index of the component to set (range 0..(Dimension-1))

					For an equation given as y' = f(y) set the result \p value for differentiating
					the i-th component of f by time.

					\note multiple calls will result in sum up the values
					\note to work correct this instance and \p continuous need to have the same %Monitor
				 */
				void setDfDt(int i, double value);

				/** \brief gets the value for DfDt returns f_i \partial t = value
					\param i
						index of the component to get (range 0..(Dimension-1))

					For an equation given as y' = f(y) set the result \p value for differentiating
					the i-th component of f by time. The value of result is determined
					by previous calls to setDfDt.
				 */
				double getDfDt(int i);
				//@}

				/** \brief sets the monitor changed

					so that solver->init() will be called before next calculation
				 */
				void setChanged();

				/** \brief Returns the baseIndex for internal use in a macro.
				*/
				int getBaseIndex();

				/** \brief
				 */
				void notifyValidState();

			//private:
				/** List all equations which belong to this instance
				 */
				std::list<ODEObject*> equations;
			private:

				/** the Monitor in which this instance will be integrated
				 */
				Monitor *monitor;

				/** the dimension of the instance
				 */
				int dimension;

				/** the base index for variables of this continuous object in
				 *	monitor variables
				 */
				int baseIndex;

				/** Observer of this object
				 */
				ContinuousTrace* trace;

				friend class Monitor;

				friend class MonitorTrace;
			};

			//forward declaration
			class Monitor;

			/** \interface MonitorObserver

				\author Sascha Qualitz

				\brief Observer for Monitor specific events

				\sa Continuous

				\since 1.0
			*/
			class ContinuousObserver:
				public ProcessObserver
			{
			public:
				virtual void onCreate( Continuous* sender ) {} ///< Construction

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

				virtual void onSetMonitor( Continuous* sender, Monitor* monitor ) {}
			};

			/** \class ContinuousTrace

				\author Sascha Qualitz

				\brief Trace for Continuous state changes of continuous objects controlled by the monitor

				\sa Continuous MonitorObserver

				MonitorTrace logs the state changes of the Continuous objects
				into a text file. The text file is automatically opened
				and closed.

				\since 1.0
			*/
			class ContinuousTrace
			:	public ContinuousObserver
			{
				public:
					/// Construction
				ContinuousTrace( Continuous* contu = 0, const std::string& fileName = "" );
					/// Destruction
					virtual ~ContinuousTrace();

					/// Follow state changes
					virtual void onNewValidState( Continuous* sender );
					void onSetMonitor( Continuous* sender, Monitor* monitor );

				private:
					// Implementation
					std::ostream* out;
					bool firstTime;
					const std::string& fileName;

					Continuous* continuous;

					void openFile( const std::string& fileName );
			};
		}
	}
}

#endif
