/**------------------------------------------------------------------------
 * @file
 *
 * Copyright 2001 by the University of Toronto (UT).
 *
 *                       All Rights Reserved
 * 
 * Permission to copy and modify this software and its documentation 
 * only for internal use in your organization is hereby granted, 
 * provided that this notice is retained thereon and on all copies.  
 * UT makes no representations as to the suitability and operability 
 * of this software for any purpose.  It is provided "as is" without 
 * express or implied warranty.
 *
 * UT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
 * SHALL UT BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * No other rights, including for example, the right to redistribute 
 * this software and its documentation or the right to prepare 
 * derivative works, are granted unless specifically provided in a 
 * separate license agreement.
 *
 * Author: Diego Macrini
 * 
 * $Log: ShapeMatcher.cpp,v $
 * Revision 1.6  2002/06/17 23:10:55  dmac
 * Cmd line args updated.
 *
 * Revision 1.5  2002/06/13 21:50:06  dmac
 * Cmd line args added.
 *
 * Revision 1.4  2002/06/10 18:43:19  dmac
 * More cmd line params added.
 *
 * Revision 1.3  2002/05/17 00:26:39  dmac
 * Modification to make the code work with any kind of DAG.
 *
 * Revision 1.2  2002/03/12 18:21:11  dmac
 * Bux fixed in Match() function. imgDB.Open(x, false) was replaced by imgDB.Open(x). The 'false' argument is a bug.
 *
 * Revision 1.1.1.1  2002/03/10 07:38:08  dmac
 * Imported sources
 *
 *
 *-----------------------------------------------------------------------*/

#ifdef WIN32
#include <windows.h>
#endif

#include "ShapeMatcher.h"
#include "Matching.h"
#include <BasicUtils.h>

#define CAST(A) (CLP_ACTION)(& ShapeMatcher::A)

using namespace std;

extern fstream g_logFile;	//!< Global log stream;

#ifdef _WINDOWS
int sub_main(int argc, char* argv[]);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
				   PSTR szCmdLine, int iCmdShow)
{
   //int nArgs;
   //return main(nArgs, CommandLineToArgv(GetCommandLine(), &nArgs);
	return sub_main(__argc, __argv);
}

int sub_main(int argc, char* argv[])
#else
int main(int argc, char* argv[])
#endif //_WINDOWS

//int main(int argc, char* argv[])
{ 
	ShapeMatcher clp;

	if (!clp.Parse(argc, argv))
		return -1;

	if (clp.IsSelected('h'))
	{
		clp.ShowHelp();
		return 0;
	}

	if (clp.GetArgCount() <= 0)
	{
		clp.ShowUsageMsg();
		clp.ShowErrorMsg("Missing database file name");
		return -1;
	}

	if (clp.Verbose())
		clp.Print();

	if (clp.IsSelected('c') || clp.IsSelected("mergedb"))
	{
		if (!clp.dagDB.Create(clp.GetArg(0)))
		{
			ShowError("Can't create object database");
			return -1;
		}
	}
	else if (clp.IsSelected('a'))
	{
		if (!clp.dagDB.Open(clp.GetArg(0), ios::out | ios::binary))
		{
			ShowError("Can't open database for adding objects");
			return -1;
		}
		
		if (!clp.IsSGComputationSyncMode())
		{
			ShowError("Due to a bug, object addition will be set to work in synchronous mode.");
			clp.SetSGComputationSyncMode();
		}
	}
	else if (!clp.dagDB.Open(clp.GetArg(0)))
	{
		ShowError("Can't open object database");
		return -1;
	}
	
	// Set global parameters for the DAGs
	DAG::SetMatchParams(clp.m_matchParams);
	
	clp.ExecActions();

	return 0;
}

