/**------------------------------------------------------------------------
 * @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.1.1.1  2002/03/10 07:38:08  dmac
 * Imported sources
 *
 *-----------------------------------------------------------------------*/
#include <map>

#ifdef WIN32
#include <windows.h>
#include <process.h>
#else
#define stricmp strcmp
#endif

#include <ShockGraphView.h>
#include <BoneGraphView.h>
#include "ShapeMatcher.h"
#include "MatchingResults.h"
#include "Matching.h"

using namespace std;

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

bool g_bLogCrashReport = false;
LogFile g_crashReportLog("rel_crash_report.log", &g_bLogCrashReport);

#ifdef WIN32
DWORD WINAPI AsyncRecomputeGraph(LPVOID lpParam)
#else
int AsyncRecomputeGraph(void* lpParam)
#endif
{
	typedef std::pair<ShapeMatcher*, int> PARAM_TYPE;

	PARAM_TYPE* pParams = (PARAM_TYPE*) lpParam;

	try
	{
		if (!pParams->first->RecomputeGraph(pParams->second))
			return 1;
	}
	catch (int v)
	{
		return v;
	}
	catch (...)
	{
		return 2;
	}

	return 0;
}

#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
{
#if defined(WIN32) && defined(_DEBUG)
	HANDLE hLogFile;

	hLogFile = CreateFile("dbg_crash_report.log", GENERIC_WRITE,
      FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
      FILE_ATTRIBUTE_NORMAL, NULL);

	// Redirect the memory leaks reports (_CRT_WARN class) to hLogFile
	_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
	_CrtSetReportFile(_CRT_WARN, hLogFile);

	//_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); // doesn't work

	// The memory dump in the error report file can be used
	// to set breakpoints at leaky memory allocation instructions
	// e.g., _CrtSetBreakAlloc(x) sets a break at memory allocation number X

	//_CrtSetBreakAlloc(1100);

	// Current unresolved memory leaks
	//_CrtSetBreakAlloc(149); // simpleshape.h in flux skeleton
	//_CrtSetBreakAlloc(5445); // ann.cpp line 117 (must dealloc point)
	//_CrtSetBreakAlloc(88651); // ?
	//_CrtSetBreakAlloc(88654); // SmartArray line 481 (Resize may not free memory)
#else
	g_bLogCrashReport = true;
#endif //_DEBUG

	// Allocate a Shapematcher in the heap so that it can be shared
	// by multiple threads and, also, can be destroyed before the
	// process ends (for debugging momory leaks)
	ShapeMatcher* pCLP = new ShapeMatcher;
	ShapeMatcher& clp = *pCLP; // create a handy shortcut

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

#if defined(WIN32) && defined(_DEBUG)
	// In asyncronous mode, we don't want the debugger to break the execution
	// so the output must be changed from _CRTDBG_FILE_STDOUT to hLogFile
	if (clp.AsyncMode())
	{
		_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
		_CrtSetReportFile(_CRT_ASSERT, hLogFile);
	}
#endif

	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.m_dagDB.Create(clp.GetArg(0)))
		{
			ShowCreateFileError(clp.GetArg(0));
			return -1;
		}
	}
	else if (clp.IsSelected('a'))
	{
		if (!clp.m_dagDB.Open(clp.GetArg(0), ios::out | ios::binary))
		{
			ShowOpenFileError(clp.GetArg(0));
			ShowError("File cannot be opened for writing");
			return -1;
		}

		if (clp.AsyncMode())
		{
			ShowError("Due to a bug, object addition will be"
				" set to work in synchronous mode.");

			clp.SetSyncMode(true);
		}
	}
	else if (clp.IsSelected('M') || clp.IsSelected('S'))
	{
		// there is nothing to do here
		cerr << "Reading multiple databases..." << endl;
	}
	else if (clp.IsSelected("matchDBP"))
	{
		// there is nothing to do here
		cerr << "Starting parsing mode..." << endl;
	}
	else if (!clp.m_dagDB.Open(clp.GetArg(0)))
	{
		ShowOpenFileError(clp.GetArg(0));
		return -1;
	}

	// Set global parameters for the DAGs
	DAGMatcher::SetMatchParams(clp.m_matchParams);

	clp.ExecActions();

	// If we read cmd-line params from a file, we probably are running from within VS
	// and must delay the exit process to keep the console window visible
	//if (clp.AreParamsFromFile())
	//	getchar();

	// Destor the ShapeMatcher now, so that we can check for memory leaks
	delete pCLP;

#if defined(WIN32) && defined(_DEBUG)
	// See if there are errors reported that are not memory leaks
	DWORD sz = GetFileSize(hLogFile, NULL);

	if (sz > 0)
		ShowErrorAndNumber("Error report file is not empty. size =", sz);

	// Dump the memory leak errors
	_CrtDumpMemoryLeaks();
	_RPT0(_CRT_WARN,"end of memory dump :-)\n");

	CloseHandle(hLogFile);
#endif // _DEBUG

	return 0;
}

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::HasWildcards(GetArg(i)))
		{
			DirWalker::CreateUniqueFileName(".", 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, m_dagDB, g_logFile, m_shapeInfo, 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), m_dagDB, g_logFile, m_shapeInfo, m_matchInfo.asyncCompu != 0);
		}

		g_logFile.close();
	}

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

	return true;
}

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

	return true;
}

