/**------------------------------------------------------------------------
 * @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: Matching.cpp,v $
 *
 *-----------------------------------------------------------------------*/
#include <map>

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

#include <ShockGraphView.h>
#include <BoneGraphView.h>
#include "ShapeMatcher.h"
#include "Matching.h"
#include "viewpoints.h"
#include "ImgPreprocess.h"

#ifndef WIN32
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <sys/wait.h>
#endif //no WIN32

using namespace std;

//#define TIMEOUT (60 * 3)

fstream g_logFile;	//!< Global log stream

#ifndef WIN32
pid_t	g_pid;		//!< Global child process ID
int g_nTimeout;   //!< Global timeout, used only to log the timeout val.
#endif

// Function definitions

/*!
	@brief Occludes part of the given graph

	@param dMaxOcclusionRate Maximum percentage of occlusion allowed

	@todo The DeleteSubDAG() function does not work for DAGs with loops.
*/
double OccludeDAG(DAGPtr& ptrDag, double dMaxOcclusionRate)
{
	leda_node v;
	double dOcclusionRate = 0;
	double nNodeMass, nTotalMass = ptrDag->GetNodeCount(); //must be double for division
	int i = 0;

	if (nTotalMass <= 3)
		return 0;

	do {
		v = ptrDag->choose_node();
		ASSERT(v != nil);

		// ptrDag->GetNodeMass(v) is really cummulative mass now
		nNodeMass = ptrDag->GetTransClosMat().RowSum(ptrDag->GetNodeDFSIndex(v));
		dOcclusionRate = nNodeMass / nTotalMass;

	} while (dOcclusionRate > dMaxOcclusionRate && i++ < nTotalMass * 2);

	if (dOcclusionRate > dMaxOcclusionRate)
		return 0;

	ptrDag->DeleteSubDAG(v);
	ptrDag->ComputeDerivedValues();

	return dOcclusionRate;
}

#ifndef WIN32
int AsyncIndexDAG(const DAG* pQueryDag, DAGDatabase& dagDB, ShapeMatchingParams& info, STATINFO& idxSt)
{
	int* pnCount = new int(0);

	if ((g_pid = fork()) != 0)
	{
		int status;
		waitpid(g_pid, &status, 0);

		if (!(WIFEXITED(status)))
		{
			cerr << "\nSegmentation fault when indexing " << pQueryDag->GetDAGLbl() << endl;
			*pnCount = 0;
		}

		int count = *pnCount;
		delete pnCount;
		//DBG_MSG2("At parent", count)
		// Parent process is done, function returns normaly
		return count;
	}

	// Child process starts here
	*pnCount = dagDB.GetSimilar(*pQueryDag, idxSt.matchList, info.idxrange,
		info.modelSimWeight, info.idxtau);
	//DBG_MSG2("At child", *pnCount)
	_exit(0);
}

double AsyncMatchDAG(const DAG* pQueryDag, const DAG* pModelDag)
{
	double* pdSim = new double(0);

	if ((g_pid = fork()) != 0)
	{
		int status;
		waitpid(g_pid, &status, 0);

		if (!(WIFEXITED(status)))
		{
			cerr << "\nSegmentation fault when matching " << pQueryDag->GetDAGLbl()
				<< " against " << pModelDag->GetDAGLbl() << endl;

			*pdSim = 0;
		}

		double sim = *pdSim;
		delete pdSim;

		// Parent process is done, function returns normaly
		return sim;
	}

	// Child process starts here
	*pdSim = pQueryDag->Similarity(*pModelDag);
	_exit(0);
}
#endif //no WIN32

