/**------------------------------------------------------------------------
 * @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: CmdLineOptions.cpp,v $
 *
 *-----------------------------------------------------------------------*/

#include "ShapeMatcher.h"

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

//! Version of the ShapeMatcher project
#define SM_VERSION_NUMBER "7.1.1"

// Subset of default values that are most likely to be changed
#define DEFAULT_SHAPE_REP 1        // ShockGraph = 1, GestureGraph = 2, BoneGraph = 3
#define DEFAULT_SKEL_CODE 2        // Voronoi = 1, Flux = 2, AFMM = 3
#define DEFAULT_COMPUTE_STATS 0
#define DEFAULT_SHOW_RESULTS 1
#define DEFAULT_SAVE_RESULTS 0
#define DEFAULT_SAVE_NODE_SIM 0
#define DEFAULT_MATCH_ALGORITHM 0 // Greedy = 0, Optimal = 1, Topological = 2, Adaptive = 3
#define DEFAULT_NODE_SIM_FUNCTION 0

#define DEFAULT_INDEXING_K_BEST 101
#define DEFAULT_INDEXING_SIM_TAU 0.3
#define DEFAULT_MATCHING_SIM_TAU 0//0.1
#define DEFAULT_SIM_MASS_WEIGHT 0.7

/*
	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 from ID or name (eg, objNN)", &m_viewDag, "", CAST(ViewDAG));
	Add('w', "mview", "Visualize a DAGs. Use with -from ID and -to ID", CAST(ViewDAGs));
	Add(0, "nnview", "Visualize a DAG (from ID) and its 9 neighbors",
		&m_viewDag, "", CAST(ViewDAGAndNeighbors));

	Add('t', "table", "Show in a table the shapes in the database", CAST(ShowDAGTable));

	Add('e', "experiment", "Performs experiment with given ID", &m_experimentId, -1);
	Add(0, "expStep", "Experiment step", &m_experimentStep, 0);
	Add(0, "expFirstParam", "First experiment parameter", &m_experimentFirstParam, 0);
	Add(0, "expLastParam", "Last experiment parameter", &m_experimentLastParam, 0);
	Add(0, "expName", "Optional name of the experiment", &m_szExperimentName, "");

	Add('S', "selfDBMatch", "Match each database against itself", CAST(MultiDBMatch));
	Add('M', "multiDBMatch", "Match a target database against all other databases",
		&m_target, "", CAST(MultiDBMatch));

	Add(0, "parse", "Cluster shapes and find prototypes", CAST(CreateDBParsings));

	Add(0, "matchDBP", "Match all query and model DB Parsings",
		&m_matchInfo.selectionMethod, 0, CAST(MatchDBParsings));

	Add(0, "cluster", "Cluster shapes and find prototypes", CAST(FindShapePrototypes));

	Add(0, "nodeMatch", "Match node v1 in graph G1 to v2 in G2", CAST(NodeMatch));

	Add(0, "compare", "Compares DAGs without using indexing", &m_target, "", CAST(CompareDAGs));

	Add(0, "matrix", "Outputs similarity matrix to file [val] for all the models in the database",
		&m_szMatrixFileName, "matrix.m", CAST(CreateAffinityMatrix));

	Add(0, "train", "Create database using some training techinque (experimental)",
		&m_szMatrixFileName, "matrix.m", CAST(AddObjsUsingTraining));

	Add(0, "toXML", "Saves the graphs from '-from ID' to '-to ID' in XML format",
		&m_target, "", CAST(SaveInXMLFormat));

	Add(0, "checkSGGrammar", "Checks the shock graph grammar of all graphs", CAST(CheckSGGrammar));

	Add(0, "delete", "Delete DAGs from database", CAST(DeleteDAGs));
	Add(0, "copy", "Extract DAGs from database", CAST(CopyDAGs));
	Add(0, "updateDB", "Read in the db and write it back", CAST(UpdateDB));
	Add(0, "recompute", "Recomputes all graphs an saves them in a new DB", CAST(RecomputeGraphs));
	Add(0, "splitdb", "Split database in random pieces of sizes"
		" N% and (100-N)% of the total size (see splitByObject) [N]",
		&m_nSplitProportion, 50, CAST(SplitDB));

	Add(0, "incObjDBSplit", "Incremental split of database in subdatabases"
		" with decreasing number of objects", CAST(IncrementalObjectDBSplit));

	Add(0, "incRandomDBSplit", "Incremental split of database in N subdatabases"
		" with decreasing number of views per object [N] {-iterations}",
		&m_nNumSplitParts, 3, CAST(IncrementalRandomDBSplit));

	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, "copyObjects", "Copies all dags of given objects", CAST(CopyObjects));
	Add(0, "delObjects", "Removes all dags of given objects", CAST(DeleteObjects));
	Add(0, "clos3NeigList", "List the 3 closest neighbours of each view", CAST(ClosestNeigList));
	Add(0, "clos9NeigList", "List the 9 closest neighbours of each view", CAST(Closest9NeigList));
	Add(0, "getNeig", "List the neighbours withing a given range in deg.",
		&m_dNeigRange, 180, CAST(GetNeig));

	Add(0, "srcDB", "Sets the soure DB for viewing operations", &m_sourceDB, 0);

	Add(0, "verbose", "Execute in verbose mode", &m_nVerbose, 0);
	Add('h', "help", "Display a man page for this program", CAST(ShowHelp));

	// ADD DAG MATCHING PARAMETERS
	Add(0, "extreme", "Loads all DAGs to memory before matching", &m_matchParams.nExtreme, 0);

	Add(0, "tsvSimWeight", "TSV similarity weight",
		&m_matchParams.dTSVSimWeight, 0.3);
	Add(0, "simMassWeight", "Similarity vs Mass weight",
		&m_matchParams.dSimilMassWeight, DEFAULT_SIM_MASS_WEIGHT);
	Add(0, "relMassWeight", "Relative mass of the query vs. tot model",
		&m_matchParams.dRelMassWeight, 0.5);

	Add(0, "diffAttachPosWeight", "Relative importance of attachment position and angle",
		&m_matchParams.dDiffAttachPosWeight, 0.5);

	Add(0, "matchAlgorithm", "Matching algorithm ID",
		&m_matchParams.nMatchingAlgorithm, DEFAULT_MATCH_ALGORITHM);
	Add(0, "nodeSimFunc", "Node similarity function ID", &
		m_matchParams.nNodeSimilarityFunction, DEFAULT_NODE_SIM_FUNCTION);

	Add(0, "ancestorPen", "Penalty term for breaking ancestor relationships (0=max)",
		&m_matchParams.dBreakAncestorRelPen, 0);
	Add(0, "descendantPen", "Penalty term for breaking desendant relationships (0=max)",
		&m_matchParams.dBreakDescendantRelPen, 0);
	Add(0, "siblingPen", "Penalty term for breaking sibling relationships (0=max)",
		&m_matchParams.dBreakSiblingRelPen, 0.8);

	Add(0, "ancestorPathSigma", "Rate of decay of ancestor relationship penalty (0=no decay)",
		&m_matchParams.dAncestorPathSigma, 0);
	Add(0, "descendantPathSigma", "Rate of decay of descendant relationship penalty (0=no decay)",
		&m_matchParams.dDescendantPathSigma, 0);
	Add(0, "siblingPathSigma", "Rate of decay of sibling relationship penalty (0=no decay)",
		&m_matchParams.dSiblingPathSigma, 0);

	Add(0, "compareNodes", "Measure node similarity", &m_matchParams.nCompareNodes, 1);
	Add(0, "compareEdges", "Measure edge similarity", &m_matchParams.nCompareEdges, 1);

	Add(0, "disableNodeSkipping", "Forbids the skipping of nodes when matching",
		&m_matchParams.nDisableNodeSkipping, 0);

	Add(0, "certaintyParam", "Parameter in the equation of node match certainty",
		&m_matchParams.dCertaintyParam, 2);

	Add(0, "sortType", "Type of sorting for node assignments",
		&m_matchParams.nNodeAssignSortType, 1);

	Add(0, "presAncRel", "Preserve ancestor relationships when matching",
		&m_matchParams.nPreserveAncestorRel, 1);

	Add(0, "useMWBMHeuristic", "Use (or not) MWBM before selecting correspondences",
		&m_matchParams.nUseMWBMHeuristic, 1);

	Add(0, "maxNumSolSets", "Maximum number of solutions sets",
		&m_matchParams.nMaxNumSolSets, 1);

	Add(0, "maxSolSetsPerIter", "Maximum number of new solutions sets per iteration",
		&m_matchParams.nMaxSolSetsPerIter, 1);

	Add(0, "slopeSigma", "Slope sigma for bone graphs",
		&m_matchParams.dSlopeSigma, 0.15);

	Add(0, "wrongSidePen", "Wrong side penalty for bone graphs",
		&m_matchParams.dBGWrongSidePen, 0.6);

	Add(0, "positionSigma", "Wrong position penalty rate for bone graphs (0=linear decay)",
		&m_matchParams.dBGPositionSigma, 0);

	Add(0, "saliencyParam", "Normalization value used to compute node saliency (>= 1)",
		&m_matchParams.dSaliencyParam, 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, DEFAULT_INDEXING_K_BEST);
	Add(0, "idxTau", "Index similarity threshold", &m_matchInfo.idxtau, DEFAULT_INDEXING_SIM_TAU);
	Add(0, "matchTau", "Matcher similarity threshold", &m_matchInfo.matchtau, DEFAULT_MATCHING_SIM_TAU);
	Add(0, "stats", "Compute view statistics", &m_matchInfo.computeStats, DEFAULT_COMPUTE_STATS);
	Add(0, "outDir", "Output directory for the stats files", &m_matchInfo.statsOutDir, ".");
 	Add(0, "asyncCompu", "Compute SG's in async mode (see timeout)", &m_matchInfo.asyncCompu, 0);
 	Add(0, "showResults", "Show matching results", &m_matchInfo.showResults, DEFAULT_SHOW_RESULTS);
 	Add(0, "saveResults", "Save matching results", &m_matchInfo.saveResults, DEFAULT_SAVE_RESULTS);
 	Add(0, "saveNodeSim", "Save node similarity values", &m_matchInfo.saveNodeSim, DEFAULT_SAVE_NODE_SIM);

	Add(0, "firstModelId", "ID of the first model shape to evaluate", &m_matchInfo.firstModelId, -1);
	Add(0, "lastModelId", "ID of the last model shape to evaluate", &m_matchInfo.lastModelId, -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, "firstDBId", "First database ID to create/match", &m_firstDBId, -1);
	Add(0, "lastDBId", "Last database ID to create/match", &m_lastDBId, -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 maintaining equal object proportions", &m_nSplitByObject, 1);
	Add(0, "bumpSize", "Size of noise bumps", &m_shapeInfo.nBumpSize, 0);
	Add(0, "notchSize", "Size of noise notches", &m_shapeInfo.nNotchSize, 0);
	Add(0, "outImgDir", "Sets the output directory for noisy images", &m_shapeInfo.outDir, "./NoisyImgs/");

	// Display parameters
	Add(0, "xpos", "Window x-coord position", &m_xpos, 0);
	Add(0, "ypos", "Window y-coord position", &m_ypos, 0);

	// Generic DAG construction parameters
	Add(0, "useDirNameAsObjName", "Use the (sub)directory name (if given) as the object name",
		&DAG::s_constructParams.nUseDirNameAsObjName, 1);

	// Generic shape representation parameters
	Add(0, "shaperep", "Shape representation: ShockGraph = 1, GestureGraph = 2, BoneGraph = 3",
		&m_shapeInfo.shapeRepType, DEFAULT_SHAPE_REP);

	// Shock Graph parameters
	Add(0, "timeout", "SG computation Timeout in seconds", &m_shapeInfo.timeout, 800);
	Add(0, "split3s", "Should split 3s into left and right 3s", &m_shapeInfo.sgparams.nSlipt3s, 0);
	Add(0, "minslope", "Slope that is to be considered as zero", &m_shapeInfo.sgparams.dMinSlope, 0.05);
	Add(0, "minerror", "#Pts/'value' defines least-square error tolerated", &m_shapeInfo.sgparams.dMinError, 2.0);
	Add(0, "maxydiff", "Maximum difference in y-coord discontinuity", &m_shapeInfo.sgparams.dMaxYDiff, 0.1);
	Add(0, "maxaccelchg", "Max acceleration chage tolerated", &m_shapeInfo.sgparams.dMaxAccelChg, 0.5);

	// Bone Graph parameters
	Add(0, "maxNumSegments", "Max number of segments in piecewise function",
		&m_shapeInfo.bgparams.nMaxNumSegments, 8);

	Add(0, "smoothIter", "Boundary smoothing interations = 0,...,5 (for AFMM only)",
		&m_shapeInfo.bgparams.nSmoothIter, 0);

	Add(0, "maxCumBARLoose", "Maximum cumulative bar for a ligature segment",
		&m_shapeInfo.bgparams.maxCumulativeBAR.loose, 0.75);

	Add(0, "maxCumBARTight", "Maximum cumulative bar for a ligature segment",
		&m_shapeInfo.bgparams.maxCumulativeBAR.tight, 0.75);

	Add(0, "maxRadRatioLoose", "Maximum radius ratio for a ligature segment",
		&m_shapeInfo.bgparams.maxRadiusRatio.loose, 0.7);

	Add(0, "maxRadRatioTight", "Maximum radius ratio for a ligature segment",
		&m_shapeInfo.bgparams.maxRadiusRatio.tight, 0.7);

	Add(0, "maxCornerAngleLoose", "Maximum corner angle for a ligature segment",
		&m_shapeInfo.bgparams.maxCornerAngle.loose, 151.2608);

	Add(0, "maxCornerAngleTight", "Maximum corner angle for a ligature segment",
		&m_shapeInfo.bgparams.maxCornerAngle.tight, 151.2608);

	Add(0, "specializeBones", "Partitions bones according to some criteria",
		&m_shapeInfo.bgparams.nSpecializeBones, 0);

	Add(0, "ignoreLigature", "Testing: ignores ligature information",
		&m_shapeInfo.bgparams.nIgnoreLigature, 0);
	Add(0, "subtractLigatureAtCrossings", "Testing: subtracts ligature rooted at crossings",
		&m_shapeInfo.bgparams.nSubtractLigatureFromCrossings, 1);
	Add(0, "subtractLigatureAtJunctions", "Testing: subtracts ligature at junctions",
		&m_shapeInfo.bgparams.nSubtractLigatureFromJunctions, 0);
	Add(0, "showDependencies", "Show junction point dependencies",
		&m_shapeInfo.bgparams.nShowDependencyGraph, 0);
	Add(0, "showGSG", "Show generalized skeletal graph",
		&m_shapeInfo.bgparams.nShowGSG, 0);

	// Skeleton graph parameters
	Add(0, "skelcode", "Use skeletonization code number = {1, 2, 3}",
		&m_shapeInfo.skelparams.nSkelAlgorithm, DEFAULT_SKEL_CODE);

	// Skeleton Approximation parameters
	Add(0, "minLinearError", "#Pts/'value' defines linear skeleton approximation error",
		&m_shapeInfo.skelparams.dMinLinearError, 2.0);
	Add(0, "minBezierError", "#Pts/'value' defines cubic Bezier skeleton approximation error",
		&m_shapeInfo.skelparams.dMinBezierError, 8.0);
	Add(0, "maxLineSegments", "Max number of line segments per skeleton branch",
		&m_shapeInfo.skelparams.dMaxLineSegments, 8);
	Add(0, "maxBezierSegments", "Max number of cubic Beziers per skeleton branch",
		&m_shapeInfo.skelparams.dMaxBezierSegments, 4);

	// Flux Skeleton params
	Add(0, "fluxStep", "1/'value' defines the sampling step for Flux skeletons",
		&m_shapeInfo.skelparams.fluxStep, 1.0);
	Add(0, "fluxTau", "Flux branch threshold for minimum angle angle",
		&m_shapeInfo.skelparams.fluxTau, 2.5);
	Add(0, "fluxResolution", "Flux value resolution",
		&m_shapeInfo.skelparams.fluxResolution, 16);

	// AFMM Skeleton params
	Add(0, "afmmTau1", "AFMM skeleton threshold 1 (deprecated)",
		&m_shapeInfo.skelparams.afmmTau1, 0);
	Add(0, "afmmTau2", "AFMM branch threshold for minimum radius",
		&m_shapeInfo.skelparams.afmmTau2, 20);

	// Skeleton Simplification params
	Add(0, "bndryWeight", "Boundary reconstruction error weight",
		&m_shapeInfo.skelparams.nBndryRecErrorWeight, 500);
	Add(0, "strucWeight", "Structural reconstruction error weight",
		&m_shapeInfo.skelparams.nStrucRecErrorWeight, 500);
	Add(0, "doExtSimp", "Perform the external branch simplification on the skeleton",
		&m_shapeInfo.skelparams.nSimplifyExternal, 1);
	Add(0, "doIntSimp", "Perform the internal branch simplification on the skeleton",
		&m_shapeInfo.skelparams.nSimplifyInternal, 0);
	Add(0, "doCompleteSimp", "Perform the external and internal branch simplification",
		&m_shapeInfo.skelparams.nSimplifyComplete, 0);
}

void ShapeMatcher::ShowUsageExamples()
{
	std::cerr << "\nShape Matcher " << SM_VERSION_NUMBER;

	std::cerr << "\n\nExamples:\n"
		"\nCreate a database from all the pgm files in a directory and subdirectories:\n"
		"\n\tsm -c 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 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\tsm -w objs.db [obj-id obj-id...]\n";

	std::cerr << std::endl;
}
