package learn.struct.algs;

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

import learn.struct.scoring.GraphScorable;
import learn.struct.scoring.bjp.BJP;
import learn.struct.scoring.search.HillClimbing;
import rep.UndirectedGraph;
import dataset.Dataset;
import dataset.DatasetType;
import dataset.FileHandler;
import dataset.adtree.ADNode;

/**
 * This class runs the HillClimbing algorithm for incremental domain sizes to
 * determine the number of ascents required by the BJP score to converge
 */
public class TestAscentsBJPHillClimbing {

	private static String dir;
	private static String[] datasetNames;
	private static String resultsDir;
	private static Dataset dataset;
	private static List<Integer> Ds;
	private static int reps;
	private static boolean useADTree;
	private static String output;

	/**
	 * Runs the experiment
	 * 
	 * @param args
	 * @throws IOException
	 * @throws ParseException
	 */
	public static void main(String[] args) throws IOException, ParseException {
		if (args.length == 0) {
			/* Default arguments used */
			String arg = "-sf bjp "
					+ "-dir /home/fschluter/experiments/datasets/mpl "
					+ "-dataset generatingModel2.n6.ds.2 "
					+ "generatingModel5.n16.ds.2 "
					+ "generatingModel7.n32.ds.2 "
					+ "generatingModel9.n64.ds.2 "
					+ "generatingModel11.n128.ds.2 " + "-numTests 2000 "
					+ "-resultsDir results " + "-reps 10 "
					+ "-Ds 500 1000 2000 4000 8000 16000 " + "-useADTree true";
			runExperiment(arg.split(" "));
		} else {
			/* External arguments used */
			runExperiment(args);
		}

	}

	/**
	 * Runs the experiment using the specified arguments in @arg args.
	 * 
	 * @param args
	 *            Arguments: * -dir : Path to datasets folder . -sf : Scoring
	 *            function used. Available values are: {bjp, ibscore, mpl} . -D:
	 *            Number of data points to read from data . -datasetName: Name
	 *            of dataset without sufixes. -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
	 * @throws ParseException
	 */
	private static void runExperiment(String[] args) throws IOException,
			ParseException {
		/* Arguments parsing */
		parseParameters(args);

		/* Running the experiment for each algorithm in param -alg */
		for (String datasetName : datasetNames) {
			for (int D : Ds) {
				int[] ascents = new int[reps];
				long[] runtime = new long[reps];
				for (int r = 0; r < reps; ++r) {
					/* Read data set */
					DatasetType type = DatasetType.CSV;
					File fileDs = new java.io.File(dir + File.separator
							+ datasetName + ".r." + (r + 1) + ".seed.1.csv");
					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 */
					runtime[r] = new GregorianCalendar().getTimeInMillis();

					GraphScorable scoringFunction = new BJP();

					/* Hill climbing search */
					HillClimbing hc = new HillClimbing(n, dataset,
							scoringFunction, true, useADTree, adTree);
					hc.setIncremental(true);
					hc.run(new UndirectedGraph(n));
					runtime[r] = new GregorianCalendar().getTimeInMillis()
							- runtime[r];

					/*
					 * Computing accuracy of the learned structure in the test
					 * data set
					 */
					ascents[r] = hc.getNumberOfAscents();

				}

				/* Saving results */
				String resultsFile = resultsDir + File.separator
						+ "bjpAscents_" + D + ".csv";

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

				fstream = new FileWriter(resultsFile, true);
				out = new BufferedWriter(fstream);

				SimpleDateFormat dt = new SimpleDateFormat(
						"yyyy-MM-dd hh:mm:ss");
				double ascentsAvg = 0;
				for (int r = 0; r < reps; ++r)
					ascentsAvg += ascents[r];
				ascentsAvg = ascentsAvg / reps;
				double runtimeAvg = 0;
				for (int r = 0; r < reps; ++r)
					runtimeAvg += runtime[r];
				runtimeAvg = runtimeAvg / reps;
				output = "bjp " + runtimeAvg + " " + ascentsAvg + " " + D + " "
						+ datasetName + " " + dt.format(new Date());
				out.write(output);
				out.write("\n");
				out.close();

				System.out.println(output);
			}
		}
	}

	/**
	 * 
	 * Parsing external parameters.
	 * 
	 * @param args
	 *            Arguments: * -dir : Path to datasets folder . -sf : Scoring
	 *            function used. Available values are: {bjp, ibscore, mpl} . -D:
	 *            Number of data points to read from data . -datasetName: Name
	 *            of dataset without sufixes. -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("-Ds")) {
				List<Integer> aux = new ArrayList<Integer>();
				int dIndex = i + 1;
				while (!args[dIndex].contains("-") || dIndex == args.length - 1) {
					aux.add(new Integer(args[dIndex]));
					++dIndex;
				}
				Ds = aux;
			} else if (args[i].equals("-reps")) {
				reps = new Integer(args[i + 1]);
			} else if (args[i].equals("-dataset")) {
				List<String> aux = new ArrayList<String>();
				int dIndex = i + 1;
				while (!args[dIndex].contains("-") || dIndex == args.length - 1) {
					aux.add(args[dIndex]);
					++dIndex;
				}
				datasetNames = new String[aux.size()];
				datasetNames = aux.toArray(datasetNames);
			} else if (args[i].equals("-resultsDir")) {
				resultsDir = args[i + 1];
			} else if (args[i].equals("-useADTree")) {
				useADTree = new Boolean(args[i + 1]);
			}
		}
	}

}