/*!
	@brief Match a list of DAGs against another list of DAGs

	@return the query DAG parsing that is most similar to any
	of the model DAGs and their parsings
*/
DAGPtr MatchDAGs(unsigned int queryDagId,
				 const DAGMemoryDBSet& queryMDBset,
				 const DAGMemoryDBSet& modelMDBset,
				 const ShapeMatchingParams& matchInfo,
				 DAGSimilarityMemory& gsm,
				 STATINFO& idxSt, STATINFO& matSt)
{
	double sim, maxSim;
	unsigned int bestQueryDBId, bestModelDBId;

	const DAGParses& queryDAGs = queryMDBset.GetParses(queryDagId);

	matSt.matchList.resize(modelMDBset.DAGCount());

	const unsigned int firstModelId = (matchInfo.firstModelId < 0) ?
		0 : matchInfo.firstModelId;

	const unsigned int endModelId  = (matchInfo.lastModelId < 0) ?
		modelMDBset.DAGCount() : matchInfo.lastModelId + 1;

	const clock_t startTime = clock();

	for (unsigned int modelDagId = firstModelId; modelDagId < endModelId; modelDagId++)
	{
		if (!queryMDBset.IsDAGPresent(queryDagId, modelDagId))
			continue;

		const DAGParses& modelDAGs = modelMDBset.GetParses(modelDagId);

		maxSim = -1;

		if (matchInfo.selectionMethod == 0)
		{
			ASSERT(queryDAGs.size() == modelDAGs.size());

			for (unsigned int i = 0; i < queryDAGs.size(); i++)
			{
				//sim = gsm(0, i, queryDagId, modelDagId);

				//if (sim < 0)
				//{
					sim = queryDAGs[i]->Similarity(*modelDAGs[i]);

					ASSERT_UNIT_INTERVAL(sim);

					gsm(0, i, queryDagId, modelDagId) = sim;
				//	gsm(0, i, modelDagId, queryDagId) = sim;
				//}

				if (sim > maxSim)
				{
					maxSim = sim;
					bestQueryDBId = i;
					bestModelDBId = i;
				}
			}
		}
		else if (matchInfo.selectionMethod == 1)
		{
			ASSERT(queryDAGs.size() == modelDAGs.size());
			double simSum = 0;

			for (unsigned int i = 0; i < queryDAGs.size(); i++)
			{
				sim = gsm(0, i, queryDagId, modelDagId);

				if (sim < 0)
				{
					sim = queryDAGs[i]->Similarity(*modelDAGs[i]);

					gsm(0, i, queryDagId, modelDagId) = sim;
					gsm(0, i, modelDagId, queryDagId) = sim;
				}

				simSum += sim;

				if (sim > maxSim)
				{
					maxSim = sim;
					bestQueryDBId = i;
					bestModelDBId = i;
				}
			}

			maxSim = simSum / queryDAGs.size();
		}
		else if (matchInfo.selectionMethod == 2)
		{
			unsigned int q, m;

			for (q = 0; q < queryDAGs.size(); q++)
			{
				for (m = 0; m < modelDAGs.size(); m++)
				{
					sim = queryDAGs[q]->Similarity(*modelDAGs[m]);

					if (sim > maxSim)
					{
						maxSim = sim;
						bestQueryDBId = q;
						bestModelDBId = m;
					}
				}
			}
		}
		else
		{
			ASSERT(false);
		}

		if (maxSim >= matchInfo.matchtau)
		{
			matSt.matchList[modelDagId].Set(modelDAGs[bestModelDBId],
				maxSim, bestQueryDBId, bestModelDBId);
		}

		//idxSt.matchList[i].ptrDAG = ptrDag;
	}

	const double elapsedTime = (double)(clock() - startTime) / CLOCKS_PER_SEC;

	DAGPtr ptrBestQueryDAGParse;

	// Sort best matches
	if (matSt.matchList.size() > 1)
	{
		matSt.matchList.sort();

		ptrBestQueryDAGParse = queryDAGs[matSt.matchList[0].nQueryDBId];
	}
	else
	{
		ptrBestQueryDAGParse = queryDAGs[0];
	}

	if (matchInfo.computeStats)
	{
		//idxSt.ComputeStats(ptrBestQueryDAGParse, (STATINFO::DATA_FORMAT) matchInfo.computeStats);
		matSt.ComputeStats(ptrBestQueryDAGParse, (STATINFO::DATA_FORMAT) matchInfo.computeStats);
		matSt.SetCompTime(elapsedTime);
	}

	return ptrBestQueryDAGParse;
}