bool ShapeMatcher::ListObjectNames()
{
	String lastObjName;
	DAGPtr ptrDag;
	int nObjIdx = 0;

	cout.flags(ios::left);

	for (int i = 0; i < m_dagDB.GetDAGCount(); i++)
	{
		ptrDag = m_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::ShowDAGTable()
{
	int nr = 9, nc = 11;

	std::list<DAGPtr> dags;
	SmartMatrix<const DAG*> dagMat(nr, nc);
	int r, c;

	for (int i = 0; i < m_dagDB.GetDAGCount(); i++)
	{
		dags.push_back(m_dagDB.ReadDAG(i, false));

		r = i / nc;
		c = i % nc;

		dagMat[r][c] = (const DAG*) dags.back();

		if (r == nr - 1 && c == nc - 1)
			break;
	}

	BoneGraphView bgview;

	bgview.SetBlackShapeMode();

	return bgview.Create(dagMat);

	return true;
}

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

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

	if (!db1.Create(GetArg(1)))
	{
		ShowCreateFileError(GetArg(1));
		return false;
	}

	bool bDelObj;

	for (int i = 0; i < m_dagDB.GetDAGCount(); i++)
	{
		ptrDag = m_dagDB.ReadDAG(i);
		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::CopyObjects()
{
	DAGPtr ptrDag;
	DAGDatabase db1;

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

	if (!db1.Create(GetArg(1)))
	{
		ShowCreateFileError(GetArg(1));
		return false;
	}

	std::vector< std::vector<int> > objs(11);
	int c, d;

	for (int j = 2; j < GetArgCount(); j++)
	{
		sscanf(GetArg(j), "%d:%d", &c, &d);

		objs[c - 1].push_back(d - 1);
	}

	bool bCopyObj;

	char className[MAX_PATH_SIZE], prevName[MAX_PATH_SIZE];
	int modelId, prevId, midx = 0, cidx = 0;

	for (int i = 0; i < m_dagDB.GetDAGCount(); i++)
	{
		ptrDag = m_dagDB.ReadDAG(i);
		bCopyObj = false;

		ptrDag->GetClassAndModel(className, &modelId);

		//ShowStatus2("Reading", modelId, className);

		if (i == 0)
		{
			strcpy(prevName, className);
			prevId = modelId;
			//ShowStatus("init class model");
		}

		if (strcmp(className, prevName) != 0)
		{
			cidx++;
			midx = 0;

			//ShowStatus2("changing from", prevId, prevName);
			//ShowStatus2("to", modelId, className);

			strcpy(prevName, className);
			prevId = modelId;
		}
		else if (modelId != prevId)
		{
			midx++;
			prevId = modelId;

			//ShowStatus("inc model");
		}

		for (unsigned int j = 0; j < objs[cidx].size(); j++)
		{
			if (objs[cidx][j] == midx)
			{
				bCopyObj = true;
				break;
			}
		}

		if (bCopyObj)
		{
			ShowStatus2("Copying", modelId, className);
			db1.AddDAG(ptrDag);
		}
	}

	return true;
}

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

	return true;
}

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

	ASSERT(GetArgCount() == 2);

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

	if (!db1.Create(GetArg(1)))
	{
		ShowCreateFileError(GetArg(1));
		return false;
	}

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

	cerr << "\nDone!\n";

	return true;
}

bool ShapeMatcher::RecomputeGraph(int dagID)
{
	const bool bKeepType = (m_shapeInfo.shapeRepType <= 0);
	DAG* pNewDAG = NULL;
	SkeletalGraph* pSkeleton;
	int type;

	DAGPtr ptrDag = m_dagDB.ReadDAG(dagID);

	ShowStatus2("Recomputing DAG", dagID, ptrDag->GetDAGLbl());

	if (ptrDag->ClassName() == "ShockGraph")
	{
		ShockGraph* pSG = (ShockGraph*)(const DAG*) ptrDag;

		pSkeleton = pSG->ReleaseSkeleton();

		type = (bKeepType) ? 1 : m_shapeInfo.shapeRepType;
	}
	else if (ptrDag->ClassName() == "BoneGraph")
	{
		BoneGraph* pBG = (BoneGraph*)(const DAG*) ptrDag;

		pSkeleton = pBG->ReleaseSkeleton();

		type = (bKeepType) ? 3 : m_shapeInfo.shapeRepType;
	}
	else
	{
		ShowError("Unknown DAG in source database");
		return false;
	}

	bool bRecomputed;

	if (type == 1)
	{
		ShockGraph* pSG = new ShockGraph;

		pNewDAG = pSG;

		bRecomputed = pSG->Create(pSkeleton, ptrDag->GetDims(),
			ptrDag->GetDAGLbl(), m_shapeInfo.sgparams);
	}
	else if (type == 3)
	{
		BoneGraph* pBG = new BoneGraph;

		pNewDAG = pBG;

		bRecomputed = pBG->Create(pSkeleton, ptrDag->GetDims(),
			ptrDag->GetDAGLbl(), m_shapeInfo.bgparams);
	}
	else
	{
		ShowError("Invalid DAG type");
		return false;
	}

	if (bRecomputed)
		m_tgtDagDB.AddDAG(pNewDAG);
	else
		ShowErrorAndNumber("Cannot recompute graph with id", dagID);

	delete pNewDAG;

	return true;
}

bool ShapeMatcher::RecomputeAndSaveGraphs(const char* szTgtDBFilename, bool bCheckExist)
{
	const char* szFileExt;

	szFileExt = DirWalker::FindFileExtension(szTgtDBFilename);

	if (strcmp(szFileExt, "db") != 0)
	{
		char cAnswer;

		cout << "\nWarning: The target database name '" << szTgtDBFilename <<
			"' does not have the standard .db extension.\n"
			"If this file is not a valid database, the data in it will be lost.\n"
			"Do you want to continue?[y/n]: " << flush;

		cin >> cAnswer;

		if (cAnswer != 'y' && cAnswer != 'Y')
			return false;
	}

	if (!m_tgtDagDB.Create(szTgtDBFilename, bCheckExist))
	{
		ShowCreateFileError(szTgtDBFilename);
		return false;
	}

	const int lastPos = GetLastDAGId();

	if (m_matchInfo.asyncCompu == 0)
	{
		for (int i = m_from; i <= lastPos; i++)
			RecomputeGraph(i);
	}
	else
	{
#ifdef WIN32
		DWORD dwThreadId;
		HANDLE hThread;
		DWORD exitCode;
		std::pair<ShapeMatcher*, int> params(this, 0);

		int numThreads = 1;

		for (int i = m_from; i <= lastPos; i++)
		{
			params.second = i;

			if (numThreads > 1)
			{
				hThread = CreateThread(NULL, 0, AsyncRecomputeGraph,
					&params, 0, &dwThreadId);

				WaitForSingleObject(hThread, INFINITE);

				if (!GetExitCodeThread(hThread, &exitCode))
					exitCode = -1;

				CloseHandle(hThread);
			}
			else
			{
				exitCode = AsyncRecomputeGraph(&params);
			}

			if (exitCode != 0)
			{
				_RPT1(_CRT_WARN, "Cannot recompute DAG %d\n\n", i);
				g_crashReportLog.Print("Cannot recompute DAG %d\n\n", i);
			}
		}
#else // WIN32
		ShowError("Async mode is windows only");
#endif // WIN32
	}

	m_tgtDagDB.Close();

	cerr << "\nDone!\n";

	return true;
}

/*!
	@brief Recomputes all graphs in the database using the current parameters,
	which include shaperep.

	If shapRep is zero or negative, all graphs maintain their original type.
*/
bool ShapeMatcher::RecomputeGraphs()
{
	if (GetArgCount() != 2)
	{
		ShowUsage("The operation needs two parameters,"
			" the source database name and the target database name.\n"
			"eg,\n\t\tsm -shaperep 0 -recompute source.db target.db");

		return false;
	}

	cerr << "\nRecomputing graphs from " << GetArg(0)
		<< " to " << GetArg(1) << endl;

	return RecomputeAndSaveGraphs(GetArg(1), false);
}

/*!
	Copies the DAGs with id given as extra arguments and also
	the DAG ids in the interval [m_from, m_to], if it is a
	valid interval (ie, m_to >= 0).
*/
bool ShapeMatcher::CopyDAGs()
{
	DAGPtr ptrDag;
	DAGDatabase db1;

	if (GetArgCount() < 2)
	{
		ShowUsage("{-from n} {-to m} source.db target.db {Id0} {Id1} ... {Idn}");
		return false;
	}

	ShowStatus1("\nCopying DAGs to database", GetArg(1));

	if (!db1.Create(GetArg(1), false))
	{
		ShowCreateFileError(GetArg(1));
		return false;
	}

	// Collect all the IDs to remove
	int i, j, n = GetArgCount();
	std::vector<int> ids(n); // extra 2 elements at the begining aren't used

	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;
		}
	}

	bool bCopyDAG;

	// inefficient but simple batch removal of the dags
	for (i = 0; i < m_dagDB.GetDAGCount(); i++)
	{
		bCopyDAG = (i >= m_from && i <= m_to); // copy if in interval

		for (j = 2; j < n && !bCopyDAG; j++)
		{
			if (ids[j] == i)
				bCopyDAG = true;
		}

		if (bCopyDAG)
			db1.AddDAG(m_dagDB.ReadDAG(i));
	}

	cerr << "\nDone!\n";

	return true;
}

