/*************************************************************************
*  Copyright (C) 2004 by Olivier Galizzi                                 *
*  olivier.galizzi@imag.fr                                               *
*  Copyright (C) 2004 by Janek Kozicki                                   *
*  cosurgi@berlios.de                                                    *
*                                                                        *
*  This program is free software; it is licensed under the terms of the  *
*  GNU General Public License v2 or later. See file LICENSE for details. *
*************************************************************************/

#pragma once

#include<lib/serialization/Serializable.hpp>
#include<core/Omega.hpp>
#include<core/Timing.hpp>
#include<lib/base/Logging.hpp>

namespace yade { // Cannot have #include directive inside.

class Body;
class Scene;

class Engine: public Serializable{
	public:
		// pointer to the simulation, set at every step by Scene::moveToNextTimeStep
		Scene* scene;
		//! high-level profiling information; not serializable
		TimingInfo timingInfo; 
		//! precise profiling information (timing of fragments of the engine)
		shared_ptr<TimingDeltas> timingDeltas;
		virtual ~Engine() {};
	
		virtual bool isActivated() { return true; };
		virtual void action() {
			LOG_FATAL("Engine "<<getClassName()<<" calling virtual method Engine::action(). Please submit bug report at https://gitlab.com/yade-dev/trunk/issues");
			throw std::logic_error("Engine::action() called.");
		}
	private:
		// py access funcs	
		TimingInfo::delta timingInfo_nsec_get(){return timingInfo.nsec;};
		void timingInfo_nsec_set(TimingInfo::delta d){ timingInfo.nsec=d;}
		long timingInfo_nExec_get(){return timingInfo.nExec;};
		void timingInfo_nExec_set(long d){ timingInfo.nExec=d;}
		void explicitAction() {scene=Omega::instance().getScene().get(); action();}; 

	DECLARE_LOGGER;

	// clang-format off
	YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Engine,Serializable,"Basic execution unit of simulation, called from the simulation loop (O.engines)",
		((bool,dead,false,,"If true, this engine will not run at all; can be used for making an engine temporarily deactivated and only resurrect it at a later point."))
		((int, ompThreads, -1,,"Number of threads to be used in the engine. If ompThreads<0 (default), the number will be typically OMP_NUM_THREADS or the number N defined by 'yade -jN' (this behavior can depend on the engine though). This attribute will only affect engines whose code includes openMP parallel regions (e.g. :yref:`InteractionLoop`). This attribute is mostly useful for experiments or when combining :yref:`ParallelEngine` with engines that run parallel regions, resulting in nested OMP loops with different number of threads at each level."))
		((string,label,,,"Textual label for this object; must be valid python identifier, you can refer to it directly from python.")),
		/* ctor */ scene=Omega::instance().getScene().get();
		#ifdef USE_TIMING_DELTAS
			timingDeltas=shared_ptr<TimingDeltas>(new TimingDeltas);
		#endif
		,
		/* py */
		.add_property("execTime",&Engine::timingInfo_nsec_get,&Engine::timingInfo_nsec_set,"Cumulative time in nanoseconds this Engine took to run (only used if :yref:`O.timingEnabled<Omega.timingEnabled>`\\ ==\\ ``True``).")
		.add_property("execCount",&Engine::timingInfo_nExec_get,&Engine::timingInfo_nExec_set,"Cumulative count this engine was run (only used if :yref:`O.timingEnabled<Omega.timingEnabled>`\\ ==\\ ``True``).")
		.def_readonly("timingDeltas",&Engine::timingDeltas,"Detailed information about timing inside the Engine itself. Empty unless enabled in the source code and :yref:`O.timingEnabled<Omega.timingEnabled>`\\ ==\\ ``True``.")
		.def("__call__",&Engine::explicitAction)
	);
	// clang-format on
};

REGISTER_SERIALIZABLE(Engine);

} // namespace yade