/*!
	@brief Match one DAG against ALL the DAGs in the given db

	@return true iff there were no errors
*/
bool MatchDAG(const DAG* pTargetDag, DAGDatabase& dagDB,
			  ShapeMatchingParams& info, STATINFO& idxSt, STATINFO& matSt)
{
	double dSim;
	DAGPtr ptrDag;
	int i, count;

	// If idxTau is zero, then we don't use indexing at all
	if (info.idxtau == 0)
	{
		ShowMsg("Shape indexing is turned off. Using linear search (slow)");

		count = dagDB.GetDAGCount();
		idxSt.matchList.resize(count);

		for (int i = 0; i < count; i++)
			idxSt.matchList[i].Set(i /*ID*/, 0 /*offset*/, 1 /*similarity*/);
	}
	else
	{
		/*if (info.asyncCompu)
			count = AsyncIndexDAG(pTargetDag, idxDagDB, info, idxSt);
		else*/
		count = dagDB.GetSimilar(*pTargetDag, idxSt.matchList, info.idxrange,
			info.modelSimWeight, info.idxtau);
	}

	matSt.matchList.resize(count);

	// Match DAGs returned by the index
	for (i = 0; i < count; i++)
	{
		dSim = idxSt.matchList[i].dSimilarity;

		if (dSim < info.idxtau)
			break;

		// Only retrive the K best allowing multiplicity of the last k one.

		if (i > info.idxKBest && dSim < idxSt.matchList[info.idxKBest].dSimilarity)
			break;

		ptrDag = dagDB.ReadDAG(idxSt.matchList[i].nDAGId, true);

		if (ptrDag.IsNull())
		{
			cerr << "Can't read DAG" << idxSt.matchList[i].nDAGId << endl;
			break;
		}

		// Match DAGs retrive by the indexer as long as the similarity thresholds for
		// the matcher is achivable, ie <= 1. If matchtau > 1, then we just want indexing stats.
		if (info.matchtau <= 1)
		{
			//std::cout << "\n...matching against " << idxSt.matchList[i].nDAGId << std::flush;

			/*if (info.asyncCompu)
				dSim = AsyncMatchDAG(pTargetDag, ptrDag);
			else*/
				dSim = pTargetDag->Similarity(ptrDag);

			//std::cout << " similarity is " << dSim;

			if (dSim >= info.matchtau)
			{
                if (info.saveNodeSim)
				{
					matSt.matchList[i].Set(ptrDag, dSim,
						pTargetDag->GetNodeMapString());
				}
				else
				{
					matSt.matchList[i].Set(ptrDag, dSim);
				}
			}
		}

		idxSt.matchList[i].ptrDAG = ptrDag;
	}

	// Sort best matches
	if (matSt.matchList.size() > 1)
		matSt.matchList.sort();

	if (info.computeStats)
	{
		idxSt.ComputeStats(pTargetDag);
		matSt.ComputeStats(pTargetDag);

		if (info.matchtau <= 1)
			idxSt.ComputeCrossRankingIdxStats(matSt);
	}

	return true;
}

//! Match one DAG against another ONE in the given database
void MatchDAG(const DAG* pImgDag, int firstModelID, int lastModelID,
			  DAGDatabase& dagDB, ShapeMatchingParams& info, STATINFO& matSt)
{
	DAGPtr ptrModelDag;
	int i, modelID;
	double dSim;

	ASSERT(firstModelID <= lastModelID);

	matSt.matchList.resize(lastModelID - firstModelID + 1);

	for (i = 0, modelID = firstModelID; modelID <= lastModelID; i++, modelID++)
	{
		ptrModelDag = dagDB.ReadDAG(modelID, true);

		if (ptrModelDag.IsNull())
		{
			ShowErrorAndNumber("Can't read DAG with ID ", modelID);
			matSt.matchList.resize(0);
			return;
		}

		dSim = pImgDag->Similarity(*ptrModelDag);

		matSt.matchList[i].Set(ptrModelDag, dSim);
	}

	matSt.matchList.sort();
}