/*!
	Removes the DAGs with id given as extra arguments and also
	the DAG ids in the interval [m_from, m_to], if it is a
	valid interval (ie, m_to >= 0).
*/
bool ShapeMatcher::DeleteDAGs()
{
	DAGPtr ptrDag;
	DAGDatabase db1;

	if (GetArgCount() < 2)
	{
		ShowUsage("{-from n} {-to m} source.db target.db {Id0} {Id1} ... {Idn}");
		return false;
	}

	ShowStatus1("\nCreating database", GetArg(1));

	if (!db1.Create(GetArg(1)))
	{
		ShowCreateFileError(GetArg(1));
		return false;
	}

	// Collect all the IDs to remove
	int i, j, n = GetArgCount();
	std::vector<int> ids(n); // extra 2 elements at the begining aren't used
	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 < m_dagDB.GetDAGCount(); i++)
	{
		bDeleteDAG = (i >= m_from && i <= m_to); // delete if in interval

		for (j = 2; j < n && !bDeleteDAG; j++)
		{
			if (ids[j] == i)
				bDeleteDAG = true;
		}

		if (bDeleteDAG)
			ShowStatus1("Deleting DAG ID: ", String("", i));
		else
			db1.AddDAG(m_dagDB.ReadDAG(i));
	}

	cerr << "\nDone!\n";

	return true;
}

/*!
	@brief Split database in random pieces of sizes N% and (100-N)% of
	the total size (N = m_nSplitProportion)

	@see splitByObject.
*/
bool ShapeMatcher::SplitDB()
{
	DAGPtr ptrDag;
	DAGDatabase db1, db2;
	int i;

	if (GetArgCount() != 3)
	{
		ShowErrorMsg("Wrong number of arguments");
		return false;
	}

	if (!db1.Create(GetArg(1)))
	{
		ShowCreateFileError(GetArg(1));
		return false;
	}

	if (!db2.Create(GetArg(2)))
	{
		ShowCreateFileError(GetArg(2));
		return false;
	}

	int nDAGCount = m_dagDB.GetDAGCount();

	char szFileName[MAX_PATH_SIZE];
	ViewSubsampler vs;
	VIEWWEIGHT vw;

	DirWalker::ChangeFileExt(m_dagDB.GetFileName(), NULL, szFileName);
	strcat(szFileName, "_subsampling.inf");

	int nMaxRankPos = (int)floor((m_nSplitByObject ? 128:nDAGCount) * (m_nSplitProportion / 100.0));

	SmartArray<int> viewsToRemove;
	String strLastObjName;

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

	for (i = 0; i < nDAGCount; i++)
	{
		ptrDag = m_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);

#ifdef _DEBUG
		if (viewsToRemove[ptrDag->GetViewNumber() - 1])
			cout << "(" << ptrDag->GetObjName() << ", "
				<< ptrDag->GetViewNumber() << ") is out." << endl;
#endif
	}

	return true;
}

/*!
	@brief Incremental split of database in N subdatabases with
	       decreasing number of views per object (N = m_nSplitProportion)
*/
bool ShapeMatcher::IncrementalRandomDBSplit()
{
	char szFileName[MAX_PATH_SIZE], szPrefix[MAX_PATH_SIZE];
	std::map<std::string, SmartArray<int> > viewsPerObject;
	int i, newNumViews, trialNum;
	StrIntMap objViewNumbers;
	StrIntMap::iterator it;
	std::string objName;
	ViewSubsampler vs;
	DAGDatabase tgtDB;
	double splitProp;
	DAGPtr ptrDag;
	DirWalker dw;

	const int nDAGCount = m_dagDB.GetDAGCount();
	const double splitPropStep = 1.0 / m_nNumSplitParts;

	cerr << "\nSplitting with increasing proportions of size " << splitPropStep << "..." << endl;

	dw.ParseFileName(GetArg(0));

	CountNumberOfViewsPerObject(objViewNumbers);

	for (splitProp = splitPropStep; splitProp <= 0.9; splitProp += splitPropStep)
	{
		for (trialNum = 0; trialNum < m_iterations; trialNum++)
		{
			// Assign random view numbers to each object according to proportions
			for (it = objViewNumbers.begin(); it != objViewNumbers.end(); it++)
			{
				newNumViews = int(it->second * splitProp);
				viewsPerObject[it->first] = vs.RandomSubsampling(it->second, newNumViews);
			}

			// Create a new target database
			sprintf(szPrefix, "%s_rand%d_sub%.0f", dw.name.c_str(), trialNum + 1, splitProp * 100);
			DirWalker::CreateUniqueFileName(".", szPrefix, "db", szFileName);

			cerr << "\nCreating " << szFileName << "..." << flush;

			if (!tgtDB.Create(szFileName))
			{
				ShowCreateFileError(szFileName);
				return false;
			}

			// Copy the selected views to the target database
			for (i = 0; i < nDAGCount; i++)
			{
				ptrDag = m_dagDB.ReadDAG(i);
				objName.assign(ptrDag->GetObjName());

				// Views marked as true must be ignored
				if (!viewsPerObject[objName][ptrDag->GetViewNumber() - 1])
					tgtDB.AddDAG(ptrDag);
			}

			cerr << " done!" << endl;
			tgtDB.Close();
		}
	}

	return true;
}

