package de.ugoe.cs.eventbench.commands;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import de.ugoe.cs.eventbench.CommandHelpers;
import de.ugoe.cs.eventbench.coverage.CoverageCalculatorProcess;
import de.ugoe.cs.eventbench.coverage.SequenceTools;
import de.ugoe.cs.eventbench.data.Event;
import de.ugoe.cs.eventbench.data.GlobalDataContainer;
import de.ugoe.cs.eventbench.models.IStochasticProcess;
import de.ugoe.cs.util.ArrayTools;
import de.ugoe.cs.util.console.Command;
import de.ugoe.cs.util.console.Console;

/**
 * <p>
 * Command to generate test suite with a greedy strategy to achieve a desired
 * coverage.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public class CMDgenerateGreedy implements Command {

	/**
	 * <p>
	 * Tolerance for double comparisons
	 * </p>
	 */
	final double eps = 0.000000000001;

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.ugoe.cs.util.console.Command#run(java.util.List)
	 */
	@Override
	public void run(List<Object> parameters) {
		String modelname;
		String sequencesName;
		int minLength;
		int maxLength;
		int coverageDepth;
		float desiredCoverage;
		try {
			modelname = (String) parameters.get(0);
			sequencesName = (String) parameters.get(1);
			minLength = Integer.parseInt((String) parameters.get(2));
			maxLength = Integer.parseInt((String) parameters.get(3));
			coverageDepth = Integer.parseInt((String) parameters.get(4));
			desiredCoverage = Float.parseFloat((String) parameters.get(5));
		} catch (Exception e) {
			throw new InvalidParameterException();
		}

		IStochasticProcess model = null;
		Object dataObject = GlobalDataContainer.getInstance()
				.getData(modelname);
		if (dataObject == null) {
			CommandHelpers.objectNotFoundMessage(modelname);
			return;
		} else if (!(dataObject instanceof IStochasticProcess)) {
			CommandHelpers.objectNotType(modelname, "IStochasticProcess");
			return;
		}
		model = (IStochasticProcess) dataObject;

		// set up everything
		List<List<? extends Event<?>>> allSequences = new ArrayList<List<? extends Event<?>>>();
		for (int length = minLength; length <= maxLength; length++) {
			allSequences.addAll(model.generateValidSequences(length + 2));
		}
		Console.traceln("" + allSequences.size() + " possible");

		Set<List<? extends Event<?>>> allSubSeqs = SequenceTools
				.containedSubSequences(allSequences, coverageDepth);
		Map<List<? extends Event<?>>, Double> weightMap = SequenceTools
				.generateWeights(model, allSubSeqs);
		Set<List<? extends Event<?>>> coveredSubSeqs = new LinkedHashSet<List<? extends Event<?>>>();

		List<Set<List<? extends Event<?>>>> containedSubSeqs = new ArrayList<Set<List<? extends Event<?>>>>(
				allSequences.size());
		for (List<? extends Event<?>> sequence : allSequences) {
			List<List<? extends Event<?>>> wrapper = new LinkedList<List<? extends Event<?>>>();
			wrapper.add(sequence);
			Set<List<? extends Event<?>>> currentSubSeqs = SequenceTools
					.containedSubSequences(wrapper, coverageDepth);
			containedSubSeqs.add(currentSubSeqs);
		}

		Double[] sequenceGain = new Double[allSequences.size()];
		List<List<? extends Event<?>>> testSuite = new LinkedList<List<? extends Event<?>>>();
		CoverageCalculatorProcess coverageCalculator = new CoverageCalculatorProcess(
				model, testSuite, coverageDepth);
		double currentCoverage = 0.0d;

		// Build test suite
		while (currentCoverage < desiredCoverage) {
			for (int i = 0; i < allSequences.size(); i++) {
				double gain = 0.0d;
				for (List<? extends Event<?>> subSeq : containedSubSeqs.get(i)) {
					if (!coveredSubSeqs.contains(subSeq)) {
						gain += weightMap.get(subSeq);
					}
				}
				sequenceGain[i] = gain;
			}
			int maxIndex = ArrayTools.findMax(sequenceGain);
			if (sequenceGain[maxIndex] <= 0.0 + eps) {
				Console.traceln("No gain anymore! Desired coverage cannot be satisfied!");
				break;
			}
			testSuite.add(allSequences.get(maxIndex));
			coveredSubSeqs.addAll(containedSubSeqs.get(maxIndex));
			coverageCalculator.setSequences(testSuite);
			currentCoverage = coverageCalculator.getCoveragePossibleWeight();
		}

		if (GlobalDataContainer.getInstance().addData(sequencesName, testSuite)) {
			CommandHelpers.dataOverwritten(sequencesName);
		}
		Console.println("" + testSuite.size() + " sequences generated");
		Console.println("" + currentCoverage + " coverage achieved");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.ugoe.cs.util.console.Command#help()
	 */
	@Override
	public void help() {
		Console.println("generateGreedy <modelname> <sequencesName> <minLength> <maxLength> <coverageDepth> <desiredCoverage>");
	}

}