void SaveResults(const DAG* pTarget, DAGDatabase& dagDB,
				 const STATINFO& idxSt, const STATINFO& matSt, ostream& os)
{
	const int RANK = 8;
	const int DAGID = 15;
	const int OBJNAME = 25;
	const int VIEWNUM = 10;
	const int SIMIL = 10;
	const int NODEMAT = 20;
	int i;

	os.flags(ios::left);

	os << "\nChoosen graph: " << pTarget->GetDAGLbl();
	os << "\nDAG ID: " << pTarget->GetDAGId() << endl;

	os << "\nINDEXING RESULTS\n\n";
	os << setw(RANK) << "Rank" << setw(DAGID) << "DAG ID" << setw(OBJNAME) << "Object Name"
		<< setw(VIEWNUM) << "View #" << setw(SIMIL) << "Similarity" << endl;

	for(i = 0; i < idxSt.matchList.size() && i < matSt.matchList.size(); i++)
	{
		const MatchInfo& info = idxSt.matchList[i];

		if (info.dSimilarity == 0.0)
			break;

		const DAGPtr ptrDAG = (info.ptrDAG.IsNull()) ? dagDB.ReadDAG(info.nDAGId, true):info.ptrDAG;

		os << endl;
		os << setw(RANK)    << i;
		os << setw(DAGID)   << ptrDAG->GetDAGId();
		os << setw(OBJNAME) << ptrDAG->GetObjName();
		os << setw(VIEWNUM) << ptrDAG->GetViewNumber();
		os << setw(SIMIL)   << info.dSimilarity;
	}

	os << "\n\nMATCHING RESULTS\n\n";
	os << setw(RANK) << "Rank" << setw(DAGID) << "DAG ID" << setw(OBJNAME) << "Object Name"
		<< setw(VIEWNUM) << "View #" << setw(SIMIL) << "Similarity"
		<< setw(NODEMAT) << "Node Correspondences" << endl;

	for(i = 0; i < matSt.matchList.size(); i++)
	{
		const MatchInfo& info = matSt.matchList[i];

		if (info.dSimilarity == 0.0)
			break;

		os << endl;
		os << setw(RANK)    << i;
		os << setw(DAGID)   << info.ptrDAG->GetDAGId();
		os << setw(OBJNAME) << info.ptrDAG->GetObjName();
		os << setw(VIEWNUM) << info.ptrDAG->GetViewNumber();
		os << setw(SIMIL)   << info.dSimilarity;
		os << setw(NODEMAT) << info.nodeMatches;
	}

	if (matSt.bCompViewStats)
	{
		int v1 = 0, v2 = 0, v3 = 0;
		GetClosestViews(pTarget->GetViewNumber(), 3, v1, v2, v3);

		SmartArray<int> secLevClosViews(0, 9);
		GetClosestViews2(pTarget->GetViewNumber(), 3, secLevClosViews, false);

		os << endl;
		os << "\n\nStatistics:\n";
		os << "\n\nClosest views: " << v1 << ' ' << v2 << ' ' << v3;
		os << "\n\nClosest 2nd level views: ";
		secLevClosViews.Print(os);
		os << endl;
	}
}

bool ShowResults(const DAG* pTarget, const STATINFO& matSt,
	DAGDatabase& queryDB, DAGDatabase& modelDB, int nTgtID /*=-1*/)
{
	bool bHaveTgtID = (nTgtID != -1);
	bool bContinue = true;
	int i;

	cout << endl << "Query DAG: " << pTarget->GetDAGLbl() << endl;

	for (i = 0; i < matSt.matchList.size() && bContinue; i++)
	{
		if (matSt.matchList[i].dSimilarity > 0.0)
		{
			if (!bHaveTgtID)
				nTgtID = matSt.queryObj.GetDAGId();

			cout << "DAG " <<  nTgtID
				<< " matches DAG " << matSt.matchList[i].nDAGId
				<< " with similarity " << matSt.matchList[i].dSimilarity * 100
				<< " %" << endl;

			if (bHaveTgtID)
				bContinue = ShowMatch(queryDB, nTgtID, modelDB, matSt.matchList[i].nDAGId);
			else
				bContinue = ShowMatch(pTarget, matSt.matchList[i].ptrDAG);
		}
	}

	// If answered NO...
	//if (i < matSt.matchList.size())
	//	return AskBool("\nKeep showing matches for other queries");

	return true;
}

void AddSubsamplingInfo(ViewSubsampler& vs, const DAG* pTarget, STATINFO& matSt, int kBest)
{
	SmartArray<int> closestViews(0, 9);

	GetClosestViews2(pTarget->GetViewNumber(), 3, closestViews, true);
	vs.AddViewWeights(128, pTarget, matSt.matchList, closestViews, kBest);
}

void SaveSubsamplingInfo(ViewSubsampler& vs, String strDBFileName)
{
	char szFileName[MAX_PATH_SIZE];

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

	vs.SaveViewWeights(szFileName);
}

bool ShowMatch(DAGDatabase& dagDB1, int nDag1Id, DAGDatabase& dagDB2, int nDag2Id)
{
	return ShowMatch(dagDB1.ReadDAG(nDag1Id), dagDB2.ReadDAG(nDag2Id));
}