/*!
	@brief Incremental split of database in subdatabases with decreasing number of objects
*/
bool ShapeMatcher::IncrementalObjectDBSplit()
{
	char szFileName[MAX_PATH_SIZE], szPrefix[MAX_PATH_SIZE];
	int i, j, dbFrom, dbCount, nDAGCount;
	std::string objName;
	StrIntMap objs;
	DAGPtr ptrDag;
	DirWalker dw;

	dbCount = 0;
	nDAGCount = m_dagDB.GetDAGCount();

	cerr << "\nCollecting object names...\n";

	for (i = 0; i < nDAGCount; i++)
	{
		ptrDag = m_dagDB.ReadDAG(i, true);
		objName.assign(ptrDag->GetObjName());

		if (objs.find(objName) == objs.end())
		{
			cerr << "\t" << dbCount + 1 << ". " << objName << endl;
			objs[objName] = dbCount++;
		}
	}

	if (dbCount == 0)
		return false;

	DAGDatabase* dbs = new DAGDatabase[dbCount];

	dw.ParseFileName(GetArg(0));

	cerr << "\nCreating " << dbCount << " incremental object databases...\n";

	for (i = 0; i < dbCount; i++)
	{
		sprintf(szPrefix, "%s_with_%d_objects", dw.name.c_str(), i + 1);
		DirWalker::CreateUniqueFileName(".", szPrefix, "db", szFileName);

		cout << "\tcreating " << szFileName << "..." << endl;

		if (!dbs[i].Create(szFileName))
		{
			ShowCreateFileError(szFileName);
			return false;
		}
	}

	cerr << "\nWriting objects...\n";

	for (i = 0; i < nDAGCount; i++)
	{
		ptrDag = m_dagDB.ReadDAG(i, false);
		objName.assign(ptrDag->GetObjName());

		dbFrom = objs[objName];

		for (j = dbFrom; j < dbCount; j++)
		{
			//cout << "Writing " << objName << " to DB " << j << endl;
			dbs[j].AddDAG(ptrDag);
		}
	}

	delete[] dbs;

	cerr << "\nDone!\n";
	return true;
}

/*!
	@brief Matches the entire query database against the model database.

	If m_matchParams.nExtreme == 1, both database are loaded to memory before
	matching. This speeds up the computation but requires enough RAM to hold
	all query and model DAGs.

	@remarks Both databases may be the same.
*/
bool ShapeMatcher::MatchDB(const char* queryDB, const char* modelDB)
{
	bool bLoadToMemory = (m_matchParams.nExtreme == 1);

	cout << "\nMatching " << queryDB << " against " << modelDB << "..." << endl;

	if (!m_dagDB.Open(modelDB))
	{
		ShowOpenFileError(modelDB);
		return false;
	}

	if (!bLoadToMemory)
	{
		m_target = queryDB;

		Match();
	}
	else if (strcmp(queryDB, modelDB) == 0)
	{
		const int N = m_dagDB.GetDAGCount();

		vector<DAGPtr> memoryDB;

		memoryDB.reserve(N);

		cout << "Loading " << N << " DAGs to memory..." << endl;

		for (int id = 0; id < N; id++)
		{
			memoryDB.push_back(m_dagDB.ReadDAG(id, true));
		}

		cout << "\nDone!" << endl;
	}
	else
	{
		cout << "\nMatching " << queryDB << " against " << modelDB << "..." << endl;

		if (!m_dagDB.Open(modelDB))
		{
			ShowOpenFileError(modelDB);
			return false;
		}
	}

	m_dagDB.Close();

	return true;
}

/*!
	@brief Matches each given database against itself
*/
bool ShapeMatcher::MultiDBMatch()
{
	char szFileName[MAX_PATH_SIZE];
	char fileExt[10];
	DirWalker dirDw;
	bool bSelfDBMatch = (strlen(m_target) == 0);

	for (int i = 0; i < GetArgCount(); i++)
	{
		cout << i << ". processing " << GetArg(i) << "..." << endl;

		if (DirWalker::IsDirectory(GetArg(i)) || DirWalker::HasWildcards(GetArg(i)))
		{
			if (!dirDw.OpenDir(GetArg(i)))
			{
				ShowOpenFileError(GetArg(i));
				return false;
			}

			while (dirDw.GetNextFile(szFileName, MAX_PATH_SIZE, fileExt))
			{
				if (!MatchDB((bSelfDBMatch) ? szFileName : m_target, szFileName))
					return false;
			}
		}
		else
		{
			if (!MatchDB((bSelfDBMatch) ? GetArg(i) : m_target, GetArg(i)))
				return false;
		}
	}

	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)))
		{
			ShowOpenFileError(GetArg(i));
			return false;
		}

		for (j = 0; j < source.GetDAGCount(); j++)
			m_dagDB.AddDAG(source.ReadDAG(j));

		source.Close();
	}

	cerr << "\nDone!\n";

	return true;
}

bool ShapeMatcher::ViewDAG()
{
	DAGDatabase srcDB;
	int nDagId = -1;

	// Get the source database that must be used, may or not be srcDB
	DAGDatabase* pSrcDB = OpenSourceDatabase(&srcDB);

	if (!pSrcDB)
		return false;

	// Show last object added if '.' is given
	if (m_viewDag[0] == '.' && m_viewDag[1] == '\0')
	{
		nDagId = pSrcDB->GetDAGCount() - 1;
	}
	// If we are given an object name, search for its ID
	else if (*m_viewDag != '-' && !isdigit(*m_viewDag))
	{
		DirWalker dw;

		dw.ParseFileName(m_viewDag);
		dw.ParseFileNameParams();

		if (dw.params.size() >= 1)
		{
			DirWalker::iterator it = dw.params.begin();
			DAGPtr ptrView;

			std::cout << "Searching for '" << it->first << "' with view number "
				<< it->second << "..." << std::flush;

			for (int id = 0; id < pSrcDB->GetDAGCount(); id++)
			{
				ptrView = pSrcDB->ReadDAG(id, true);

				if (it->second == ptrView->GetViewNumber() &&
					!it->first.compare(ptrView->GetObjName()))
				{
					std::cout << " found with ID = " << id << std::endl;
					nDagId = id;
					break;
				}
			}
		}

		if (nDagId == -1)
		{
			ShowError("Invalid object name or id");
			return false;
		}
	}
	else
	{
		nDagId = atoi(m_viewDag);

		// If the id is negative, it is an offset from the last DAG
		if (nDagId < 0)
			nDagId = pSrcDB->GetDAGCount() + nDagId;
	}

	return ShowDAG(*pSrcDB, nDagId);
}

