import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.deckfour.xes.classification.XEventClass;
import org.deckfour.xes.classification.XEventClasses;
import org.deckfour.xes.classification.XEventClassifier;
import org.deckfour.xes.in.XUniversalParser;
import org.deckfour.xes.info.XLogInfo;
import org.deckfour.xes.info.XLogInfoFactory;
import org.deckfour.xes.info.impl.XLogInfoImpl;
import org.deckfour.xes.model.XLog;
import org.processmining.plugins.InductiveMiner.mining.MiningParametersIM;
import org.processmining.plugins.InductiveMiner.mining.MiningParametersIMi;
import org.processmining.plugins.InductiveMiner.plugins.IMPetriNet;

import org.processmining.contexts.cli.CLI;
import org.processmining.contexts.cli.CLIContext;
import org.processmining.contexts.cli.CLIPluginContext;
import org.processmining.framework.boot.Boot;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.util.ArrayUtils;
import org.processmining.models.graphbased.directed.petrinet.Petrinet; 
import org.processmining.models.graphbased.directed.petrinet.elements.Transition;
import org.processmining.models.semantics.petrinet.Marking; 

import org.processmining.plugins.astar.petrinet.PetrinetReplayerWithILP;
import org.processmining.plugins.astar.petrinet.PetrinetReplayerWithoutILP;
import org.processmining.plugins.astar.petrinet.PrefixBasedPetrinetReplayer;
import org.processmining.plugins.connectionfactories.logpetrinet.TransEvClassMapping;
import org.processmining.plugins.petrinet.replayer.PNLogReplayer;
import org.processmining.plugins.petrinet.replayer.algorithms.IPNReplayAlgorithm;
import org.processmining.plugins.petrinet.replayer.algorithms.IPNReplayParamProvider;
import org.processmining.plugins.petrinet.replayer.algorithms.IPNReplayParameter;
import org.processmining.plugins.petrinet.replayer.algorithms.costbasedcomplete.CostBasedCompleteParam;
import org.processmining.plugins.petrinet.replayer.algorithms.costbasedprefix.CostBasedPrefixAlg;
import org.processmining.plugins.petrinet.replayer.algorithms.costbasedprefix.CostBasedPrefixParam;
import org.processmining.plugins.petrinet.replayer.algorithms.swapping.PetrinetSwapReplayer;
import org.processmining.plugins.petrinet.replayresult.PNRepResult;
import org.processmining.plugins.pnalignanalysis.conformance.AlignmentPrecGen;
import org.processmining.plugins.pnalignanalysis.conformance.AlignmentPrecGenRes;

import org.processmining.plugins.etconformance.ETCResults;

import uk.ac.shef.wit.simmetrics.similaritymetrics.AbstractStringMetric;
import uk.ac.shef.wit.simmetrics.similaritymetrics.Levenshtein;

import org.processmining.models.heuristics.HeuristicsNet;
import org.processmining.plugins.causalnet.miner.FlexibleHeuristicsMinerPlugin;
import org.processmining.plugins.heuristicsnet.miner.heuristics.miner.FlexibleHeuristicsMiner;
import org.processmining.plugins.heuristicsnet.miner.heuristics.miner.settings.HeuristicsMinerSettings;
import org.processmining.plugins.heuristicsnet.miner.heuristics.HeuristicsMinerConstants;

int preSelectOption(String transition, Object[] events, Pattern pattern) {
		// try to find precise match
		for (int i = 1; i < events.length; i++) {
			String event = ((XEventClass) events[i]).toString().toLowerCase();
			if (event.equalsIgnoreCase(transition)) {
				return i;
			}
		}

		Matcher matcher = pattern.matcher(transition);
		if (matcher.find() && matcher.start() == 0) {
			return 0;
		}

		//The metric to get the similarity between strings
		AbstractStringMetric metric = new Levenshtein();
		transition = transition.toLowerCase();
		int index = 0;
		float simOld = Float.MIN_VALUE;
		for (int i = 1; i < events.length; i++) {
			String event = ((XEventClass) events[i]).toString().toLowerCase();
			float sim = metric.getSimilarity(transition, event);

			if (simOld < sim) {
				simOld = sim;
				index = i;
			}
		}

		return index;
}

String fname(String stem, String extension) {
	String fname = new String(stem);
	fname = fname + "k" + numClusters;
	if (penalizeImproperCompletion)
		fname = fname + "penalizeImproperCompletion";
	if (forceSymmetry)
		fname = fname + "forceSymmetry";
	switch (numDimensions) {
		case -1 : { fname = fname + "Sqrt"; break; }
		case -2 : { fname = fname + "Log10"; break; }
		case -3 : { fname = fname + "Log"; break; }
		case -4 : { fname = fname + "Log2"; break; }
		default : { fname = fname + "Dim"+numDimensions; break; }
	}
	if (useSim)
		fname = fname + "useSimilarity";
	fname = fname + "mPR" + mismatchPenaltyRelative + "cGOR" + costGapOpenRelative + "cGER" + costGapExtendRelative;
	fname = fname + "." + extension;
	return fname;
}

String cond() {
	return ("AlignCluster, " + numClusters + ", " + c + ", " + penalizeImproperCompletion + ", " + forceSymmetry + ", " + numDimensions + ", " + useSim + ", " + mismatchPenaltyRelative + ", " + costGapOpenRelative + ", " + costGapExtendRelative + ", ");
}


final String invisibleTransitionRegEx = "[a-z][0-9]+|(tr[0-9]+)|(silent)|(tau)|(skip)|(invisible)";
final Pattern pattern = Pattern.compile(invisibleTransitionRegEx);