/*!
	@brief Shows the matching lines between two DAGs.

	Instead of asking for a node correspondece map, we simply run
	the matching algorithm again.
*/
bool ShowMatch(const DAG* pDag1, const DAG* pDag2)
{
	NodeMatchMap nodeMap;

	pDag1->Similarity(*pDag2, nodeMap);

	if (pDag1->ClassName() == "ShockGraph")
		return ShowShockGraphMatch(pDag1, pDag2, nodeMap);
	else if (pDag1->ClassName() == "BoneGraph")
		return ShowBoneGraphMatch(pDag1, pDag2, nodeMap);
	else if (pDag1->ClassName() == "GestureGraph")
		ShowError("Show graph is nto implemented for GestureGraph's");
	else
		ShowError("Unknown graph class");

	return false;
}

bool ShowShockGraphMatch(const DAG* pDag1, const DAG* pDag2, NodeMatchMap& nodeMap)
{
	const ShockGraph* pSG1 = dynamic_cast<const ShockGraph*>(pDag1);
	const ShockGraph* pSG2 = dynamic_cast<const ShockGraph*>(pDag2);

	if (pSG1 && pSG2)
	{
		ShockGraphView sgview;
		sgview.SetMatchMap(nodeMap);
		return sgview.Create(pSG1, pSG2);
	}

	return false;
}

bool ShowBoneGraphMatch(const DAG* pDag1, const DAG* pDag2, NodeMatchMap& nodeMap)
{
	const BoneGraph* pBG1 = dynamic_cast<const BoneGraph*>(pDag1);
	const BoneGraph* pBG2 = dynamic_cast<const BoneGraph*>(pDag2);

	if (pBG1 && pBG2)
	{
		BoneGraphView bgview;
		bgview.SetMatchMap(nodeMap);
		return bgview.Create(pBG1, pBG2);
	}

	return false;
}

bool ShowShockGraph(const DAG* pDag, int x, int y)
{
	const ShockGraph* pSG = dynamic_cast<const ShockGraph*>(pDag);

	if (!pSG)
	{
		ShowError("Invalid ShockGraph pointer");
		return false;
	}

	ShockGraphView sgview;

	return sgview.Create(pSG, x, y);
}

bool ShowBoneGraph(const DAG* pDag, int x, int y)
{
	const BoneGraph* pBG = dynamic_cast<const BoneGraph*>(pDag);

	if (!pBG)
	{
		ShowError("Invalid BoneGraph pointer");
		return false;
	}

	BoneGraphView bgview;

	return bgview.Create(pBG, x, y);
}

/*double SGNodeDistance(const SGNode& n1, const SGNode& n2)
{
	int i, j;
	double total = 0, subtotal, d;

	for (i = 0; i < n1.GetShockCount(); i++)
	{
		subtotal = 1000000;

		for (j = 0; j < n2.GetShockCount(); j++)
		{
			d = abs(abs(n1.m_shocks[i].dr_ds) - abs(n2.m_shocks[j].dr_ds));

			if (d < subtotal)
				subtotal = d;
		}

		total += subtotal;
	}

	return total;
}*/

