//Author: Yusuf Sahillioglu
//EM algorithm (for isometric coarse correspondence) and coarse-to-fine (C2F) algorithm correspondence (for isometric coarse and dense correspondence)

//x64 release mode is damn fast
//even in dbg mode, i see the benefit of x64: 83 dijkstra on 20K dancing takes 25, 10, and 7 secs on x86-dbg, x64-dbg, x64-release, respectively; 285 dijkstra on dancing takes 91, 39, and 30 secs on x86-dbg, x64-dbg, x64-release, respectively
//50 min coarseToFineMatching() (= EM_ALGO undefined) in x86 debug mode w/ prints, takes just 1.5min (not even 2) here; incredible!!!!!!!!!!!!!!!!!!!!!!!!!
//rarely x64 release mode not reach its optimum performance; if you feel it is not that fast, just delete \ipch and \x64 folders in ShapeCorresponderV2_x64.sln folder and build-run (ctrl+f5, or f7 just to build)

//for slightly faster output, play w/ Mesh.executionTimeOnly boolean to disable file printing of the computed correspondeces, and do your build for a brand new ShapeCorresponder_x64.exe (copy new ShapeCorresponder_x64.exe from x64\Release\ two parents up, i.e. to ShapeCorresponder_x64.sln's folder)
//for exhaustive printing to see almost every computational step, define DBG_DETAIL in Mesh.h, and do your build for a brand new ShapeCorresponder_x64.exe (copy new ShapeCorresponder_x64.exe from x64\Release\ two parents up, i.e. to ShapeCorresponder_x64.sln's folder)

//selected algorithm set by the 1th cmd line argument
#define ORIG				10 //use EM algo introuduced in the following journal:
							//Y. Sahillioglu, Y. Yemez, Minimum-Distortion Isometric Shape Correspondence Using EM Algorithm, PAMI, 2011
							//also used during EM algo: greedy optimization in: Y. Sahillioglu, Y. Yemez, 3D shape correspondence by isometry-driven greedy optimization, CVPR'10
#define BIHARM				20
#define PROP				30
#define PROP_BIHARM			40
#define C2F_ALGO			100 //use C2F algo from:
							//Y. Sahillioglu, Y. Yemez, Coarse-to-Fine Combinatorial Matching for Dense Isometric Shape Correspondence, SGP, 2011.

#include "Model.h" //a screen object
#include <sstream>

#include <windows.h> //for get_time just-below
inline double get_time()
{
	LARGE_INTEGER t, frequency;
	QueryPerformanceCounter(&t);
	QueryPerformanceFrequency(&frequency);
	return (double)t.QuadPart/(double)frequency.QuadPart;
}

