package learn.struct.algs;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;

import rep.UndirectedGraph;
import util.files.UndirectedGraphParser;
import util.math.UndirectedGraphsQualityMeasures;
import dataset.Dataset;
import dataset.DatasetType;
import dataset.FileHandler;
import dataset.adtree.ADNode;

/**
 * This class runs the MPLInterIAMBandHillClimbing algorithm for Markov network
 * structure learning
 * 
 */
public class TestMPLInterIAMBandHillClimbing {
	
	private static String dir = "/home/fschluter/experiments/datasets/sampled/undirected/mpl";
	private static String datasetName = "generatingModel2.n5";
	private static String resultsDir = "/home/fschluter/foo";
	private static int domainSize = 2;
	private static Dataset dataset;
	private static String testSufix = ".test.data";
	private static String trainSufix = ".ts.data";
	private static int D = 10;
	private static int numberOfTestsForAccuracy = 2000;
	private static boolean useADTree;
	private static String output;

	/**
	 * Runs the experiment
	 * 
	 * @param args Arguments: 	 -dir : Path to datasets folder .
	 * -D: Number of data points to read from data .
	 * -datasetName: Name of dataset without sufixes.  
	 * -trainSufix: Sufix of train dataset. It will learn the structure from the file in $dir/$datasetName$trainSufix .  
	 * -testSufix: Sufix of test dataset. It will evaluate the structure from the file in $dir/$datasetName$testSufix . 
	 * -resultsDir: Folder where the results file will be stored .
	 * -useADTree: boolean value specifying if ADTree cache is used to get better performance for contingency tables construction.   
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		if (args.length == 0) {
			/* Default arguments used */
			String arg = "-alg mplIAMBHC "
					+ "-dir data "
					+ "-dataset msnbc -numTests 2000 "
					+ "-resultsDir results "
					+ "-testSufix .test.data -trainSufix .ts.data "
					+ "-D 1000 -useADTree true";
			runExperiment(arg.split(" "));
			System.out.println(output);
		} else {
			/* External arguments used */
			runExperiment(args);
		}

	}

	private static void runExperiment(String[] args) throws IOException {
		/* Arguments parsing */
		parseParameters(args);

		/* Read data set */
		DatasetType type = DatasetType.CSV;
		File fileDs = new java.io.File(dir + File.separator + datasetName
				+ trainSufix);
		dataset = FileHandler.readDataset(fileDs, D, type);

		/* Set binary cardinalities for the example data set */
		int n = dataset.getDomainVariables().size();
		int[] card = new int[n];
		for (int i = 0; i < n; ++i)
			card[i] = 2;
		dataset.setCard(card);

		/* ADTree cache use if -useADTree parameter is true */
		ADNode adTree = null;
		if (useADTree)
			adTree = ADNode.makeADNode(dataset);

		/* Runtime */

		long runtime = new GregorianCalendar().getTimeInMillis();

		/* Optimizing the Marginal Pseudo Likelihood scoring function 
		 * for Markov networks structure discover, 
		 * as proposed in (Pensar et al, 2014), Section 5. 
		 * */
		MPLInterIAMBandHillClimbing mplMMHC = new MPLInterIAMBandHillClimbing(
				n, dataset, true, useADTree, adTree);
		mplMMHC.run();
		runtime = new GregorianCalendar().getTimeInMillis() - runtime;
		UndirectedGraph outputGraph = mplMMHC.getOutputNetwork();

		/* Saving results */
		String resultsFile = resultsDir + File.separator
				+ "rawResults_generatingModel_" + datasetName
				+ "_alg_mplIAMBHC";

		/* Saving libra file for parameters learning with Libra toolkit */
		UndirectedGraphParser.writeGraphInLibraFormat(outputGraph, domainSize,
				resultsFile + ".mn");
		/* Saving .graph file for storing tha adjacency matrix of the graph learned */
		UndirectedGraphParser.writeGraphInAdjacencyMatrixFormat(outputGraph,
				resultsFile + ".graph");

		/* Computing accuracy of the learned structure in the test data set */
		double accuracy = UndirectedGraphsQualityMeasures.accuracy(outputGraph,
				dir + File.separator + datasetName + testSufix, D,
				numberOfTestsForAccuracy, 123456);

		/* Creating results file */
		FileWriter fstream;
		BufferedWriter out;

		fstream = new FileWriter(resultsFile + ".csv", true);
		out = new BufferedWriter(fstream);
		
		SimpleDateFormat dt = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		output = "mplIAMBHC " + " " + runtime + " " + accuracy + " "
				+ D + " " + datasetName + " " + dt.format(new Date()) ;
		out.write(output);
		out.write("\n");
		out.close();
	}

	/**
	 * Parsing external parameters.
	 * 
	 * @param args Arguments: 	 
	 * -dir : Path to datasets folder .
	 * -alg : Scoring function used. Available values are: {bjp, ibscore, mpl} . 
	 * -D: Number of data points to read from data .
	 * -datasetName: Name of dataset without sufixes.  
	 * -trainSufix: Sufix of train dataset. It will learn the structure from the file in $dir/$datasetName$trainSufix .  
	 * -testSufix: Sufix of test dataset. It will evaluate the structure from the file in $dir/$datasetName$testSufix . 
	 * -resultsDir: Folder where the results file will be stored .
	 * -useADTree: boolean value specifying if ADTree cache is used to get better performance for contingency tables construction.   

	 */
	private static void parseParameters(String[] args) {
		for (int i = 0; i < args.length; i++) {
			if (args[i].equals("-dir")) {
				dir = args[i + 1];
			} else if (args[i].equals("-D")) {
				D = new Integer(args[i + 1]);
			} else if (args[i].equals("-dataset")) {
				datasetName = args[i + 1];
			} else if (args[i].equals("-numTests")) {
				numberOfTestsForAccuracy = new Integer(args[i + 1]);
			} else if (args[i].equals("-resultsDir")) {
				resultsDir = args[i + 1];
			} else if (args[i].equals("-testSufix")) {
				testSufix = args[i + 1];
			} else if (args[i].equals("-trainSufix")) {
				trainSufix = args[i + 1];
			} else if (args[i].equals("-useADTree")) {
				useADTree = new Boolean(args[i + 1]);
			}
		}
	}

}
