#ifndef __TRIANGLEMESH__FOR_MT__
#define __TRIANGLEMESH__FOR_MT__

#include <vector>
#include <map>
#include <i3d/image3d.h>
#include "params.h"

struct mesh_t {
	std::vector<Vector3FC> Pos;        //list of mesh vertices
	std::vector<long unsigned int> ID; //list of triangles organized in tripplets of indices into the Pos array of vertices;
                                      //the array length should be 3x the number of triangles in the mesh
	std::vector<Vector3F> norm;        //list of vectors that are normal/orthogonal to the planes given by the individual triangles

	///metadata to keep the skeleton information
	std::vector<Vector3FC> fPoints;         //skel. vertex coordinates
	std::vector<unsigned int> segFromPoint; //indices of starting points of all segments
	std::vector<unsigned int> segToPoint;   //...of ending points
	std::vector<float> segFromRadius;       //radii at starting points for all segments

	int parent;                        //index of the "parent" filopodium-branch,
	                                   //-1 if the parent is the cell body itself
};

/**
 * This is simple, minimalistically compatible (somewhat at the syntax level (*))
 * class alternative to the main MotionTracking class ActiveMesh.
 *
 * (*) It uses standard container (std::vector) instead of those of MotionTracking (MArray).
 * Data from both containers, however, can be accessed in a syntactically the same fashion,
 * i.e., with pointers and []. Modifying lengths of the containers differ.
 *
 * (*) Also the Vector3F is different from the MotionTracking one, yet they are similar.
 *
 * It is meant for rapid trying/debugging of basic "mesh-processing" mesh-not-modifying algorithms.
 */
class ActiveMesh
{
  public:
	///(only!) cell body mesh stuff
	std::vector<Vector3FC> Pos;        //list of mesh vertices
	std::vector<long unsigned int> ID; //list of triangles organized in tripplets of indices into the Pos array of vertices;
                                      //the array length should be 3x the number of triangles in the mesh
	std::vector<Vector3F> norm;        //list of vectors that are normal/orthogonal to the planes given by the individual triangles

	std::vector<Vector3FC> oldVolPos;  //list of volumetric mesh vertices, previous situation
	std::vector<Vector3FC> newVolPos;  //list of volumetric mesh vertices, new situation
	std::vector<long unsigned int> VolID; //list of tetrahedra (topology definition), this one is filled only with this->ImportVTK_Volumetric()

	///this one only manages data required for the ConstructFF_T() function
	void RotateVolPos(void)
	{
		oldVolPos=newVolPos;
		newVolPos=Pos;
	}

	///filopodia mesh stuff
	std::vector<struct mesh_t> Ftree;

	///use this one before loading a new set of filopodia
	void DiscardAllFilopodiaObjects(void)
	{ Ftree.clear(); }

	///use this one after loading a new set of filopodia
	void DiscardLastEmptyFilopodiumObject(void)
	{ if (Ftree.size() > 0 && Ftree.back().fPoints.size() == 0)
		Ftree.pop_back(); }

	///returns the number of filopodia whose geometry data is stored
	size_t GetFilopodiaNumber(void)
	{ return(Ftree.size()); }

	///constructs FlowFields using the \e this::oldVolPos and \e this::newVolPos
	void ConstructFF_T(FlowField<float> &FF); //based on interpolation of tetrahedra
	size_t GetVolIDsize(void)
	{ return (VolID.size()); }

	///input is filename
	///returns 0 on success, otherwise non-zero
	int ImportSTL(const char* filename);
	int ExportSTL(const char* filename);
	int ImportVTK(const char* filename);
	int ImportVTK_Volumetric(const char* filename,bool saveAlsoTetrahedra=false);

	///a binding map to be reset with every ImportVTK_Ftree();
	///this map connects branchIDs found in the VTK with
	///the indices added into the this->Ftree
	std::map<int,unsigned int> tipOffsets;

	/**
	 * Import filopodium from file, and save it at specific index (replacing what was there);
	 * if the index is 999999, just append (to the ActiveMesh::Ftree array).
	 * 
	 * This one saves the skeleton information (fPoints,segFrom/ToPoint,segFromRadius attributes)
	 * and calls PopulateSurfTriangles_Ftree() to obtain a mesh.
	 *
	 * This one also resets the this->tipOffsets.
	 */
	int ImportVTK_Ftree(const char* filename,unsigned int idx,const float stretch=1.f,bool resetMesh=true);