/*!
	Opens the source database and returns a pointer to it. If
	m_sourceDB equals zero, a pointer to the default database
	m_dagDB is returned.

	@return pointer to m_dagDB or the given pointer pSrcDB if
	a database is opened. NULL if there is an error.
*/
DAGDatabase* ShapeMatcher::OpenSourceDatabase(DAGDatabase* pSrcDB)
{
	// Determine the source database
	if (m_sourceDB < 0 || m_sourceDB >= GetArgCount())
	{
		ShowErrorMsg("Invalid source database id");
		return NULL;
	}

	// If the source database is the zeroth argument, then is already opened
	if (m_sourceDB == 0)
	{
		return &m_dagDB;
	}
	else if (!pSrcDB->Open(GetArg(m_sourceDB)))
	{
		ShowOpenFileError(GetArg(m_sourceDB));
		return NULL;
	}

	return pSrcDB;
}

/*!
	Shows DAG with ID nDagId in the given target DAG database.
*/
bool ShapeMatcher::ShowDAG(DAGDatabase& dagBD, int nDagId) const
{
	if (nDagId < 0 || nDagId >= dagBD.GetDAGCount())
	{
		ShowErrorMsg("Invalid DAG ID");
		return false;
	}

	DAGPtr ptrDag = dagBD.ReadDAG(nDagId);
	bool bRetVal = false;

	//ptrDag->Print();

	if (ptrDag->ClassName() == "ShockGraph")
		bRetVal = ShowShockGraph(ptrDag, m_xpos, m_ypos);
	else if (ptrDag->ClassName() == "BoneGraph")
		bRetVal = ShowBoneGraph(ptrDag, m_xpos, m_ypos);
	else if (ptrDag->ClassName() == "GestureGraph")
	{
		ptrDag->Print();
		cout << endl;
		//ShowGraph(*ptrDag);
	}

	return bRetVal;
}

bool ShapeMatcher::ViewDAGs()
{
	DAGDatabase srcDB;

	// Get the source database, which may not be srcDB
	DAGDatabase* pSrcDB = OpenSourceDatabase(&srcDB);

	if (!pSrcDB)
		return false;

	int n = pSrcDB->GetDAGCount() - 1;
	bool bSuccess = true; // Gets input from the user to cancel display

	if (GetArgCount() <= 1)
	{
		// If there is to -to argument, show all the graphs
		if (m_to > 0 && m_to < n)
			n = m_to;

		for (int i = m_from; i <= n && bSuccess; i++)
			bSuccess = ShowDAG(*pSrcDB, i);
	}
	else
	{
		int i;

		// Show individual IDs given as extra arguments
		for (int argId = 1; argId < GetArgCount() && bSuccess; argId++)
		{
			i = atoi(GetArg(argId));

			ShowStatus2("Reading DAG", i, "...");

			if (i == 0 && strcmp(GetArg(argId), "0"))
				bSuccess = false;
			else
				bSuccess = ShowDAG(*pSrcDB, i);
		}
	}

	return bSuccess;
}

bool ShapeMatcher::ViewDAGAndNeighbors()
{
	int nDagId = atoi(m_viewDag);

	if (nDagId < 0 || nDagId >= m_dagDB.GetDAGCount())
	{
		ShowErrorMsg("Invalid DAG ID");
		return false;
	}

	// Show the 9 neighbours...

	// Get the DAG ID in order to read it and find its view number
	DAGPtr ptrDag = m_dagDB.ReadDAG(nDagId, true);
	int nView = ptrDag->GetViewNumber();

	// Use the view number to find its closest 9 neighbours' view numbers
	SmartArray<int> views(0, 9);
	GetClosestViews2(nView, 3, views, true);

	// Find the DAG with the given object name and a valid neighbouring view number
	DAGPtr ptrView;
	int id, j;
	char szDagID[100];
	SmartArray<int> procHandles;
	int h;

	for (id = 0; id < m_dagDB.GetDAGCount(); id++)
	{
		ptrView = m_dagDB.ReadDAG(id, true);

		if (!strcasecmp(ptrDag->GetObjName(), ptrView->GetObjName()))
		{
			for (j = 0; j < views.Size(); j++)
			{
				if (views[j] == ptrView->GetViewNumber())
				{
					std::cout << "Opening neighbour: " << ptrView->GetObjName()
						<< " with view number " << ptrView->GetViewNumber() << std::endl;

#ifdef WIN32
					sprintf(szDagID, "%d", id);
					h = _spawnl(_P_NOWAIT, GetPrgName(), GetPrgName(), "-v", szDagID,
						"-xpos=-1", "-ypos=-1", GetArg(0), NULL);
#else
					ShowError("Show neighbours is windows only.");
#endif

					if (h != -1)
						procHandles.AddTail(h);
					else
						ShowError("Cannot execute child process");

					break;
				}
			}
		}
	}

	// Show the reqested DAG last
#ifdef WIN32
	std::cout << "Opening target: " << ptrDag->GetObjName()
		<< " with view number " << nView << std::endl;
	_spawnl(_P_WAIT, GetPrgName(), GetPrgName(), "-v", m_viewDag, GetArg(0), NULL);
#else
	ShowError("This is a windows only function now. Needs fixing in Linux!");
#endif

	for (int i = 0; i < procHandles.Size(); i++)
	{
#ifdef WIN32
		TerminateProcess((HANDLE)procHandles[i], 0);
#else
		//Kill(procHandles[i])
		//@TODO fixing
#endif
	}

	return true;
}

