#include <i3d/image3d.h>
#include <i3d/transform.h>
#include <i3d/morphology.h>

#include <stdlib.h> //for rnd generator seeding
#include <time.h>
#include <unistd.h>
#include <string.h>

#include "params.h"
#include "graphics.h"
#include "TriangleMesh.h"

//create some shared objects...
ParamsClass params;
void ParamsSetup(void);
//
ActiveMesh mesh;

bool LoadNewMesh(const char* path,const int ID,const int fileNo);

int main(int argc,char **argv)
{
	if (argc == 1)
	{
		//I'm executed with no params, provide help
		std::cout << "ADD SOME HEADER INFORMATION HERE\n\n";
		std::cout << "params:  cell_surf_mesh  filo1_description [filoN_description]\n\n";
		std::cout << "The program will try to load object files (cell body surface mesh -- the\n"
		             "first parameter) and (filopodia skeleton definitions -- the second till\n"
		             "the last parameter(s)) and display them with OpenGL. Note, at least one\n"
		             "filopodium mesh has to be supplied.\n\n";
		std::cout << "or, params:  mesh.stl\n";
		std::cout << "The program will try to load and display the given STL mesh.\n";
		return(-1);
	}

	//init the situation...
	ParamsSetup();

	if (argc == 2)
	{
		int retval=mesh.ImportSTL(argv[1]);
		if (retval) 
		{
			std::cout << "some error reading STL: " << retval << "\n";
			return(5);
		}
		std::cout << "vertices   #: " << mesh.Pos.size() << "\n";
		std::cout << "triangles  #: " << mesh.ID.size()/3 << "\n";
		std::cout << "normals    #: " << mesh.norm.size() << "\n";

		//position the mesh somewhat inside the scene
		mesh.ScaleMesh(Vector3F(180.f,180.f,250.f));
		mesh.TranslateMesh(params.sceneCentre);
	}
	else
	{
		//read cell body
		std::cout << "reading cell body: " << argv[1] << "\n";
		int retval=mesh.ImportVTK(argv[1]);
		if (retval) 
		{
			std::cout << "some error reading cell body: " << retval << "\n";
			return(1);
		}

		//read filopodia files
		for (int i=0; i < argc-2; ++i)
		{
			std::cout << "reading filopodium: " << argv[2+i] << "\n";
			retval=mesh.ImportVTK_Ftree(argv[2+i],999999);
			if (retval) 
			{
				std::cout << "some error reading filopodium: " << retval << "\n";
				return(2);
			}
		}

		std::cout << "vertices   #: " << mesh.Pos.size() << "\n";
		std::cout << "triangles  #: " << mesh.ID.size()/3 << "\n";
		std::cout << "normals    #: " << mesh.norm.size() << "\n";

		std::cout << "filopodia #: " << mesh.Ftree.size() << "\n";
		for (size_t f=0; f < mesh.Ftree.size(); ++f)
		{
			std::cout << f << ": vertices  #: " << mesh.Ftree[f].Pos.size() << "\n";
			std::cout << f << ": triangles #: " << mesh.Ftree[f].ID.size()/3 << "\n";
			std::cout << f << ": normals   #: " << mesh.Ftree[f].norm.size() << "\n";
		}

		//position the mesh somewhat inside the scene
		mesh.ScaleMesh(Vector3F(180.f,180.f,250.f));
		mesh.TranslateMesh(params.sceneCentre);
	}

	//display all the meshes
	initializeGL();
	loopGL();
	closeGL();

	return(0);
}


void ParamsSetup(void)
{
	//set up the environment
   params.sceneOffset=Vector3d<float>(0.f);
   params.sceneSize=Vector3d<float>(27.5f,27.5f,22.0f); //for sample cell

	params.sceneCentre=params.sceneSize;
	params.sceneCentre/=2.0f;
	params.sceneCentre+=params.sceneOffset;

	params.sceneOuterBorder=Vector3d<float>(2.f);
	params.sceneBorderColour.r=0.7f;
	params.sceneBorderColour.g=0.7f;
	params.sceneBorderColour.b=0.0f;

	params.inputCellsFilename="cells/cell%d.txt";
	params.numberOfAgents=2;

	params.friendshipDuration=10.f;
	params.maxCellSpeed=0.20f;

	//adapted for cell cycle length of 24 hours
	params.cellCycleLength=14.5*60; //[min]

	params.currTime=0.0f; //all three are in units of minutes
	params.incrTime=1.0f;
	params.stopTime=50.0f;

	params.imgSizeX=500; //pixels
	params.imgSizeY=500;
	params.imgResX=1.0f; //pixels per micrometer
	params.imgResY=1.0f;

	params.imgOutlineFilename="Outline%05d.tif";
	params.imgPhantomFilename="Phantom%05d.tif";
	params.imgMaskFilename="Mask%05d.tif";
	params.imgFluoFilename="FluoImg%05d.tif";
	params.imgPhCFilename="PhCImg%05d.tif";
	params.tracksFilename="tracks.txt";


	//re-seed the standard rnd generator
	//(which is used for Perlin texture)
	unsigned int seed=(1 << 20) * (unsigned int)time(NULL) * (unsigned int)getpid();
	srand(seed);
}


bool LoadNewMesh(const char* path,const int ID,const int fileNo)
{
	char fn[1024];

	//first, obtain a fresh volumetric mesh of the cell body
	sprintf(fn,"%s/ID%d/ID%d_t%03d_nucleus.vtk",path,ID,ID,fileNo);
	std::cout << "reading cell body: " << fn << "\n";

	//keep the volumetric configuration of the first load
	bool saveAlsoTetrahedra=(mesh.GetVolIDsize() == 0);
	int retval=mesh.ImportVTK_Volumetric(fn,saveAlsoTetrahedra);
	if (retval) 
	{
		//report errors except for "cannot open file"
		if (retval > 1) 
			std::cout << "some error reading cell body: " << retval << "\n";
		return(false);
	}

	//start loading filopodia objects
	mesh.DiscardAllFilopodiaObjects();
	int filoNo=0;

	do
	{
		++filoNo;
		sprintf(fn,"%s/ID%d/ID%d_t%03d_fTree%02d.vtk",path,ID,ID,fileNo,filoNo);
		std::cout << "reading filopodium: " << fn << "\n";

		retval=mesh.ImportVTK_Ftree(fn,999999);
	} while (retval == 0);

	//report errors except for "cannot open file"
	if (retval > 1) 
	{
		std::cout << "some error reading filopodium: " << retval << "\n";
		return(false);
	}

	//remove last filopodium mesh (which is usually empty...)
	mesh.DiscardLastEmptyFilopodiumObject();

	std::cout << "loaded mesh #" << fileNo << " (with " << filoNo-1 << " filopodia)\n";

	//to be removed after GM is OK
	mesh.ScaleMesh(Vector3F(180.f,180.f,250.f));

	//position the mesh somewhat inside the scene
	mesh.TranslateMesh(params.sceneCentre);

	//keep (by rotating) vertex positions
	mesh.RotateVolPos();

	return(true);
}