/*
	@brief Computes a shock graph from the given ppm file.

	@param szFileName PPM file name
	@param outFile The SG will be written into this file
	@logFil Log info will be written in this file
	@info Parameters for the SG computation code
	@bAsyncProcessing Whether a TIME OUT must be used to control the SG computation

	Since the SG computation used to fail, the async processing was necessary to
	avoid a batch processing of files getting stuck in a particular file.
*/
void ProcessFile(const char* szFileName, DAGDatabase& dagDB, fstream& logFile,
				 ShapeRepresentationParams& info, bool bAsyncProcessing)
{
	logFile << endl << '\t' << szFileName << "... ";
	logFile.flush();

#ifndef WIN32
	int status;

	if (bAsyncProcessing)
	{
		if ((g_pid = fork()) != 0)
		{
			signal(SIGALRM, OnAlarm);
			g_nTimeout = info.timeout;
			alarm(info.timeout);
			waitpid(g_pid, &status, 0);

			if (!(WIFEXITED(status)))
				logFile << "ERROR: Seg. Fault";

			// Parent process is done, function returns normaly
			return;
		}
	}
#endif // no WIN32

	// Child process executes the following code...
	DAGPtr pDag;
	bool bIsRead;
	const char* szFileExt;

	szFileExt = DirWalker::FindFileExtension(szFileName);

	if (!strcmp(szFileExt, "ppm") || !strcmp(szFileExt, "pgm") ||
		!strcmp(szFileExt, "bmp") || !strcmp(szFileExt, "tif") ||
		!strcmp(szFileExt, "jpg") || !strcmp(szFileExt, "png"))
	{
		ImageInfo imgInfo(szFileName);

		// Add artificial noise if requested
		if (info.nBumpSize > 0 || info.nNotchSize > 0)
		{
			ShowMsg("Adding noise to the image...");
			AddBumpsAndNotches(szFileName, info, &imgInfo);
		}

		// Choose the appropriate shape representation
		if (info.shapeRepType == SGShapeRep)
		{
			ShockGraph* pSG = new ShockGraph;
			pDag = pSG;

			bIsRead = pSG->Create(imgInfo, info.sgparams, info.skelparams);
		}
		else if (info.shapeRepType == BGShapeRep)
		{
			BoneGraph* pBG = new BoneGraph;
			pDag = pBG;

			bIsRead = pBG->Create(imgInfo, info.bgparams, info.skelparams);
		}
		else
		{
			bIsRead = false;
			ShowError("Only ShockGraph or BoneGraph are valid options for image files.");
		}
	}
	else if (!strcmp(szFileExt, "gg"))
	{
		GestureGraph* pGG = new GestureGraph;
		pDag = pGG;

		bIsRead = pGG->Read(szFileName);
	}
	else
	{
		cerr << "\nUnknown file extension: " << szFileExt << endl;
		bIsRead = false;
	}

	if(bIsRead)
	{
		logFile << "DONE! (" << pDag->GetNodeCount() << " nodes)... " << flush;

		dagDB.AddDAG(pDag, true);

		logFile << "WRITTEN!" << flush;
	}
	else
		logFile << "ERROR: Can't read dag." << flush;

#ifndef WIN32
	// If bAsyncProcessing, the child process must finish
	if (bAsyncProcessing)
	{
		dagDB.Close();
		_exit(0); // Warning exit() with no '_' flushes the buffer and we don't want that.
	}
#endif // no WIN32
}

#ifndef WIN32
void OnAlarm(int param)
{
	kill(g_pid, SIGKILL);
	cerr << "\nTIMEOUT (" << g_nTimeout << " sec)" << endl;
	g_logFile << "TIMEOUT (" << g_nTimeout << " sec)";
}
#endif // no WIN32

/*!
	@brief It ensutes that the given PPM file has square dimentions

	The present version of the SG code crashes if the images aren't square
*/
/*void MakeSquare(const char* szPPMFileName)
{
	pixel** img;
	FILE* fp = fopen(szPPMFileName, "r+");
	int rows, cols, row, col, n, i;
	pixval maxval;
	int format;

	if (fp == NULL)
	{
		cerr << "\nERROR: Can't open " << szPPMFileName
			<< ". Missing or read-only file.\n PPM might not be square." << endl;

		return;
	}

	ppm_readppminit(fp, &cols, &rows, &maxval, &format);

	// If img is already square, there is nothing to do
	if (cols == rows)
		return;

	n = (cols > rows) ? cols:rows;

	img = ppm_allocarray(n, n);

	// Set all the pixels in the new image to white
	for (i = 0; i < n; i++)
		memset(img[i], maxval, sizeof(pixel) * n);

	// Add the old image in the center of the new one
	if (cols > rows)
		for (i = 0, row = (cols - rows) / 2; i < rows; i++, row++)
			ppm_readppmrow(fp, img[row], cols, maxval, format);
	else
		for (i = 0, col = (rows - cols) / 2; i < rows; i++)
			ppm_readppmrow(fp, img[i] + col, cols, maxval, format);

	// Write down the new image
	fseek(fp, 0, SEEK_SET);
	ppm_writeppm(fp, img, n, n, maxval, 0);

	fclose(fp);
}*/

bool AskBool(const char* szQuestion)
{
	char cAnswer;

	cout << szQuestion << "?[y/n]: ";
	cin >> cAnswer;

	return (cAnswer == 'y' || cAnswer == 'Y');
}

int AskInt(const char* szQuestion)
{
	int nAnswer;

	cout << szQuestion << "?: ";
	cin >> nAnswer;

	return nAnswer;
}