/*!
	@brief Creates the database index if it doen's exist
*/
bool ShapeMatcher::CreateDBIndex()
{
	m_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)
	{
		ShowUsage("sm -nodeMatch model.db #node1 graph1ID #node2 graph2ID");
		return false;
	}

	NODE_LABEL idxN1 = GetArg(1);
	int idxG1 = atoi(GetArg(2));

	NODE_LABEL idxN2 = GetArg(3);
	int idxG2 = atoi(GetArg(4));

	DAGPtr ptrDag1 = m_dagDB.ReadDAG(idxG1, true);
	DAGPtr ptrDag2 = m_dagDB.ReadDAG(idxG2, true);

	leda_node n1 = ptrDag1->GetNode(idxN1);
	leda_node n2 = ptrDag2->GetNode(idxN2);

	if (n1 == nil || n2 == nil)
	{
		ShowError("Invalid node label(s)");
		return false;
	}

	double sim = ptrDag1->NodeSimilarity(n1, ptrDag2, n2);

	cout << "\nNode similarity is: " << sim << endl;

	return true;
}

bool ShapeMatcher::CreateDBParsings()
{
	char szFilename[MAX_PATH_SIZE];
	//DirWalker dw;

	//dw.ParseFileName(GetArg(0));

	if (GetArgCount() < 2)
	{
		ShowUsage("source_db_name target_db_prefix");
		return false;
	}

	m_shapeInfo.shapeRepType = 3;

	const int firstParam = (m_firstDBId >= 0) ? m_firstDBId : 0;
	const int lastParam  = (m_lastDBId >= 0) ? m_lastDBId : 10;

	const double maxAngle = 151.2608;

	for (int i = firstParam; i <= lastParam; i++)
	{
		// Set the parameter according to the current index
		switch (i)
		{
		case 0:
			m_shapeInfo.bgparams.maxCumulativeBAR.Set(0.75);
			m_shapeInfo.bgparams.maxRadiusRatio.Set(0.9);
			m_shapeInfo.bgparams.maxCornerAngle.Set(maxAngle);
			break;
		case 1:
			m_shapeInfo.bgparams.maxCumulativeBAR.Set(0.75);
			m_shapeInfo.bgparams.maxRadiusRatio.Set(0.8);
			m_shapeInfo.bgparams.maxCornerAngle.Set(maxAngle);
			break;
		case 2:
			m_shapeInfo.bgparams.maxCumulativeBAR.Set(0.75);
			m_shapeInfo.bgparams.maxRadiusRatio.Set(0.7);
			m_shapeInfo.bgparams.maxCornerAngle.Set(maxAngle);
			break;
		case 3:
			m_shapeInfo.bgparams.maxCumulativeBAR.Set(0.75);
			m_shapeInfo.bgparams.maxRadiusRatio.Set(0.6);
			m_shapeInfo.bgparams.maxCornerAngle.Set(maxAngle);
			break;
		case 4:
			m_shapeInfo.bgparams.maxCumulativeBAR.Set(0.75);
			m_shapeInfo.bgparams.maxRadiusRatio.Set(0.5);
			m_shapeInfo.bgparams.maxCornerAngle.Set(maxAngle);
			break;
		case 5:
			m_shapeInfo.bgparams.maxCumulativeBAR.Set(0.75);
			m_shapeInfo.bgparams.maxRadiusRatio.Set(0.4);
			m_shapeInfo.bgparams.maxCornerAngle.Set(maxAngle);
			break;
		case 6:
			m_shapeInfo.bgparams.maxCumulativeBAR.Set(0.75);
			m_shapeInfo.bgparams.maxRadiusRatio.Set(0.3);
			m_shapeInfo.bgparams.maxCornerAngle.Set(maxAngle);
			break;
		default:
			ASSERT(false);
		}

		// Create the file name for the parse
		//sprintf(szFilename, "%s%s_parse%d.db", dw.path.c_str(), GetArg(1), i);
		sprintf(szFilename, "%s_parse%d.db", GetArg(1), i);

		ShowStatus1("Creating parsing database", szFilename);

		if (!RecomputeAndSaveGraphs(szFilename, true))
			return false;
	}

	return true;
}

bool ShapeMatcher::MatchDBParsings()
{
	int firstQueryDBId, lastQueryDBId;
	int firstModelDBId, lastModelDBId;
	int queryDBCount, modelDBCount;

	ShowStatus("Loading all DAGs to memory...");

	String strQueryPrefix(GetArg(0), "_parse");
	String strModelPrefix(GetArg(1), "_parse");

	DirWalker dw;

	queryDBCount = dw.CountNumberedFiles(".", strQueryPrefix, "db",
		&firstQueryDBId, &lastQueryDBId);

	modelDBCount = dw.CountNumberedFiles(".", strModelPrefix, "db",
		&firstModelDBId, &lastModelDBId);

	if (queryDBCount <= 0 || modelDBCount <= 0)
	{
		ShowError("There are no parsing files");
		return false;
	}

	if (m_firstDBId >= 0)
	{
		firstQueryDBId = m_firstDBId;
		firstModelDBId = m_firstDBId;
	}

	if (m_lastDBId >= 0)
	{
		lastQueryDBId = m_lastDBId;
		lastModelDBId = m_lastDBId;
	}

	DAGMemoryDBSet queryDBSet;

	if (!queryDBSet.LoadDAGs(strQueryPrefix, firstQueryDBId, lastQueryDBId))
		return false;

	char fileNameSubfix[256];

	for (int expPar = m_experimentFirstParam; expPar <= m_experimentLastParam; expPar++)
	{
		if (!queryDBSet.SetDBDagPresence(m_experimentId, m_experimentStep, expPar))
			return false;

		sprintf(fileNameSubfix, "_%s_%d_%d_%d", m_szExperimentName,
			m_experimentId, m_experimentStep, expPar);

		DAGMemoryDBSet* pModelDBSet;
		bool bDeleteModelDBSet;

		if (strQueryPrefix != strModelPrefix)
		{
			pModelDBSet = new DAGMemoryDBSet;
			bDeleteModelDBSet = true;

			if (!pModelDBSet->LoadDAGs(strModelPrefix, firstModelDBId, lastModelDBId))
				return false;
		}
		else
		{
			pModelDBSet = &queryDBSet;
			bDeleteModelDBSet = false;
		}

		DAGMemoryDBSet& modelDBSet = *pModelDBSet;

		ShowStatus("...done");

		ShowStatus("Allocating similarity matrices...");

		DAGSimilarityMemory simMemory;

		if (m_matchInfo.selectionMethod <= 1)
		{
			if (queryDBSet.ParseCount() != modelDBSet.ParseCount())
			{
				ShowError("The number of query and model databases must be equal");
				return false;
			}

			simMemory.Init(1, modelDBSet.ParseCount(),
				queryDBSet.DAGCount(), modelDBSet.DAGCount());
		}
		else
		{
			simMemory.Init(queryDBSet.ParseCount(), modelDBSet.ParseCount(),
				queryDBSet.DAGCount(), modelDBSet.DAGCount());
		}

		ShowStatus("...done");

		STATINFO idxSt, matSt;
		MatchingResults matchingResults(m_matchInfo);
		const int lastPos = GetLastDAGId(queryDBSet.DAGCount());
		DAGPtr ptrDag;

		if (queryDBSet.ParseCount() == 1 && modelDBSet.ParseCount() == 1)
		{
			matchingResults.Init(queryDBSet.GetDatabase(0),
				modelDBSet.GetDatabase(0), fileNameSubfix);
		}
		else
		{
			matchingResults.Init(GetArg(0), GetArg(1), fileNameSubfix);
		}

		if (matchingResults.HasStatsFile())
			Print(matchingResults.GetStatsFile() << "\n%{\n") << "\n%}\n";

		for (int i = m_from; i <= lastPos; i++)
		{
			ShowStatus2("Matching query DAG", i, queryDBSet.GetDAGLabel(i));

			ShowStatus2("Number of model DAGs removed",
				queryDBSet.CountNotPresent(i), "");

			ptrDag = MatchDAGs(i, queryDBSet, modelDBSet, m_matchInfo,
				simMemory, idxSt, matSt);

			matchingResults.Add(ptrDag, idxSt, matSt, i);
		}

		matchingResults.Close();

		if (bDeleteModelDBSet)
			delete pModelDBSet;
	}

	return true;
}