/*
	Notice that the order of adm_matchParamsdition of 'Actions' matters since it determines
	the priority of the action. If more than one action are selected, 
	the action's priority specifies the execution order.
*/
void ShapeMatcher::InitOptions()
{
	SetOptArgHelp("db-file-name file1 file2 ...");

	// ADD ACTIONS
	Add('d', "debug", "Generate a number of log files", CAST(EnterDbgMode));
	Add('c', "create", "Create database of shock graphs", CAST(AddObjsToDB));
	Add('a', "add", "Add DAG to DB", CAST(AddObjsToDB));
	Add('l', "list", "List DAGs in the database", CAST(ListDAGs));
	Add('s', "size", "Shows the database size in num. of views", CAST(ShowDBSize));
	Add('i', "createIndex", "Explicitly creates the DB index", CAST(CreateDBIndex));
	Add('m', "match", "Match DAG [DAG ID]", &m_target, "", CAST(Match));
	Add('p', "plot", "Plot DAG node info", CAST(PlotNodeInfo));
	Add('v', "view", "Visualize a DAG", &m_viewDag, "", CAST(ViewDAG));
	Add('w', "mview", "Visualize all DAGs", CAST(ViewDAGs));

	Add(0, "nodeMatch", "Match node v1 in graph G1 to v2 in G2", CAST(NodeMatch));
	
	Add(0, "matrix", "Outputs similarity matrix to file [val] for all the models in the database", 
		&m_szMatrixFileName, "matrix.m", CAST(CreateAffinityMatrix));
		
	Add(0, "checkSGGrammar", "Checks the shock graph grammar of all graphs", CAST(CheckSGGrammar));

	Add(0, "delete", "Delete DAGs from database", CAST(DeleteDAG));
	Add(0, "extract", "Extract DAGs from database", CAST(ExtractDAG));
	Add(0, "updatedb", "Read in the db and write it back", CAST(UpdateDB));
	Add(0, "recomputeSG", "Recomputes all SG an saves them in another DB", CAST(RecomputeSG));
	Add(0, "splitdb", "Split database in N%, (100-N)% pieces", &m_nSplitProportion, 50, CAST(SplitDB));
	Add(0, "mergedb", "Merge databases (dest src1 src2 ...)", CAST(MergeDB));
	Add(0, "test", "Test all the object in the DB", CAST(Test));
	Add(0, "objNames", "List object names in the database", CAST(ListObjectNames));
	Add(0, "delObjects", "Removes all dags of given objects", CAST(DeleteObjects));
	Add(0, "clos3NeigList", "List the 3 closest nighbours of each view", CAST(ClosestNeigList));
	Add(0, "clos9NeigList", "List the 9 closest nighbours of each view", CAST(Closest9NeigList));
	Add(0, "getNeig", "List the neighbours withing a given range in deg.", &m_dNeigRange, 180, CAST(GetNeig));
	Add('h', "help", "Display a man page for this program", CAST(ShowHelp));

	// ADD DAG MATCHING PARAMETERS
	Add(0, "tsvSimWeight", "TSV similarity weight", &m_matchParams.dTSVSimWeight, 0.3);
	Add(0, "simMassWeight", "Similarity vs Mass weight", &m_matchParams.dSimilMassWeight, 0.7);
	Add(0, "relMassWeight", "Relative mass of the query vs. tot model", &m_matchParams.dRelMassWeight, 0.5);
	
	Add(0, "simFuncType", "Node similarity function", (int*)(void*)&m_matchParams.nSimFuncType, 1);
	Add(0, "nodeDistFunc", "Node distance function", &m_matchParams.nNodeDistFunc, 3);
	
	Add(0, "minNodeSim", "Minimum node similarity allowed", &m_matchParams.dMinNodeSim, 0.0);
	Add(0, "sibRelPen", "Penalty term for breaking sibling's relationship (1=max)", &m_matchParams.dBreakSibRelPen, 0.2);

	Add(0, "presAncRel", "Preserve ancestor relationships when matching", &m_matchParams.nPreserveAncestorRel, 1);
	Add(0, "useNewVWF", "Use new vote weighting function", &m_matchParams.nUseNewVoteWeightFunc, 1);
	Add(0, "useMOOVC", "Use multiple one-to-one vote counting", &m_matchParams.nUseMOOVC, 1);
		
	// ADD MATCHING PROCESS INFORMATION
	Add(0, "modelSimWeight", "Model similarity weight", &m_matchInfo.modelSimWeight, 0.5);
	Add(0, "idxRange", "Index range", &m_matchInfo.idxrange, 0.4);
	Add(0, "kBest", "Match only k best from index", &m_matchInfo.idxKBest, 101);
	Add(0, "idxTau", "Index similarity threshold", &m_matchInfo.idxtau, 0.3);
	Add(0, "matchTau", "Matcher similarity threshold", &m_matchInfo.matchtau, 0.1);	
	Add(0, "stats", "Compute view statistics", &m_matchInfo.computeStats, 1);
 	Add(0, "asyncCompu", "Compute SG's in async mode (see timeout)", &m_matchInfo.asyncCompu, 0);
 	Add(0, "showResults", "Show matching results", &m_matchInfo.showResults, 1);
 	Add(0, "saveResults", "Save matching results", &m_matchInfo.saveResults, 1);
 	
	// ADD EXPERIMENT PARAMETERS
	Add('o', "objName", "Set object name", &m_objName, "");
	Add(0, "iterations", "Parameter for the SG computation", &m_iterations, 1);
	Add(0, "maxDags", "Maximum number of DAGs to process", &m_maxdags, -1);
	Add(0, "imgStep", "Image step", &m_imgstep, 1);
	Add(0, "from", "Match DAGs from ID N", &m_from, 0);
	Add(0, "to", "Match DAGs up to ID M", &m_to, -1);
	Add(0, "maxOcclRate", "Maximum occlusion rate", &m_maxOcclusionRate, 0.0);
	Add(0, "saveSubsamplingInfo", "Save info to split DB when matching", &m_saveSubsamplingInfo, 0);
	Add(0, "splitByObject", "Split DB by object", &m_nSplitByObject, 1);
	Add(0, "bumpSize", "Size of noise bumps", &m_sgInfo.m_nBumpSize, 0);
	Add(0, "notchSize", "Size of noise notches", &m_sgInfo.m_nNotchSize, 0);
	Add(0, "outDir", "Sets the output directory for noisy images", &m_sgInfo.outDir, "./NoisyImgs/");

	// Shock Graph parameters
	Add(0, "timeout", "SG computation Timeout in seconds", &m_sgInfo.timeout, 800);
	Add(0, "skelcode", "Use skeletoniaztion code number = {1, 2, 3}", &m_sgInfo.skelcode, 3);
	Add(0, "split3s", "Should split 3s into left and right 3s", &m_sgInfo.sgparams.nSlipt3s, 0);
	Add(0, "minslope", "Slope that is to be considered as zero", &m_sgInfo.sgparams.dMinSlope, 0.05);
	Add(0, "minerror", "Coeff for max min-least-square error tolerated", &m_sgInfo.sgparams.dMinError, 2.0);
	Add(0, "maxydiff", "Maximum difference in y-coord discontinuity", &m_sgInfo.sgparams.dMaxYDiff, 0.1);
	Add(0, "skthreshold", "Parameter for the skeleton computation", &m_sgInfo.sgparams.dSkTreshold, 2.5);
	Add(0, "maxaccelchg", "Max acceleration chage tolerated", &m_sgInfo.sgparams.dMaxAccelChg, 0.5);
	
	//TreeSkel params
	Add(0, "tsTau1", "TreeSkel threshold number 1", &m_sgInfo.sgparams.tsTau1, 0);
	Add(0, "tsTau2", "TreeSkel threshold number 2", &m_sgInfo.sgparams.tsTau2, 20);
	Add(0, "bndryWeight", "Boundary reconstruction error weight", &m_sgInfo.sgparams.nBndryRecErrorWeight, 500);
	Add(0, "strucWeight", "Structural reconstruction error weight", &m_sgInfo.sgparams.nStrucRecErrorWeight, 500);
	Add(0, "doExtSimp", "Perform the external branch simplification on the skeleton", &m_sgInfo.sgparams.nSimplifyExternal, 1);
	Add(0, "doIntSimp", "Perform the internal branch simplification on the skeleton", &m_sgInfo.sgparams.nSimplifyInternal, 0);
	Add(0, "doCompleteSimp", "Perform the external as well as the internal branch simplification on the skeleton", &m_sgInfo.sgparams.nSimplifyComplete, 0);
	
	Add(0, "verbose", "Execute in verbose mode", &m_nVerbose, 0);
}