int main(int argc, char ** argv)
{
	int trialNum = 1;

	if ((argc == 7 || argc == 9) && (atoi(argv[1]) == ORIG))
	{
		int maxC2Flevel = -1;
		int f1to1 = -1;
		int k = 6;
		bool compDistFromBaseToAll = true;
		bool biharmonicDistUse = false;

		char *outputDirName = argv[2];
		float rFactor = (float) atof(argv[3]);
		//char *srcMeshPath = argv[4];
		//char *tarMeshPath = argv[5];
		int srcFrameID = atoi(argv[4]), targetFrameID = atoi(argv[5]);
		bool useShortcutEdges = atoi(argv[6]) == 1;
		bool groundTruthAvailable = argc == 9;
		char *srcGroundTruthMeshPath;
		char *tarGroundTruthMeshPath;
		if (groundTruthAvailable)
		{
			srcGroundTruthMeshPath = argv[7];
			tarGroundTruthMeshPath = argv[8];
		}

		char srcPathSub[50], srcPath[250];
		sprintf(srcPathSub, "kid%d", srcFrameID);
		sprintf(srcPath, "..\\TOPKIDS_complete\\low resolution\\contest\\%s.off", srcPathSub);
		
		char targetPathSub[50], targetPath[250];
		sprintf(targetPathSub, "kid%d", targetFrameID);
		sprintf(targetPath, "..\\TOPKIDS_complete\\low resolution\\contest\\%s.off", targetPathSub);

/*		string outDir(outputDirName);
		outDir.append("\\orig\\");
		outDir.append(srcPathSub);
		outDir.append("_");
		outDir.append(targetPathSub);
		_mkdir(outDir.c_str());*/

		Model *srcModel = new Model(srcPathSub, srcPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
		srcModel->init();
		
		Model *tarModel = new Model(targetPathSub, targetPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
		tarModel->init();

		srcModel->getMesh()->C2Fmode = tarModel->getMesh()->C2Fmode = false;
		srcModel->getMesh()->biharmVersion = tarModel->getMesh()->biharmVersion = false;
		
		srcModel->getMesh()->initialSpectralMatching(tarModel->getMesh(), k, biharmonicDistUse, compDistFromBaseToAll, trialNum);
		srcModel->getMesh()->greedyOpt(tarModel->getMesh(), true, k);
		srcModel->getMesh()->finalizeMatching(tarModel->getMesh(), k, false, true);

		if (groundTruthAvailable)
		{
			useShortcutEdges = false;

			string srcGTName(srcGroundTruthMeshPath);
			srcGTName = srcGTName.substr(srcGTName.find_last_of("/\\")+1);
			srcGTName = srcGTName.substr(0,srcGTName.find_last_of("."));

			string tarGTName(tarGroundTruthMeshPath);
			tarGTName = tarGTName.substr(tarGTName.find_last_of("/\\")+1);
			tarGTName = tarGTName.substr(0,tarGTName.find_last_of("."));

			Model *srcGTModel = new Model(srcGTName.c_str(), srcGroundTruthMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
			srcGTModel->init();

			Model *tarGTModel = new Model(tarGTName.c_str(), tarGroundTruthMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
			tarGTModel->init();

			vector<int> one2OneCorresp;
			vector<int> many2OneCorresp;
			srcModel->getMesh()->getFinalCorrespResults(one2OneCorresp, many2OneCorresp);

/*			string finalCorrespName = string(srcName);
			finalCorrespName.append("_vs_");
			finalCorrespName.append(tarName);

			srcGTModel->getMesh()->importCorrespWriteErrorsToFile(tarGTModel->getMesh(), one2OneCorresp, many2OneCorresp, finalCorrespName.c_str());*/
		}
	}
	else if ((argc == 8 || argc == 10) && (atoi(argv[1]) == BIHARM))
	{
		int maxC2Flevel = -1;
		int f1to1 = -1;
		int k = 6;
		bool biharmonicDistUse = true;

		char *outputDirName = argv[2];
		float rFactor = (float) atof(argv[3]);
		//char *srcMeshPath = argv[4];
		//char *tarMeshPath = argv[5];
		int srcFrameID = atoi(argv[4]), targetFrameID = atoi(argv[5]);
		bool useShortcutEdges = atoi(argv[6]) == 1;
		bool compDistFromBaseToAll = atoi(argv[7]) == 1;
		bool groundTruthAvailable = argc == 10;
		char *srcGroundTruthMeshPath;
		char *tarGroundTruthMeshPath;
		if (groundTruthAvailable)
		{
			srcGroundTruthMeshPath = argv[8];
			tarGroundTruthMeshPath = argv[9];
		}

		char srcPathSub[50], srcPath[250];
		sprintf(srcPathSub, "kid%d", srcFrameID);
		sprintf(srcPath, "..\\TOPKIDS_complete\\low resolution\\contest\\%s.off", srcPathSub);
//sprintf(srcPathSub, "0001.null.0col"); sprintf(srcPath, "..\\shrec10\\geoBad-biharmGood\\%s.obj", srcPathSub);
		
		char targetPathSub[50], targetPath[250];
		sprintf(targetPathSub, "kid%d", targetFrameID);
		sprintf(targetPath, "..\\TOPKIDS_complete\\low resolution\\contest\\%s.off", targetPathSub);
//sprintf(targetPathSub, "0001.topology.3col");sprintf(targetPath, "..\\shrec10\\geoBad-biharmGood\\%s.obj", targetPathSub);

/*		string outDir(outputDirName);
		outDir.append("\\biharm\\");
		outDir.append(srcName);
		outDir.append("_vs_");
		outDir.append(tarName);
		_mkdir(outDir.c_str());*/

		Model *srcModel = new Model(srcPathSub, srcPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
		srcModel->init();
		
		Model *tarModel = new Model(targetPathSub, targetPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
		tarModel->init();

		srcModel->getMesh()->C2Fmode = tarModel->getMesh()->C2Fmode = false;
		srcModel->getMesh()->biharmVersion = tarModel->getMesh()->biharmVersion = true;

		srcModel->getMesh()->initialSpectralMatching(tarModel->getMesh(), k, biharmonicDistUse, compDistFromBaseToAll, trialNum);
		srcModel->getMesh()->greedyOpt(tarModel->getMesh(), true, k);
		srcModel->getMesh()->finalizeMatching(tarModel->getMesh(), k, false, true);

		if (groundTruthAvailable)
		{
			useShortcutEdges = false;

			string srcGTName(srcGroundTruthMeshPath);
			srcGTName = srcGTName.substr(srcGTName.find_last_of("/\\")+1);
			srcGTName = srcGTName.substr(0,srcGTName.find_last_of("."));

			string tarGTName(tarGroundTruthMeshPath);
			tarGTName = tarGTName.substr(tarGTName.find_last_of("/\\")+1);
			tarGTName = tarGTName.substr(0,tarGTName.find_last_of("."));

			Model *srcGTModel = new Model(srcGTName.c_str(), srcGroundTruthMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
			srcGTModel->init();

			Model *tarGTModel = new Model(tarGTName.c_str(), tarGroundTruthMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
			tarGTModel->init();

			vector<int> one2OneCorresp;
			vector<int> many2OneCorresp;
			srcModel->getMesh()->getFinalCorrespResults(one2OneCorresp, many2OneCorresp);

/*			string finalCorrespName = string(srcName);
			finalCorrespName.append("_vs_");
			finalCorrespName.append(tarName);

			srcGTModel->getMesh()->importCorrespWriteErrorsToFile(tarGTModel->getMesh(), one2OneCorresp, many2OneCorresp, finalCorrespName.c_str());*/
		}
	}
/*	else if ((argc == 9 || argc == 11) && (atoi(argv[1]) == PROP))
	{
		int maxC2Flevel = -1;
		int f1to1 = -1;
		int k = 6;
		bool compDistFromBaseToAll = true;
		bool biharmonicDistUse = false;

		char *outputDirName = argv[2];
		float rFactor = (float) atof(argv[3]);
		char *srcMeshPath = argv[4];
		char *tarMeshPath = argv[5];
		char *srcIsosurfPath = argv[6];
		char *tarIsosurfPath = argv[7];
		bool useShortcutEdges = atoi(argv[8]) == 1;

		bool groundTruthAvailable = argc == 11;
		char *srcGroundTruthMeshPath;
		char *tarGroundTruthMeshPath;
		if (groundTruthAvailable)
		{
			srcGroundTruthMeshPath = argv[9];
			tarGroundTruthMeshPath = argv[10];
		}

		string srcIsosurf(srcIsosurfPath);
		srcIsosurf = srcIsosurf.substr(srcIsosurf.find_last_of("/\\")+1);
		srcIsosurf = srcIsosurf.substr(0,srcIsosurf.find_last_of("."));
		srcIsosurf = srcIsosurf.substr(0,srcIsosurf.find_last_of("_"));
		srcIsosurf = srcIsosurf.substr(0,srcIsosurf.find_last_of("_"));

		string tarIsosurf(tarIsosurfPath);
		tarIsosurf = tarIsosurf.substr(tarIsosurf.find_last_of("/\\")+1);
		tarIsosurf = tarIsosurf.substr(0,tarIsosurf.find_last_of("."));
		tarIsosurf = tarIsosurf.substr(0,tarIsosurf.find_last_of("_"));
		tarIsosurf = tarIsosurf.substr(0,tarIsosurf.find_last_of("_"));

		string srcName(srcMeshPath);
		srcName = srcName.substr(srcName.find_last_of("/\\")+1);
		srcName = srcName.substr(0,srcName.find_last_of("."));

		string tarName(tarMeshPath);
		tarName = tarName.substr(tarName.find_last_of("/\\")+1);
		tarName = tarName.substr(0,tarName.find_last_of("."));

		string outDir(outputDirName);
		outDir.append("\\prop\\");
		outDir.append(srcIsosurf);
		outDir.append("_vs_");
		outDir.append(tarIsosurf);

		_mkdir(outDir.c_str());

		Model *isoSrcModel = new Model(outDir.c_str(), srcIsosurf.c_str(), srcIsosurfPath, useShortcutEdges, false, maxC2Flevel, f1to1, rFactor);
		isoSrcModel->init();

		Model *isoTarModel = new Model(outDir.c_str(), tarIsosurf.c_str(), tarIsosurfPath, useShortcutEdges, false, maxC2Flevel, f1to1, rFactor);
		isoTarModel->init();

		isoSrcModel->getMesh()->C2Fmode = isoTarModel->getMesh()->C2Fmode = false;
		
		isoSrcModel->getMesh()->initialSpectralMatching(isoTarModel->getMesh(), k, biharmonicDistUse, compDistFromBaseToAll, trialNum);
		isoSrcModel->getMesh()->greedyOpt(isoTarModel->getMesh(), true, k);
		isoSrcModel->getMesh()->finalizeMatching(isoTarModel->getMesh(), k, false, false);

		assert(isoSrcModel->getMesh()->baseVerts.size() == isoTarModel->getMesh()->baseVerts.size());

		Model *srcModel = new Model(outDir.c_str(), srcName.c_str(), srcMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
		srcModel->init();
		
		Model *tarModel = new Model(outDir.c_str(), tarName.c_str(), tarMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
		tarModel->init();

		srcModel->getMesh()->importFinalCorrespResults(tarModel->getMesh(), isoSrcModel->getMesh(), isoTarModel->getMesh());

		if (groundTruthAvailable)
		{
			useShortcutEdges = false;

			string srcGTName(srcGroundTruthMeshPath);
			srcGTName = srcGTName.substr(srcGTName.find_last_of("/\\")+1);
			srcGTName = srcGTName.substr(0,srcGTName.find_last_of("."));

			string tarGTName(tarGroundTruthMeshPath);
			tarGTName = tarGTName.substr(tarGTName.find_last_of("/\\")+1);
			tarGTName = tarGTName.substr(0,tarGTName.find_last_of("."));

			Model *srcGTModel = new Model(outDir.c_str(), srcGTName.c_str(), srcGroundTruthMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
			srcGTModel->init();

			Model *tarGTModel = new Model(outDir.c_str(), tarGTName.c_str(), tarGroundTruthMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
			tarGTModel->init();

			vector<int> one2OneCorresp;
			vector<int> many2OneCorresp;
			srcModel->getMesh()->getFinalCorrespResults(one2OneCorresp, many2OneCorresp);

			string finalCorrespName = string(srcName);
			finalCorrespName.append("_vs_");
			finalCorrespName.append(tarName);

			srcGTModel->getMesh()->importCorrespWriteErrorsToFile(tarGTModel->getMesh(), one2OneCorresp, many2OneCorresp, finalCorrespName.c_str());
		}
	}
	else if ((argc == 10 || argc == 12) && (atoi(argv[1]) == PROP_BIHARM))
	{
		int maxC2Flevel = -1;
		int f1to1 = -1;
		int k = 6;
		bool biharmonicDistUse = true;

		char *outputDirName = argv[2];
		float rFactor = (float) atof(argv[3]);
		char *srcMeshPath = argv[4];
		char *tarMeshPath = argv[5];
		char *srcIsosurfPath = argv[6];
		char *tarIsosurfPath = argv[7];
		bool useShortcutEdges = atoi(argv[8]) == 1;
		bool compDistFromBaseToAll = atoi(argv[9]) == 1;

		bool groundTruthAvailable = argc == 12;
		char *srcGroundTruthMeshPath;
		char *tarGroundTruthMeshPath;
		if (groundTruthAvailable)
		{
			srcGroundTruthMeshPath = argv[10];
			tarGroundTruthMeshPath = argv[11];
		}

		string srcIsosurf(srcIsosurfPath);
		srcIsosurf = srcIsosurf.substr(srcIsosurf.find_last_of("/\\")+1);
		srcIsosurf = srcIsosurf.substr(0,srcIsosurf.find_last_of("."));
		srcIsosurf = srcIsosurf.substr(0,srcIsosurf.find_last_of("_"));
		srcIsosurf = srcIsosurf.substr(0,srcIsosurf.find_last_of("_"));

		string tarIsosurf(tarIsosurfPath);
		tarIsosurf = tarIsosurf.substr(tarIsosurf.find_last_of("/\\")+1);
		tarIsosurf = tarIsosurf.substr(0,tarIsosurf.find_last_of("."));
		tarIsosurf = tarIsosurf.substr(0,tarIsosurf.find_last_of("_"));
		tarIsosurf = tarIsosurf.substr(0,tarIsosurf.find_last_of("_"));

		string srcName(srcMeshPath);
		srcName = srcName.substr(srcName.find_last_of("/\\")+1);
		srcName = srcName.substr(0,srcName.find_last_of("."));

		string tarName(tarMeshPath);
		tarName = tarName.substr(tarName.find_last_of("/\\")+1);
		tarName = tarName.substr(0,tarName.find_last_of("."));

		string outDir(outputDirName);
		outDir.append("\\propbiharm\\");
		outDir.append(srcIsosurf);
		outDir.append("_vs_");
		outDir.append(tarIsosurf);

		_mkdir(outDir.c_str());

		Model *isoSrcModel = new Model(outDir.c_str(), srcIsosurf.c_str(), srcIsosurfPath, useShortcutEdges, false, maxC2Flevel, f1to1, rFactor);
		isoSrcModel->init();

		Model *isoTarModel = new Model(outDir.c_str(), tarIsosurf.c_str(), tarIsosurfPath, useShortcutEdges, false, maxC2Flevel, f1to1, rFactor);
		isoTarModel->init();

		isoSrcModel->getMesh()->C2Fmode = isoTarModel->getMesh()->C2Fmode = false;
		
		isoSrcModel->getMesh()->initialSpectralMatching(isoTarModel->getMesh(), k, biharmonicDistUse, compDistFromBaseToAll, trialNum);
		isoSrcModel->getMesh()->greedyOpt(isoTarModel->getMesh(), true, k);
		isoSrcModel->getMesh()->finalizeMatching(isoTarModel->getMesh(), k, false, false);

		assert(isoSrcModel->getMesh()->baseVerts.size() == isoTarModel->getMesh()->baseVerts.size());

		Model *srcModel = new Model(outDir.c_str(), srcName.c_str(), srcMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
		srcModel->init();
		
		Model *tarModel = new Model(outDir.c_str(), tarName.c_str(), tarMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
		tarModel->init();

		srcModel->getMesh()->importFinalCorrespResults(tarModel->getMesh(), isoSrcModel->getMesh(), isoTarModel->getMesh());

		if (groundTruthAvailable)
		{
			useShortcutEdges = false;

			string srcGTName(srcGroundTruthMeshPath);
			srcGTName = srcGTName.substr(srcGTName.find_last_of("/\\")+1);
			srcGTName = srcGTName.substr(0,srcGTName.find_last_of("."));

			string tarGTName(tarGroundTruthMeshPath);
			tarGTName = tarGTName.substr(tarGTName.find_last_of("/\\")+1);
			tarGTName = tarGTName.substr(0,tarGTName.find_last_of("."));

			Model *srcGTModel = new Model(outDir.c_str(), srcGTName.c_str(), srcGroundTruthMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
			srcGTModel->init();

			Model *tarGTModel = new Model(outDir.c_str(), tarGTName.c_str(), tarGroundTruthMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
			tarGTModel->init();

			vector<int> one2OneCorresp;
			vector<int> many2OneCorresp;
			srcModel->getMesh()->getFinalCorrespResults(one2OneCorresp, many2OneCorresp);

			string finalCorrespName = string(srcName);
			finalCorrespName.append("_vs_");
			finalCorrespName.append(tarName);

			srcGTModel->getMesh()->importCorrespWriteErrorsToFile(tarGTModel->getMesh(), one2OneCorresp, many2OneCorresp, finalCorrespName.c_str());
		}
	}
	else if (argc == 8 && atoi(argv[1]) == C2F_ALGO)
	{
		float rFactor = 1.35f;
		char *sourceMeshPath = argv[2];
		char *targetMeshPath = argv[3];
		bool useShortcutEdges = atoi(argv[4]) == 1;
		bool groundTruthAvailable = atoi(argv[5]) == 1;
		int maxC2Flevel = atoi(argv[6]);
		int f1to1 = atoi(argv[7]);

		string srcName(sourceMeshPath);
		srcName = srcName.substr(srcName.find_last_of("/\\")+1);
		srcName = srcName.substr(0,srcName.find_last_of("."));

		string tarName(targetMeshPath);
		tarName = tarName.substr(tarName.find_last_of("/\\")+1);
		tarName = tarName.substr(0,tarName.find_last_of("."));

		string outDir("output\\C2F\\");
		outDir.append(srcName);
		outDir.append("_versus_");
		outDir.append(tarName);

		_mkdir(outDir.c_str());

		Model *jModel = new Model(outDir.c_str(), srcName.c_str(), sourceMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
		jModel->init();

		Model *jModel2 = new Model(outDir.c_str(), tarName.c_str(), targetMeshPath, useShortcutEdges, groundTruthAvailable, maxC2Flevel, f1to1, rFactor);
		jModel2->init();

		double start_time = get_time();

		jModel->getMesh()->C2Fmode = jModel2->getMesh()->C2Fmode = true;
		jModel->getMesh()->coarseToFineMatching( jModel2->getMesh() );

		cout << get_time()-start_time << " secs for all computations (excluding load time of 2 input meshes)" << endl;
	}*/
	else
	{
		system("pause");

		exit(-1);
	}

	return 0;
}