bool ShapeMatcher::Match()
{
	MatchingResults matchingResults(m_matchInfo);
	ViewSubsampler vs; // collects stats for subsampling (if requested)
	STATINFO idxSt, matSt;
	DAGPtr ptrDag;
	bool bSuccess;

	m_dagDB.OpenIndex();

	if (DirWalker::CompareFileExtension(m_target, "db"))
	{
		DAGDatabase imgDB;
		double dOcclusionRate = 0;

		if (!imgDB.Open(m_target))
		{
			ShowOpenFileError(m_target);
			return false;
		}

		matchingResults.Init(&imgDB, &m_dagDB);

		const int lastPos = GetLastDAGId(&imgDB);

		for (int i = m_from; i <= lastPos; i++)
		{
			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 view " << ptrDag->GetViewNumber()
				<< " of " << ptrDag->GetObjName();

			if (dOcclusionRate > 0)
				cout << " with occlusion/noise = " << dOcclusionRate;

			cout << endl;

			bSuccess = MatchDAG(ptrDag, m_dagDB, m_matchInfo, idxSt, matSt);

			if (bSuccess)
				matchingResults.Add(ptrDag, idxSt, matSt, i);

			if (bSuccess && m_saveSubsamplingInfo)
				AddSubsamplingInfo(vs, ptrDag, matSt, m_matchInfo.idxKBest);
		}

		matchingResults.Close();

		if (m_saveSubsamplingInfo)
			SaveSubsamplingInfo(vs, m_dagDB.GetFileName());
	}
	else if (strlen(m_target) == 0 || !strcmp(m_target, "."))
	{
		// There are no params to -m or param is '.'. Process individual
		// ppm/image files if any, or else, choose a random graph
		if (GetArgCount() > 1)
		{
			return MatchImageFile();
		}
		else // no extra params to process, choose a random query
		{
			DAGPtr pTargetDag;

			ShowMsg("No queries were selected. Choosing 'random'"
				" query from model database...");

			m_matchInfo.computeStats = 0; // ensure that no stats are computed

			matchingResults.Init(&m_dagDB, &m_dagDB);

			// Choose random target DAG
			for(int j = 0; j < m_iterations; j++)
			{
				pTargetDag = m_dagDB.ChooseDAG();

				bSuccess = MatchDAG(pTargetDag, m_dagDB, m_matchInfo, idxSt, matSt);

				if (bSuccess)
					matchingResults.Add(pTargetDag, idxSt, matSt);
			}
		}
	}
	else // we are given a single DAG id to match
	{
		int nDagId = atoi(m_target);

		if (nDagId >= m_dagDB.GetDAGCount())
		{
			ShowErrorMsg("Invalid DAG ID");
			return false;
		}

		m_matchInfo.computeStats = 0; // ensure that no stats are computed

		matchingResults.Init(&m_dagDB, &m_dagDB);

		ptrDag = m_dagDB.ReadDAG(nDagId);

		// See if we are given IDs of model DAG IDs too
		if (GetArgCount() > 1)
		{
			int modelDagID;

			for (int i = 1; i < GetArgCount(); i++)
			{
				modelDagID = atoi(GetArg(i));

				MatchDAG(ptrDag, modelDagID /*first ID*/, modelDagID /*last ID*/,
					m_dagDB, m_matchInfo, matSt);

				matchingResults.Add(ptrDag, idxSt, matSt, nDagId);
			}
		}
		else // matche the query DAG agains the whole database
		{
			bSuccess = MatchDAG(ptrDag, m_dagDB, m_matchInfo, idxSt, matSt);

			if (bSuccess)
				matchingResults.Add(ptrDag, idxSt, matSt, nDagId);
		}
	}

	cerr << "\nDone!\n";

	return true;
}

bool ShapeMatcher::MatchImageFile()
{
	// We'll create a query database by reusing the existing code...
	// Close the current (model) database
	m_dagDB.Close();

	// Create a query database
	char szQueryDBName[MAX_PATH_SIZE];
	DirWalker::CreateUniqueFileName(".", "query", "db", szQueryDBName);

	if (!m_dagDB.Create(szQueryDBName))
	{
		ShowCreateFileError(szQueryDBName);
		return false;
	}

	// Add objects to the 'new' current database in the usual fashion
	AddObjsToDB();

	// Close query database and reopen the model database
	m_dagDB.Close();

	if (!m_dagDB.Open(GetArg(0)))
	{
		ShowOpenFileError(GetArg(0));
		return false;
	}

	// We have a query database now, so call Math() again
	m_target = szQueryDBName;

	Match();

	remove(szQueryDBName); // delete the database file

	return true;
}