void ShapeMatcher::ShowUsageExamples()
{
	cerr << "\nExamples:\n"
		"\nCreate a database from all the pgm files in a directory and subdirectories:\n"
		"\n\tsm -c objs.db MyImageDir | dm -c -cutoff .15 objs.db MyImageDir\n"
		"\nCreate a database from pgm files:\n"
		"\n\tsm -c objs.db file1.pgm file2.pgm ...\n"
		"\nDelete dags from the database:\n"
		"\n\tsm -delete source.db target.db id1 id2 ...\n"
		"\nList objects stored in the database (ID, name, ...):\n"
		"\n\tsm -l objs.db\n"
		"\nMatch a random object in the DB against all the objects:\n"
		"\n\tsm -m objs.db \nor\n\tdm -m -idxtau .6 objs.db\n"
		"\nMatch a particular object in the DB against all the objects:\n"
		"\n\tsm -m obj-id objs.db\n"
		"\nMatch all objects in a db against those in another db:\n"
		"\n\tsm -m querys.db models.db\n"
		"\nCreate a similarity (MATLAB) matrix for all objects in a database:\n"
		"\n\tsm -matrix simmat.m models.db\n"
		"\nDisplay DAG(s):\n"
		"\n\tsm -v obj-id objs.db \nor\n\tdm -w objs.db [obj-id obj-id...]\n";

	cerr << endl;
}

bool ShapeMatcher::AddObjsToDB()
{
	DirWalker dirDw, fileDw;
	char szDirName[MAX_PATH_SIZE], szFileName[MAX_PATH_SIZE];
	char fileExt[4];
	int imgNumber;

	ShowMsg("% Adding objects to database...");

	for (int i = 1; i < GetArgCount(); i++)
	{
		if (DirWalker::IsDirectory(GetArg(i)))
		{
			DirWalker::CreateFileName(".", GetArg(0), "log", szFileName);
			g_logFile.open(szFileName, ios::out | ios::trunc);

			if (!dirDw.OpenDir(GetArg(i)))
			{
				g_logFile << "Error: Can't open " << GetArg(i) << endl;
				return false;
			}
	
			for (int j = 0; j < GetArgCount(); j++)
				g_logFile << GetArg(j) << ", ";
				
			g_logFile << endl;
			Print(g_logFile);

			strcpy(szDirName, GetArg(i));

			do
			{
				g_logFile << endl << szDirName;
				g_logFile.flush();

				if (!fileDw.OpenDir(szDirName))
				{
					g_logFile << "Error: Can't open " << szDirName << endl;
					continue;
				}

				imgNumber = 0;

				while (fileDw.GetNextFile(szFileName, MAX_PATH_SIZE, fileExt))
				{
					if (!(imgNumber++ % m_imgstep))
						ProcessFile(szFileName, dagDB, g_logFile, m_sgInfo, m_matchInfo.asyncCompu != 0);
				}

				fileDw.CloseDir();

			} while (dirDw.GetNextDir(szDirName, MAX_PATH_SIZE));
		}
		else
		{

			g_logFile.open("dbcreation.log", ios::out | ios::app);

			ProcessFile(GetArg(i), dagDB, g_logFile, m_sgInfo, m_matchInfo.asyncCompu != 0);

		}
		
		g_logFile.close();
	}

	ShowMsg("... all objects have been added.\n");

	return true;
}

bool ShapeMatcher::ListDAGs()
{
	dagDB.ListDAGs();

	return true;
}

bool ShapeMatcher::ListObjectNames()
{
	String lastObjName;
	DAGPtr ptrDag;
	int nObjIdx = 0;
	
	cout.flags(ios::left);
	
	for (int i = 0; i < dagDB.GetDAGCount(); i++)
	{
		ptrDag = dagDB.ReadDAG(i, true);
		
		if (lastObjName != ptrDag->GetObjName())
		{
			cout << setw(5) << nObjIdx << setw(30) << ptrDag->GetObjName() << endl;
			lastObjName = ptrDag->GetObjName();
			nObjIdx++;
		}
	}

	return true;
}