	/**
	 * Blows up the skeleton to a surface mesh (Pos,ID,norm attributes).
	 *
	 * If \e enforce is false, use current skeleton starting vertex (default).
	 * If \e enforce is true, use original skeleton starting vertex.
	 * The original vertex is saved right before the current vertex in the list
	 * of vertices (the fPoints array).
	 */
	void PopulateSurfTriangles_Ftree(const unsigned int idx,const bool enforce=false);

	///renders filopodium first aside, then overlays the given mask
	void RenderMask_filo(i3d::Image3d<i3d::GRAY16>& mask,const unsigned int idx,
		const unsigned short color=100,const bool showTriangles=false,
		const bool checkCollisionOfTip=false);
	/// render directly into mask using volumetric mesh
	void RenderMask_bodyVol(i3d::Image3d<i3d::GRAY16>& mask,const bool showTriangles=false);

	///time-lapse (coherent) manipulation with fluorescent dots,
	///always with the given mask
	void InitDots_body(const i3d::Image3d<i3d::GRAY16>& mask);
	void InitDots_filo(const i3d::Image3d<i3d::GRAY16>& mask,const unsigned int idx);

	///displace randomly the dots positions a little
	///but not further away than half pixel
	void BrownDots(const i3d::Image3d<i3d::GRAY16>& mask);

	///displace dots positions according to the FlowField
	void FFDots(const i3d::Image3d<i3d::GRAY16>& mask,
	            const FlowField<float> &FF);

	///project dots into the output \e texture image
	///the \e mask is here only to initate the metadata of
	///the \e texture image
	void RenderDots(const i3d::Image3d<i3d::GRAY16>& mask,
	                i3d::Image3d<i3d::GRAY16>& texture);

	void PhaseII(const i3d::Image3d<i3d::GRAY16>& texture,
	             i3d::Image3d<float>& blurred_texture);
	void PhaseIII(i3d::Image3d<float>& blurred,
	              i3d::Image3d<i3d::GRAY16>& texture);

	///stores simulated fl. dots of the cell body
	std::vector<Vector3F> dots;
	///stores simulated fl. dots of the cell filopodia (of all of them)
	std::vector<Vector3F> dots_filo;

	///indiciator function to tell if we have some dots stored
	///from a previous similation run
	bool DotsFirstRun(void)
	{ return (dots.size() == 0); }

	///if needed, it shifts all mesh coordinates by the \e shift vector
	void TranslateMesh(const Vector3F& shift);

	/**
	 * scans all mesh coordinates and checks if some falls
	 * outside the \e min <-> \e max interval, and updates
	 * (increases) the interval in the respective coordinate axis.
	 *
	 * NB: If you initiate \e min and \e max with, for instance,
	 * mesh body centre, this update will give you current bounding
	 * box of the mesh.
	 */
	void UpdateMeshBBox(Vector3F& min,Vector3F& max);

	/**
	 * sets the optimal size and translation vectors (defined below),
	 * it does not alter the mesh
	 *
	 * to determine scene size, we assume that
	 * the filopodia length will be max diameter of the cell body,
	 * and multiply with certain "safe" constant (=1.0);
	 * all of it integrated into one constant:
	 */
	void CenterMesh(const float safeConstant=3.f);
	///optimal output image size in microns
	Vector3F meshPlaygroundSize;
	///default mesh translation to appear approx. in the output image centre
	///or, it can be considered as an image offset (to avoid translating the mesh)
	Vector3F meshShift;

	///internal class to store all params that one can read in from a config file
	class configDataSubClass
	{
	 public:
		///returns non-zero if some problem occured,
		///the default values are listed in this function
		int ParseConfigFile(const char* fileName);

		///output image resolution as voxels/um
		Vector3F texture_Resolution;
		Vector3F texture_borderWidth;

		std::string texture_PSFpath;
	
		// Filopodium parameter:
		// multiplicity of how many dots should be pressed between two
		// pixel positions. The neighbouring pixels are understood as
		// neighbouring pixel along the direction of filopodium axis
		int filoAxisOrientedDensity;

		// Filopodium parameter:
		// multiplicity of how many dots should be pressed between two
		// pixel positions. The neighbouring pixels are understood as
		// neighbouring pixel in the direction from filo axis to the envelope
		// (surface). This direction is orthogonal to filo axis.
		int filoContourOrientedDensity;


	 private:
		void getValueOrDefault(const char* key,const std::string& defValue, std::string& var);
		void getValueOrDefault(const char* key,const int defValue,          int& var);
		void getValueOrDefault(const char* key,const float defValue,        float& var);
		std::map<std::string,std::string> keyVal_map; //tmp container
	} configFile;

	//the following functions are all defined in graphics.cpp
	void displayMesh(void);

	//these are not used in this project, related only to the OpenGL vizualization
	void displayMeshEdges(void);
};

#endif