bool ShapeMatcher::CompareDAGs()
{
	int queryDagId = atoi(m_target);

	if (queryDagId >= m_dagDB.GetDAGCount())
	{
		ShowErrorMsg("Invalid query DAG ID");
		return false;
	}

	const int lastId = GetLastDAGId();
	STATINFO matSt;

	if (lastId < 0)
		return false;

	DAGPtr ptrDag = m_dagDB.ReadDAG(queryDagId, true);

	cout << "\nComparing view " << ptrDag->GetViewNumber()
		<< " of " << ptrDag->GetObjName() << "..." << endl;

	// Compare the query against the models in [m_from, lastId]

	MatchDAG(ptrDag, m_from, lastId, m_dagDB, m_matchInfo, matSt);

	if (m_matchInfo.showResults)
		ShowResults(ptrDag, matSt, m_dagDB, m_dagDB, queryDagId);

	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 = m_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;

	/*std::string str = DirWalker::AppendToFileName(GetArg(1), "_diego", "m");

	cout << str << endl;

	return true;*/

	// Read all dags and...
	DAGPtr ptrDag;
	leda_node v;
	int i, j, nShockCount;
	double y, y0, y00, y1, y11;

	for (i = 0; i < m_dagDB.GetDAGCount(); i++)
	{
		ptrDag = m_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;

				LineSegmentArray 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: " << m_dagDB.GetDAGCount() << endl;

	return true;
}

int ShapeMatcher::CountNumberOfViewsPerObject(StrIntMap& objs)
{
	const int nDAGCount = m_dagDB.GetDAGCount();
	std::string objName;
	DAGPtr ptrDag;

	cerr << "\nCounting number of views per object..." << endl;

	for (int i = 0; i < nDAGCount; i++)
	{
		ptrDag = m_dagDB.ReadDAG(i, true);
		objName.assign(ptrDag->GetObjName());

		if (objs.find(objName) == objs.end())
			objs[objName] = 1;
		else
			objs[objName]++;
	}

	// Check that all objects have the same number of views
	StrIntMap::iterator it = objs.begin();
	int numViews = -1;

	for (; it != objs.end(); it++)
	{
		if (numViews == -1)
		{
			numViews = it->second;
		}
		else if (numViews != it->second)
		{
			cerr << "Warning: object " << it->first << " has " << it->second
				<< " views, while previous objects have " << numViews
				<< " views" << endl;
		}
	}

	cerr << "Number of views per object is " << numViews << endl;

	return numViews;
}

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 = m_dagDB.GetDAGCount();
	Matrix m(n, n);

	m = 0.0; // Init matrix

	if (m_to > 0 && m_to < n)
		n = m_to;

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

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

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

	cout << endl;

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

	os << "n = " << n << ";\n";

	os << "id = [ ";
	for (i = m_from; i < n; i++)
		os << i << ' ';
	os << "];\n";

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

	return true;
}

bool ShapeMatcher::SaveInXMLFormat()
{
	fstream outputFile;
	std::string fileName;
	DAGPtr ptrDag;
	const int numChars = strlen(m_target);

	if (numChars == 0 || (numChars == 1 && m_target[0] == '.'))
	{
		fileName = DirWalker::GetName(m_dagDB.GetFileName());
		fileName += ".xml";

		DirWalker::MakeNameUnique(&fileName);
	}
	else
	{
		fileName = m_target;
	}

	outputFile.open(fileName.c_str(), ios::out | ios::trunc);

	const int lastPos = GetLastDAGId();

	cerr << endl << "Saving DAGs " << m_from << "--"
		<< lastPos << " to XML file '" << fileName << "' ..." << endl;

	XML_OPEN_ATTS(outputFile, "database");
	XML_PRINT_ATT2(outputFile, "totalSize", m_dagDB.GetDAGCount());
	XML_PRINT_ATT2(outputFile, "firstRequestedID",  m_from);
	XML_PRINT_ATT2(outputFile, "lastRequestedID",  lastPos);
	XML_END_ATTS(outputFile);

	for (int i = m_from; i <= lastPos; i++)
	{
		ptrDag = m_dagDB.ReadDAG(i);

		ptrDag->Print(outputFile, "XML");
	}

	XML_CLOSE(outputFile, "database");

	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];

	const int lastPos = GetLastDAGId();

	for (int i = m_from; i <= lastPos; i++)
	{
		ptrDag = m_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::CreateUniqueFileName(".", "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;
}

bool ShapeMatcher::FindShapePrototypes()
{
	const int BUF_SIZE = 256;
	char szFilename[BUF_SIZE];
	char buf[BUF_SIZE];
	double tau;
	int k;
	bool onlyNeighborLinks, ok;

	cout << "Name of the similarity matrix file [matrix.m]: " << flush;
	cin.getline(szFilename, BUF_SIZE);

	if (szFilename[0] == '\0')
		strcpy(szFilename, "matrix.m");

	do {
		ok = true;
		cout << "Only allow links between neighboring views (yes/no) [no]: " << flush;
		cin.getline(buf, BUF_SIZE);

		if (!stricmp(buf, "yes"))
			onlyNeighborLinks = true;
		else if (!stricmp(buf, "no") || buf[0] == '\0')
			onlyNeighborLinks = false;
		else
			ok = false;
	} while(!ok);

	cout << "Small components threshold ( tau > 0) [120]: " << flush;
	cin.getline(buf, BUF_SIZE);
	tau = atof(buf);
	if (tau <= 0) tau = 120;

	cout << "Maximum number of nearest neighbors per node [4]: " << flush;
	cin.getline(buf, BUF_SIZE);
	k = atoi(buf);
	if (k <= 0)	k = 4;

	// Show parameters
	const char* szAnsw = (onlyNeighborLinks) ? "yes":"no";
	cout << "\nStarting clustering with parameters {" << szFilename << ", " <<
		szAnsw << ", " << tau << ", " << k << "} ..." << endl;

	// Start clustering
	/*ViewClustering vc;

	vc.ClusterFromSimilarityMatrix(szFilename, k, tau);*/

	return true;
}

bool ShapeMatcher::AddObjsUsingTraining()
{
	/*std::vector<TrainingParamList> paramSet(m_dagDB.GetDAGCount());

	// read DAG
	DAGPtr ptrDag = m_dagDB.ReadDAG(nDagId, true);
	int nView = ptrDag->GetViewNumber();

	// get neigbours list
	SmartArray<int> views(0, 9);
	GetClosestViews2(nView, 3, views, true);*/

	// for all query params

	// for all model params

	// for all neigbours

	// if sim is better, save it

	// ....

	// for all dags in set

	// for all elems in list

	// collect agreements and best averarge

	return true;
}