bool ShapeMatcher::DeleteObjects()
{
	DAGPtr ptrDag;
	DAGDatabase db1;

	if (GetArgCount() < 3)
	{
		ShowError("Usage: dbname.db newdb.db objname1 objname2 ...");
		return false;
	}	

	if (!db1.Create(GetArg(1)))
	{
		ShowErrorMsg("Can't create the requested database");
		return false;
	}

	bool bDelObj;
		
	for (int i = 0; i < dagDB.GetDAGCount(); i++)
	{
		ptrDag = dagDB.ReadDAG(i, true);
		bDelObj = false;

		for (int j = 2; j < GetArgCount(); j++)
			if (!strcasecmp(GetArg(j), ptrDag->GetObjName()))
			{
				bDelObj = true;
				break;
			}
		
		if (!bDelObj)
			db1.AddDAG(ptrDag);
	}

	return true;
}

bool ShapeMatcher::ShowDBSize()
{
	cout << dagDB.GetDAGCount() << endl;

	return true;
}

bool ShapeMatcher::UpdateDB()
{
	DAGPtr ptrDag;
	DAGDatabase db1;

	ASSERT(GetArgCount() == 2);

	cerr << "\nUpdating database...\n";

	if (!db1.Create(GetArg(1)))
	{
		ShowErrorMsg("Can't create the requested database");
		return false;
	}

	for (int i = 0; i < dagDB.GetDAGCount(); i++)
	{
		ptrDag = dagDB.ReadDAG(i);
		cerr << endl << ptrDag->GetDAGLbl();
		ptrDag->ComputeDerivedValues();
		db1.AddDAG(ptrDag);
	}

	cerr << "\nDone!\n";

	return true;
}

bool ShapeMatcher::RecomputeSG()
{
	DAGPtr ptrDag;
	DAGDatabase db1;

	ASSERT(GetArgCount() == 2);

	cerr << "\nRecomputing SG database...\n";

	if (!db1.Create(GetArg(1)))
	{
		ShowErrorMsg("Can't create the requested database");
		return false;
	}

	for (int i = 0; i < dagDB.GetDAGCount(); i++)
	{
		ptrDag = dagDB.ReadDAG(i);
		cerr << endl << ptrDag->GetDAGLbl() << endl;

		ShockGraph* pSG = (ShockGraph*)(const DAG*) ptrDag;
		
		pSG->RecomputeFromSkeleton(m_sgInfo.sgparams);

		db1.AddDAG(ptrDag);
	}

	cerr << "\nDone!\n";

	return true;
}

bool ShapeMatcher::ExtractDAG()
{
	DAGPtr ptrDag;
	DAGDatabase db1;

	ASSERT(GetArgCount() > 2);

	cerr << "\nCreating copy of database...\n";

	if (!db1.Create(GetArg(1)))
	{
		ShowErrorMsg("Can't create the requested database");
		return false;
	}
	
	// Collect all the IDs to remove
	int i, j, n = GetArgCount();
	std::vector<int> ids(n); //has extra space
	bool bDeleteDAG;
	
	for (j = 2; j < n; j++)
	{
		ids[j] = atoi(GetArg(j));
		
		if (ids[j] == 0 && GetArg(j)[0] != '0')
		{
			cerr << "\n" << GetArg(j) << " is not a valid DAG ID\n";
			return false;
		}			
	}

	// inefficient but simple batch removal of the dags
	for (i = 0; i < dagDB.GetDAGCount(); i++)
	{
		bDeleteDAG = true;
		
		for (j = 2; j < n; j++)
		{
			if (ids[j] == i)
			{
				cout << "Deleting DAG ID: " << i << endl;
				bDeleteDAG = false;
				break; // don't copy this dag
			}
		}
				
		if (!bDeleteDAG)
			db1.AddDAG(dagDB.ReadDAG(i));
	}

	cerr << "\nDone!\n";

	return true;
}

bool ShapeMatcher::DeleteDAG()
{
	DAGPtr ptrDag;
	DAGDatabase db1;

	ASSERT(GetArgCount() > 2);

	cerr << "\nCreating copy of database...\n";

	if (!db1.Create(GetArg(1)))
	{
		ShowErrorMsg("Can't create the requested database");
		return false;
	}
	
	// Collect all the IDs to remove
	int i, j, n = GetArgCount();
	std::vector<int> ids(n); //has extra space
	bool bDeleteDAG;
	
	for (j = 2; j < n; j++)
	{
		ids[j] = atoi(GetArg(j));
		
		if (ids[j] == 0 && GetArg(j)[0] != '0')
		{
			cerr << "\n" << GetArg(j) << " is not a valid DAG ID\n";
			return false;
		}			
	}

	// inefficient but simple batch removal of the dags
	for (i = 0; i < dagDB.GetDAGCount(); i++)
	{
		bDeleteDAG = false;
		
		for (j = 2; j < n; j++)
		{
			if (ids[j] == i)
			{
				cout << "Deleting DAG ID: " << i << endl;
				bDeleteDAG = true;
				break; // don't copy this dag
			}
		}
				
		if (!bDeleteDAG)
			db1.AddDAG(dagDB.ReadDAG(i));
	}

	cerr << "\nDone!\n";

	return true;
}

