#ifndef GTGEN_SCHEDULER_H
#define GTGEN_SCHEDULER_H

#include <list>
#include <map>
#include <i3d/image3d.h>

#include "cell.h"


template < class TYPE > struct TrackRecord
{
	 size_t fromTimeStamp;
	 size_t toTimeStamp;
	 TYPE parentID;

	 TrackRecord(size_t from, size_t to, TYPE id): 
				fromTimeStamp(from), toTimeStamp(to), parentID(id) {};

	 TrackRecord(): fromTimeStamp(0), toTimeStamp(0), parentID(0) {};

};


template <class MV, class PV> class Cell;

/**
 * Basic class responsible for time scheduling and activating the
 * individual cell phases
 * MV ... mask voxel datatype
 * PV ... phantom voxel datatype
 */
template <class MV = i3d::GRAY16, class PV = i3d::GRAY16> class Scheduler
{
  public:
			 /**
			  * Constructor used when simulation should start "from nothing",
			  * that is, scene and cells had to be created.
			  */
			 Scheduler(const IniHandler &_iniConfig);

			 /**
			  * Constructor used when user supplied initial cell layout
			  * to start with the simulation.
			  *
			  * \param[in] fname 	 labeled image file with cell masks
			  */
			 Scheduler(const IniHandler &_iniConfig, const char *fname);
			 
			 /// destructor
			 ~Scheduler();

			 /**
			  * Generate new scenes. Duplicate the most recent one frame in the 
			  * sceneMasks n-times. For the sequence of phantoms stored in
			  * scenePhantoms allocate the memory only. Do not duplicate the
			  * most recent phantom. 
			  *
			  * In general, this method lengthen the vectors: sceneMasks, 
			  * scenePhantoms
			  *
			  * Updates the variable 'newestScene'.
			  */
			 void AddNewScenes(size_t noNewScenes);

			 /**
			  * This method takes the time frames that do not contain any living 
			  * cell, i.e. the frames are too old, and flushes them to the disk
			  * as a sequence of images. This is normal operation.
			  *
			  * If \e releaseAll is set to true, the method flushes all time frames
			  * it has to the disk. This is supposed to be called only during
			  * processing of exceptions when the simulation is stopped.
			  *
			  * The function updates the variables 'oldestScene' and 'timeStamp'.
			  *
			  * \param[in] releaseAll	indicator for emergency branch operation
			  *
			  * Default value for \e releaseAll is false.
			  */
			 void ReleaseOldScenes(bool releaseAll=false);

			 /**
			  * Main simulation cycle is implemented here.
			  *
			  * Basically, it just selects a cell that shall finish with lowest
			  * Cell::timeStamp and calls its Cell::DoNextPhase(), it then updates
			  * this->timeStamp to mark which is currently the oldest fully prepared
			  * frame. This cycle is repeated until the this->timeStamp is not advanced
			  * by at least \e noFrames.
			  *
			  * \param[in] noFrames		how many new frames to generate
			  *
			  * \note This function used to select a cell with the oldest Cell::timeStamp.
			  * However, both planning politics can be shown to work well while the current
			  * one is easier to justify.
			  */
			 void Run(const size_t noFrames);

			 /**
			  * Goes over all cells in the list, finds adepts and asks them to become cell comets.
			  * Finding adepts means seeking \e count cells that are the closest to right, top,
			  * left, and bottom scene boundaries in the xy-plane sense, respectively. These cells
			  * however, must be within relative distance \e dist from its boundary. Computations
			  * are based only on cell centres, the lists of boundary points are not examined.
			  *
			  * \param[in] count	desired number of comets in each direction
			  * \param[in] dist	relative maximum distance from boundary
			  *
			  * For details about cell comets, see docs of the StateOfComet type.
			  */
			 void CreateCellComets(const size_t count, const float dist=0.6f);

			 /**
			  * Computes the ratio A / B where A is the total number of cells being
			  * either 'noComet' or 'hesitating' or 'fromBoundary', and where B is
			  * the total number of cells being 'toBoundary'.
			  *
			  * It, basically, tells the current ratio of a number of cells considered
			  * for 'living in the scene' to a number of cells considered for 'cut away'
			  * as a result of scene cropping (as a result of introducing flow-in and
			  * flow-out tracking events).
			  *
			  * If above 1, there is more 'living' than 'cut away' cells.
			  */
			 float GetCellCometsLeavingFactor(void);

			 /// Just prints average frame budgets for every phase using Scheduler::GetPhaseFrameBudget()
			 void PrintPhaseBudgets(void);

			 /// Returns sum of frame budgets for interphase phases using Scheduler::GetPhaseFrameBudget()
			 size_t GetInterphaseBudget(void);

			 /**
			  * Computes (without generating any cell) how many frames
			  * are _typically_ needed for the given phase. The difference
			  * between this function and the Cell::GetPhaseFrameBudget()
			  * is that this one calculates with Scheduler::framesPerCycle,
			  * a typical average lenght of the cell cycle, while the other
			  * calculates with Cell::cellCycleLength, a particular
			  * cell-specific slightly-randomized length of the cell cycle.
			  *
			  * \param[in] phase		the phase whose length is questioned
			  */
			 size_t GetPhaseFrameBudget(const ListOfPhases &phase);

			 /// Reveal scene resolution, in pixels per micron
			 i3d::Vector3d<float> GetSceneRes() const { return sceneRes; };

			 /// Reveal scene size, in microns
			 i3d::Vector3d<float> GetSceneSize() const { return sceneSize; };

			 /// Reveal scene size, in pixels
			 i3d::Vector3d<size_t> GetSceneSizePX() const 
			 { return i3d::MicronsToPixels(sceneSize,sceneRes); };

			 /// Reveal scene offset, in microns
			 i3d::Vector3d<float> GetSceneOffset() const { return sceneOffset; };

			/**
			 * Inits or reinits the Scheduler::pnArray and associated variables
			 * Scheduler::pnSizes and Scheduler::pnRealWidth according to the
			 * given image \e size and number of desired \e frames.
			 *
			 * The function takes the Scheduler::sceneRes into account.
			 *
			 * \param[in] size	spatial size of the image in pixels
			 * \param[in] frames	number of frames
			 * \param[in] fileName	name of a cache file to save the power noise into
			 *
			 * Since a cell boundary points are reinitialized minimally once
			 * per cell cycle, we use the Scheduler::framesPerCycle as a default
			 * value for the number of \e frames. The function PnGetMeNonrigidHints()
			 * gives more information on this subject.
			 *
			 * The default value for \e fileName is NULL, indicating no file should
			 * be stored.
			 *
			 * \note The function may take well a few minutes to fill the array.
			 * \note Saving of the 'power noise' array to the disk may take some
			 * time as well.
			 */
			void PnInitArray(i3d::Vector3d<size_t> const &size, const size_t frames,
					const char *fileName=NULL);

			/**
			 * This function does exactly the same thing as the PnInitArray() but
			 * read the power noise array from some precomputed cache file \e fileName.
			 * The file also contains the metadata information like size, etc.
			 * 
			 * \param[in] fileName	name of the cache file with generated power noise
			 *
			 * \note Unlike its sibbling function, this one may be faster.
			 */
			void PnInitArray(const char *fileName);

			/**
			 * Internal function for debugging. It prints histogram of generated
			 * 'power noise' image pnArray.
			 */
			void PnPrintArrayStats(void);

			/**
			 * SOFA: initial export
			 */
			void SOFA_InitialExport(void);

  private:
			 /// structure loaded external ini file
			 const IniHandler &configIni;

			 /// size of the whole controlled space (= the scene), in microns
			 i3d::Vector3d<float> sceneSize;

			 /// resolution of the scene, in pixels per micron
			 i3d::Vector3d<float> sceneRes;

			 /// offset of the scene, in microns
			 i3d::Vector3d<float> sceneOffset;

			 /** 
			  * Sequence of labeled frames (Each cell bears its unique label).
			  * The sequence contains only the active frames. The frames, that are 
			  * too old, are deleted (null pointer)
			  */
			 std::vector<i3d::Image3d<MV>* > sceneMasks;

#ifdef GTGEN_WITH_NUCLEIMASKS
			 /**
			  * Sequence of labeled frames with cell nuclei just like Scheduler::sceneMasks.
			  * Note that in some cell phases the nuclei is not present.
			  */
			 std::vector<i3d::Image3d<MV>* > sceneNuclei;
#endif
			 /**
			  * Sequence of frames with individual phantoms put inside.
			  */
			 std::vector<i3d::Image3d<PV>* > scenePhantoms;

#ifdef GTGEN_WITH_TERRITORIES
			 /**
			  * Sequence of AF frames with chromosomal territories.
			  */
			 std::vector<i3d::Image3d<PV>* > sceneTerritories;
#endif
			 /// global time stamp
			 size_t timeStamp;

			 /// time stamp of the oldest frame stored in the memory
			 size_t oldestScene;

			 /// time stamp of the newest frame stored in the memoery
			 size_t newestScene;

			 /// number of frames defining the length of cell cycle
			 size_t framesPerCycle;

			 /// list of all cells in the scene
			 std::list<Cell<MV,PV>* > cells;
			 
			 /**
			  * this class can also access the private/internal 
			  * attributes directly
			  */
			 friend class Cell<MV, PV>;

			 /// spatiotemporal (2D+t) masks for debug purposes
			 std::list<i3d::Image3d<MV>/**/> stMasks;

			 /// spatiotemporal (2D+t) phantoms for debug purposes
			 std::list<i3d::Image3d<PV>/**/> stPhantoms;

#ifdef GTGEN_WITH_TERRITORIES
			 /// spatiotemporal (2D+t) phantoms for debug purposes
			 std::list<i3d::Image3d<PV>/**/> stTerritories;
#endif

			 /**
			  * A helper structure to be filled during the simulation
			  * which should explain when a cell appeared first and last
			  * time in the generated sequence and if it is a daughter
			  * of some other parent cell.
			  */
			 std::map<MV, TrackRecord<MV> /**/ > tracker;

			/**
			 * Array of 'power noise' values, which are used to drive
			 * non-rigid deformations of cells. The array, despite it
			 * is 1D, is logically 4D, i.e. 3D space + 1D time. The
			 * arrangement is x-axis first, then y, z, and time.
			 * The size of the array is encoded in the Scheduler::pnSizes.
			 *
			 * To access pixel at [x,y,z,t], one needs to look at
			 *  *(pnArray + t*frame_sz + z*slice_sz + y*buffer_line_length + x),
			 * buffer_line_length = Scheduler::pnSizes[0].
			 *
			 * Anytime a 3D mask of a cell at some timePoint is to be non-rigidly
			 * deformed, one projects its boundaries to this 'power noise' 4D
			 * image, which this array represents. The scalar values read out along
			 * the cell mask boundary tell deformation force and its direction
			 * which pushes towards or outwards the cell centre at the particular
			 * boundary point. The 'power noise' provides smoothly varying forces.
			 * Clearly, the array spatial size must be larger than is the size
			 * of the cell mask.
			 *
			 * During the process of filling the array, it may got padded
			 * in the x-axis. The original width is given in Scheduler::pnRealWidth.
			 * The valid 'power noise' values should be expected only
			 * within this width.
			 *
			 * If it is NULL, it is not allocated.
			 *
			 * \note This array is likely to be \b very \b big (several houndreds of MBs).
			 */
			float *pnArray;

			/**
			 * size of the Scheduler::pnArray
			 *
			 * In the array, the first value is the size along x, then y, z,
			 * and number of frames (the time coordinate). The length of the
			 * array should be multiple of these four numbers.
			 *
			 * Sizes are given in pixels.
			 */
			size_t pnSizes[4];

			/// Original width of the array before padding: pnRealWidth <= pnSizes[0]
			size_t pnRealWidth;

			/**
			 * Fills a hint image \e Hints with forces that should be applied
			 * on the boundary points in order to simulate non-rigid deformations.
			 *
			 * The x-size of the \e Hints image will be the number \e BPLength of outer
			 * boundary points \e BPList of some cell mask. The y-size of the \e Hints
			 * image will be Scheduler::pnSizes[3]-1, i.e. the number of frames minus one
			 * for which the power noise array is generated.
			 *
			 * It is assumed that the order of boundary points in the \e BPList is not
			 * changing, until the list is reinitialized. Hence, a column at certain
			 * x-index can describe time-lapse 'history' of forces being applied on
			 * this point.
			 *
			 * \note The \e Hints image becomes invalid whenever Cell::scmCellBPList is
			 * reinitialized!
			 *
			 * A bounding box in microns is computed from the \e BPList and its sizes
			 * are converted to pixel coordinates using Scheduler::sceneRes. This
			 * bounding box must fit inside the Scheduler::pnSizes with Scheduler::pnRealWidth.
			 * It is randomly shifted within the larger power noise image.
			 *
			 * \param[in] BPList	list of outer boundary points
			 * \param[in] BPLength	number of such points
			 * \param[out] Hints	output deformation forces image
			 *
			 * \note The \e BPList may actaully be longer than \e BPLength.
			 */
			void PnGetMeNonrigidHints(std::vector< i3d::Vector3d<float> /**/> const &BPList,
						const size_t BPLength,
						i3d::Image3d<float> &Hints);

			/**
			 * a name of the lock file, which should, in the ideal case without
			 * segfaults etc, flag that the simulation is still in progress
			 *
			 * If the file gets deleted by user, it is understood that user wishes
			 * the simulation to peacefully abort while saving everything it can.
			 * This is detected at the end of every Cell::DoNextPhase() function.
			 */
			char lockFile[50];

			/**
			 * The spatio-temporal image (2D+t) will be generated from
			 * the time-lapse sequence of 3D frames. Each frame offers its 
			 * slice of z-depth 'slideOfInterest'
			 */
			size_t sliceOfInterest;
};


#endif