bool ShapeMatcher::SplitDB()
{
	DAGPtr ptrDag;
	DAGDatabase db1, db2;
	int i;

	ASSERT(GetArgCount() == 3);

	if (!db1.Create(GetArg(1)) || !db2.Create(GetArg(2)))
	{
		ShowErrorMsg("Can't create the requested databases");
		return false;
	}

	int nDAGCount = dagDB.GetDAGCount();
	
	char szFileName[MAX_PATH_SIZE];
	ViewSubsampler vs;
	VIEWWEIGHT vw;
	
	DirWalker::ChangeFileExt(dagDB.GetFileName(), NULL, szFileName);
	strcat(szFileName, "_subsampling.inf");
	
	// Views are retrieved weighthed according to how badly they are ranked.
	// the worst ranked ones are ranked first and are to be deleted.
	/*if (!vs.RetrieveViewWeights(szFileName, 128, m_nSplitByObject != 0))
	{
		cerr << "Can't retrieve subsampling info from " << szFileName << endl;
		return false;
	}*/
	
	int nMaxRankPos = (int)floor((m_nSplitByObject ? 128:nDAGCount) * (m_nSplitProportion / 100.0));
	
	/*for (i = 0; i < nDAGCount; i++)
	{
		ptrDag = dagDB.ReadDAG(i);
		vw = vs.GetViewRank(ptrDag->GetObjName(), ptrDag->GetViewNumber());	
		
		(vw.nViewRankPos < nMaxRankPos) ? db2.AddDAG(ptrDag):db1.AddDAG(ptrDag);
		
		// DBG
		if (vw.nViewRankPos < nMaxRankPos)
			cout << "(" << ptrDag->GetObjName() << ", " << ptrDag->GetViewNumber() << ")"
				<< " is out. Has rank " << vw.nViewRankPos << " and vote " << vw.dVoteWeight << endl;
	}*/
	SmartArray<int> viewsToRemove; 
	String strLastObjName;

	if (!m_nSplitByObject)
		viewsToRemove = vs.RandomSubsampling(nDAGCount, nMaxRankPos);

	for (i = 0; i < nDAGCount; i++)
	{
		ptrDag = dagDB.ReadDAG(i);
		
		if (m_nSplitByObject && strLastObjName != ptrDag->GetObjName())
		{
			viewsToRemove = vs.RandomSubsampling(128, nMaxRankPos);
			strLastObjName = ptrDag->GetObjName();
		}
		
		(viewsToRemove[ptrDag->GetViewNumber() - 1]) ? db2.AddDAG(ptrDag):db1.AddDAG(ptrDag);
		
		// DBG
		if (viewsToRemove[ptrDag->GetViewNumber() - 1])
			cout << "(" << ptrDag->GetObjName() << ", " << ptrDag->GetViewNumber() << ") is out." << endl;
	}

	return true;
}

bool ShapeMatcher::MergeDB()
{
	DAGDatabase source;
	int i, j;

	cerr << "\nMerging databases...\n";
	
	for (i = 1; i < GetArgCount(); i++)
	{
		if (!source.Open(GetArg(i)))
		{
			cerr << "Can't open " << GetArg(i) << " database." << endl;
			return false;
		}
		
		for (j = 0; j < source.GetDAGCount(); j++)
			dagDB.AddDAG(source.ReadDAG(j));
			
		source.Close();
	}

	cerr << "\nDone!\n";

	return true;
}

bool ShapeMatcher::ViewDAG()
{
	int nDagId;

	// Default value should be last object added
	if (!isdigit(*m_viewDag))
		nDagId = dagDB.GetDAGCount() - 1;
	else
		nDagId = atoi(m_viewDag);
	
	if (nDagId < dagDB.GetDAGCount())
	{
		DAGPtr ptrDag;

		ptrDag = dagDB.ReadDAG(nDagId);

		//ptrDag->Print();

		if (ptrDag->ClassName() == "ShockGraph")
			ShowSkeleton(ptrDag);
		else if (ptrDag->ClassName() == "GestureGraph") {
			ptrDag->Print();
			cout << endl;
			ShowGraph(*ptrDag);
		}
	}
	else
	{
		ShowErrorMsg("Invalid DAG ID");
		return false;
	}

	return true;
}

bool ShapeMatcher::ViewDAGs()
{
	// If there are no arguments (besides the DB name), we show all the SGs.

	if (GetArgCount() <= 1) 
	{
		char szDagId[100];
		m_viewDag = szDagId;

		for (int i = 0; i < dagDB.GetDAGCount(); i++)
		{
			sprintf(szDagId, "%d", i);
			ViewDAG();
		}
	}
	else
	{
		for (int i = 1; i < GetArgCount(); i++)
		{
			m_viewDag = GetArg(i);
			ViewDAG();
		}
	}

	return true;
}

/*!
	@brief Creates the database index if it doen's exist
*/
bool ShapeMatcher::CreateDBIndex()
{
	dagDB.OpenIndex();
	return true;
}

/*!
	@brief Maches node v1 in graph G1 to node v2 in graph G2.
	
	It is usefule manly for testing node similarity functions.
*/
bool ShapeMatcher::NodeMatch()
{
	if (GetArgCount() != 5)
	{
		ShowError("Usage: dm -nodeMatch model.db #node1 graph1ID #node2 graph2ID");
		return false;
	}	
	
	int idxN1 = atoi(GetArg(1));
	int idxG1 = atoi(GetArg(2));
	int idxN2 = atoi(GetArg(3));
	int idxG2 = atoi(GetArg(4));
	
	DAGPtr ptrDag1 = dagDB.ReadDAG(idxG1, true);
	DAGPtr ptrDag2 = dagDB.ReadDAG(idxG2, true);

	leda_node n1 = ptrDag1->GetNode(idxN1);
	leda_node n2 = ptrDag2->GetNode(idxN2);
	
	double sim = ptrDag1->NodeSimilarity(n1, ptrDag2, n2);

	cout << "\nNode " 
		<< idxN1 << " (lbl:" << ptrDag1->GetNodeLbl(n1) << ") in " 
		<< ptrDag1->GetObjName() << "(#" << ptrDag1->GetViewNumber() << ")"
		<< " is " << sim << " similar to node "
		<< idxN2 << " (lbl:" << ptrDag2->GetNodeLbl(n2) << ") in " 
		<< ptrDag2->GetObjName() << "(#" << ptrDag2->GetViewNumber() << ")\n"
		<< endl;

	return true;
}

bool ShapeMatcher::Match()
{
	DAGPtr ptrDag;
	char szFileName[MAX_PATH_SIZE], szFileNameNoExt[MAX_PATH_SIZE];
	fstream os;
	ViewSubsampler vs; // Only used id subsampling info is needed

	dagDB.OpenIndex();

	if (m_matchInfo.saveResults)
	{
		DirWalker::ChangeFileExt(dagDB.GetFileName(), NULL, szFileNameNoExt);
		strcat(szFileNameNoExt, "_results");
			
		DirWalker::CreateFileName(".", szFileNameNoExt, "log", szFileName);
		cerr << "Writing output to " << szFileName << "...\n";
		os.open(szFileName, ios::out | ios::trunc);
		
		Print(os);
	}

	if (strlen(m_target) == 0)
	{
		DAGPtr pTargetDag;

		// Choose target DAG
		for(int j = 0; j < m_iterations; j++)
		{
			STATINFO idxSt, matSt;
			pTargetDag = dagDB.ChooseDAG();
			MatchDAG(pTargetDag, dagDB, m_matchInfo, idxSt, matSt);
			SaveResults(pTargetDag, dagDB, idxSt, matSt, os);
			ShowResults(pTargetDag, matSt, dagDB, dagDB);
		}
	}
	else if (DirWalker::CompFileExt(m_target, "db"))
	{
		DAGDatabase imgDB;
		double dOcclusionRate = 0;
		time_t compTime;
		int nFileIdx;
		
		DirWalker::ChangeFileExt(m_target, NULL, szFileNameNoExt);
		strcat(szFileNameNoExt, "_stats");
		
		nFileIdx = DirWalker::CreateFileName(".", szFileNameNoExt, "m", szFileName);
		fstream plot1(szFileName, ios::trunc | ios::out);
		
		int i = 0;

		if (imgDB.Open(m_target))
		{
			bool bShowRes = m_matchInfo.showResults && AskBool("\nShow matching result for all DAGs");

			if (m_matchInfo.computeStats)
				STATINFO::WriteHeader(plot1, nFileIdx, m_matchInfo.idxKBest, m_maxOcclusionRate);
			
			if (m_to == -1)
				m_to = imgDB.GetDAGCount() - 1;
			else if (m_to >= imgDB.GetDAGCount())
			{
				ShowErrorMsg("'to' value outnumbers the number of objects in the database");
				return false;
			}

			for (int i = m_from; i <= m_to; i++)
			{
				STATINFO idxSt, matSt;
				dOcclusionRate = 0;

				ptrDag = imgDB.ReadDAG(i, true);

				if (m_maxOcclusionRate > 0.0)
					dOcclusionRate = OccludeDAG(ptrDag, m_maxOcclusionRate);
				else
				{
					DirWalker dw;
					dw.ParseFileName(ptrDag->GetDAGLbl());
					dw.ParseFileNameParams();
					
					if (dw.params.size() >= 2)
					{
						map<string, int>::iterator it = dw.params.begin();
						dOcclusionRate = (++it)->second;
					}
				}
					
				cout << "\nMatching " << ptrDag->GetDAGLbl() 
					<< " with occlusion/noise = " << dOcclusionRate << endl;

				compTime = MatchDAG(ptrDag, dagDB, m_matchInfo, idxSt, matSt);
				
				if (m_matchInfo.saveResults)
					SaveResults(ptrDag, dagDB, idxSt, matSt, os);

				if (bShowRes)
					bShowRes = ShowResults(ptrDag, matSt, imgDB, dagDB, i);

				if (m_matchInfo.computeStats)
					STATINFO::Write(plot1, i, ptrDag->GetViewNumber(), idxSt, matSt,
						compTime, dOcclusionRate);
					
				if (m_saveSubsamplingInfo)
					AddSubsamplingInfo(vs, ptrDag, matSt, m_matchInfo.idxKBest);
			}

			if (m_matchInfo.computeStats)
				STATINFO::WriteTailer(plot1);
			
			if (m_saveSubsamplingInfo)
				SaveSubsamplingInfo(vs, dagDB.GetFileName());
		}
		else
			ShowErrorMsg("Can't open target DB");
	}
	else
	{
		int nDagId = atoi(m_target);

		if (nDagId < dagDB.GetDAGCount())
		{
			STATINFO idxSt, matSt;

			ptrDag = dagDB.ReadDAG(nDagId);

			if (GetArgCount() > 1)
			{
				for (int i = 1; i < GetArgCount(); i++)
				{
					MatchDAG(ptrDag, dagDB, m_matchInfo, atoi(GetArg(i)), matSt);
					
					if (m_matchInfo.saveResults)
						SaveResults(ptrDag, dagDB, idxSt, matSt, os);
					
					ShowResults(ptrDag, matSt, dagDB, dagDB, nDagId);
				}
			}
			else
			{
				MatchDAG(ptrDag, dagDB, m_matchInfo, idxSt, matSt);
				
				if (m_matchInfo.saveResults)
					SaveResults(ptrDag, dagDB, idxSt, matSt, os);
					
				ShowResults(ptrDag, matSt, dagDB, dagDB, nDagId);
			}
		}
		else
			ShowErrorMsg("Invalid DAG ID");
	}

	cerr << "\nDone!\n";

	return true;
}

bool ShapeMatcher::PlotNodeInfo()
{
	//char szLbl[100];
	/*int i, j, k;
	bool bRes = true;
	DAGPtr ptrDag;
	leda_node v;
	fstream plt("plots.m", ios::out | ios::trunc);

	const int ROWS = 4, COLS = 4;

	for (i = 1; i < GetArgCount() && bRes; i++)
	{
		const char* sz = GetArg(i);

		if (isdigit(*sz))
			ptrDag = dagDB.ReadDAG(atoi(sz));
		else
			ShowErrorMsg("Invalid DAG ID");

		if (bRes)
		{
			k = 1;
			plt << "figure(" << i << ");\n";

			plt << "hold on;\n";

			forall_nodes(v, sg)
			{
				const SGNode& n = *sg.GetSGNode(v);

				plt << "x = 1:" << n.GetShockCount() << ";\n";
				plt << "y = [";

				for (j = 0; j < n.GetShockCount(); j++)
					plt << n.m_shocks[j].dr_ds << ' ';

				plt << "];\n";

				plt << "subplot(" << ROWS << ", " << COLS << ", " << k++ << ");\n"; 
				plt << "plot(x, y);\n";
				plt << "xlabel('node " << n.GetNodeLbl() << "');\n";*/

				/*plt << "xi = 1:(" << n.GetShockCount() << "-1)/100:" << n.GetShockCount() << ";\n";
				plt << "xi = xi(1, 1:100);\n";
				plt << "yi = interp1(x, y, xi);\n";
				plt << "yi(isnan(yi)) = 0;";
				plt << "plot(xi, yi);\n";
				plt << "xlabel('node " << n.GetNodeLbl() << "');\n";

				plt << "subplot(" << ROWS << ", " << COLS << ", " << k++ << ");\n"; 
				plt << "m(:," << kk++ << ") = fftshift(abs(fft(yi)))';\n";
				plt << "plot(fftshift(abs(fft(yi))));\n";
				plt << "xlabel('node " << n.GetNodeLbl() << "');\n";*/

				/*if (k > ROWS * COLS)
					break;
			}

			plt << "hold off;\npause;\n";
		}
	}

	return bRes;*/

	return true;
}

bool ShapeMatcher::EnterDbgMode()
{
	DAG::EnterDbgMode();
	return true;
}

/*!
	@brief Shows the symmetric view and the 9 closest neighbour views.
*/
bool ShapeMatcher::Closest9NeigList()
{	
	// Check closest views indices...
	for (int i = 1; i <= 128; i++)
	{
		SmartArray<int> secLevClosViews(0, 9);
		GetClosestViews2(i, 3, secLevClosViews, true);
		
		cout << "\nview " << i << " has neighbours: ";
			
		for (int j = 0; j < secLevClosViews.GetSize(); j++) 
		{
			cout << secLevClosViews[j];
			
			if (j == 2)
				cout << " | ";
			else if (j < secLevClosViews.GetSize() - 1) 
				cout << ", ";
		}
		cout << endl;
	}

	return true;
}

/*!
	@brief Shows the symmetric view and the 3 closest neighbour views.
*/
bool ShapeMatcher::ClosestNeigList()
{
	// Check closest views indices...
	int a, b, c;

	SmartArray<int> map = GetSymmetricViewMap();
	
	for (int i = 1; i <= 128; i++)
	{
		GetClosestViews(i, 3, a, b, c);
		
		cout << "\n\nview " << i << " (sym. " << map[i] << ") has neighbours "
			<< a << ", " << b << ", " << c;
	}

	cout << endl << map[0] << endl;

	return true;
}

bool ShapeMatcher::GetNeig()
{
	SmartArray<int> views;

	//views = GetClosestViewsByRange(1, 3, m_dNeigRange);
	views = GetFarthestViews((int)m_dNeigRange, 3, 160.0);
	
	for (int i = 0; i < views.GetSize(); i++)	
		cout << endl << views[i] + 1;

	cout << endl << "Number of views: " << views.GetSize() << endl;

	return true;
}

bool ShapeMatcher::Test()
{
	cout << "\nRunning Test function...\n" << endl;
	
	// Read all dags and...
	DAGPtr ptrDag;
	leda_node v;
	int i, j, nShockCount;
	double y, y0, y00, y1, y11;
	
	for (i = 0; i < dagDB.GetDAGCount(); i++)
	{
		ptrDag = dagDB.ReadDAG(i, true);
		
		if (ptrDag->GetNodeCount() <= 5 || ptrDag->GetNodeCount() >= 40)
		{
			cout << "\n[" << i << "] " <<
				ptrDag->GetDAGLbl() << " -> " << ptrDag->GetNodeCount() << endl;
		
			// Count number of shocks in each node
			forall_nodes(v, *ptrDag)
			{
				const ShockGraph* pSG = dynamic_cast<const ShockGraph*>((const DAG*) ptrDag);
				const SGNode* pNode = pSG->GetSGNode(v);
				nShockCount = pNode->GetShockCount();
				
				for (j = 0; j < nShockCount; j++)
					if ((*pNode)[j].radius <= 1.0)
						cout << "[" << i << "] " << pSG->GetDAGLbl() << ":Node "
							<< pSG->GetNodeLbl(v) << "[" << j << "] has radius = "
							<< (*pNode)[j].radius << endl;
							
				SEGMENTS segs = pNode->GetSegments();
				
				for (j = 0; j < segs.GetSize(); j++)
				{
					y0 = (*pNode)[0].radius;
					y1 = (*pNode)[nShockCount - 1].radius;
				
					if (nShockCount > 1)
					{
						y00 = (*pNode)[1].radius;
						y11 = (*pNode)[nShockCount - 2].radius;
					}
					else
						y00 = y11 = -1;
					
					/*cout << "--------------------------------------------------------------\n";
					DBG_VAL(nShockCount)
					DBG_VAL(segs[j].p0.y)
					DBG_VAL(segs[j].p1.y)
					
					for (k = 0; k < nShockCount; k++)
						cout << (*pNode)[k].radius << ", ";
					
					cout << endl;
					
					DBG_VAL(y0)
					DBG_VAL(y00)
					DBG_VAL(y1)
					DBG_VAL(y11)
					cout << "--------------------------------------------------------------\n";*/
					
					y = segs[j].p0.y;
						
					if (y != y0 && y != y1 && y != y00 && y != y11)
					{
						cout << "There is an error in the information of segment " << j 
							<< " in [" << i << "] " << pSG->GetDAGLbl() << ":Node " << pSG->GetNodeLbl(v)
							<<  endl;
							
						/*DBG_VAL(segs[j].p0.y)
						DBG_VAL(y0)
						DBG_VAL(y00)
						DBG_VAL(y1)
						DBG_VAL(y11)
						DBG_VAL(nShockCount)*/
					}
					
					y = segs[j].p1.y;
					
					if (y != y0 && y != y1 && y != y00 && y != y11)
					{
						cout << "There is an error in the information of segment " << j 
							<< " in [" << i << "] " << pSG->GetDAGLbl() << ":Node " << pSG->GetNodeLbl(v)
							<<  endl;
							
						/*
						DBG_VAL(segs[j].p1.y)
						DBG_VAL(y0)
						DBG_VAL(y00)
						DBG_VAL(y1)
						DBG_VAL(y11)
						DBG_VAL(nShockCount)
						*/
					}
				}
			}
		}
	}

	cout << endl;
	
	cout << "Objects in database: " << dagDB.GetDAGCount() << endl;

	return true;
}

bool ShapeMatcher::ShowHelp()
{
	char line[80];
	ifstream helpFile("help.txt", ios::in /*| ios::nocreate*/);

	if (!helpFile.is_open())
	{
		cerr << "\nError: it can't find help.txt\n";
		return false;
	}

	while (!helpFile.fail())
	{
		helpFile.getline(line, sizeof(line));
		cout << line << '\n';
	}

	cout.flush();
	
	return true;
}

bool ShapeMatcher::CreateAffinityMatrix()
{
	DAGPtr ptrImgDag, ptrModelDag;
	int i, j, n = dagDB.GetDAGCount();
	Matrix m(n, n);
	DAGNodeMap nodeMap;

	m = 0.0; // Init matrix
	
	if (m_to > 0 && m_to < n)
		n = m_to;

	for (i = m_from; i < n; i++)
	{
		ptrImgDag = dagDB.ReadDAG(i, true);

		cout << "\nProcessing " << ptrImgDag->GetObjName()
			<< " view # " << ptrImgDag->GetViewNumber() << "..." << flush;

		for (j = m_from; j < n; j++)
		{
			ptrModelDag = dagDB.ReadDAG(j, true);
			m(i + 1, j + 1) = ptrImgDag->Similarity(ptrModelDag, nodeMap);
		}
	}
	
	cout << endl;

	fstream os(m_szMatrixFileName, ios::out | ios::trunc);

	os << "m = [\n" << setprecision(3) << m << "];\n";

	return true;
}

bool ShapeMatcher::CheckSGGrammar()
{
	DAGPtr ptrDag;
	
	cerr << endl;
	int nErrors = 0;
	ModelFit mf(2); // 2 is only useful when fitting
	int d0, dN;
	bool firstTime = true;
	fstream os;
	char szFileName[MAX_PATH_SIZE];
	
	if (m_to == -1)
		m_to = dagDB.GetDAGCount() - 1;
	
	for (int i = m_from; i <= m_to; i++)
	{
		ptrDag = dagDB.ReadDAG(i, true);
		cerr << "Processing graph " << i << ": " << ptrDag->GetDAGLbl() << endl;

		const ShockGraph* pSG = dynamic_cast<const ShockGraph*>((const DAG*) ptrDag);
    			
		if (pSG == NULL)
			cerr << "The graph is not a shock graph" << endl;
		else
		{
			leda_node u = pSG->CheckGrammar();
			
			if (u == nil)
				cerr << "\tGrammar is valid" << endl;
			else 
			{
				const SGNode* pNode = pSG->GetSGNode(u);
				
				cerr << "\t(" << ++nErrors << ") First grammatical error found at node: " 
					<< pNode->GetLblForGraph() << endl;
					
				if (pSG->NodeType(u) != ROOT)
				{
					// Log useful info to debug grammatical error
					if (firstTime)
					{
						firstTime = false;
						DirWalker::CreateFileName(".", "sgg_shock_info", "m", szFileName);
						os.open(szFileName, ios::out | ios::trunc);
					}	
				
					cerr << "\tWriting node info to " << szFileName << "..." << endl;
					os << "disp('SG ID: " << i << " Label: " << ptrDag->GetDAGLbl() << "')\n";
					os << "disp('Node label: " << pNode->GetLblForGraph() << "')\n";
					mf.Plot(os, pNode->GetVelocityRadiusArray(d0, dN, false), pNode->GetSegments());
					os << "pause\n";	
					os << "\n% --------------------------------------------------------------------\n";
					os.flush();
				}
			}
		}
	}

	cerr << "\nTotal number of errors: " << nErrors << endl;

	return true;
}

