package de.ugoe.cs.eventbench.coverage;

import java.security.InvalidParameterException;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import de.ugoe.cs.eventbench.data.Event;
import de.ugoe.cs.eventbench.models.IStochasticProcess;

/**
 * <p>
 * This class calculates various types of sequence coverage in relation to a
 * stochastic process.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public class CoverageCalculatorProcess {

	/**
	 * <p>
	 * Stochastic process that is the foundation for probabilistic coverages and
	 * coverages with reference to all possible sequences.
	 * </p>
	 */
	private final IStochasticProcess process;

	/**
	 * <p>
	 * Sequences for which the coverage is calculated.
	 * </p>
	 */
	private Collection<List<? extends Event<?>>> sequences;

	/**
	 * <p>
	 * Length of the subsequences in relation to which the coverage is
	 * calculated.
	 * </p>
	 */
	private final int length;

	/**
	 * <p>
	 * All subsequences of {@link #length} of {@link #sequences}.
	 * </p>
	 */
	private Collection<List<? extends Event<?>>> containedSubSeqs = null;

	/**
	 * <p>
	 * All subsequences of {@link #length} that can be generated by
	 * {@link #process}.
	 * </p>
	 */
	private Collection<List<? extends Event<?>>> allPossibleSubSeqs = null;

	/**
	 * <p>
	 * The probabilities of all subsequences of {@link #length} according to
	 * {@link #process}.
	 * </p>
	 */
	private Map<List<? extends Event<?>>, Double> subSeqWeights = null;

	/**
	 * <p>
	 * Constructor. Creates a new CoverageCalculatorProcess for a given
	 * stochastic process and generated sequences.
	 * </p>
	 * 
	 * @param process
	 *            stochastic process used for coverage calculations; must not be
	 *            null
	 * @param sequences
	 *            sequences for which the coverage is calculated; must not be
	 *            null
	 * @param length
	 *            length of the subsequences for which the coverage is analyzed;
	 *            must be >0
	 * @throws InvalidParameterException
	 *             thrown if process or sequences is null or length less than or equal to 0
	 */
	public CoverageCalculatorProcess(IStochasticProcess process,
			Collection<List<? extends Event<?>>> sequences, int length) {
		if (process == null) {
			throw new InvalidParameterException("process must not be null");
		}
		if (sequences == null) {
			throw new InvalidParameterException("sequences must not be null");
		}
		if (length <= 0) {
			throw new InvalidParameterException(
					"length must be >0; actual value: " + length);
		}
		this.process = process;
		this.sequences = sequences;
		this.length = length;
	}

	/**
	 * <p>
	 * Calculates the percentage of subsequences of length k that occur,
	 * including those that cannot be generated by {@link #process}.
	 * </p>
	 * 
	 * @return coverage percentage
	 */
	public double getCoverageAllNoWeight() {
		if (containedSubSeqs == null) {
			containedSubSeqs = SequenceTools.containedSubSequences(sequences,
					length);
		}
		return ((double) containedSubSeqs.size())
				/ SequenceTools.numSequences(process, length);
	}

	/**
	 * <p>
	 * Calculates the percentage of subsequences of length k that occur and can
	 * generated by {@link #process}.
	 * </p>
	 * 
	 * @return coverage percentage
	 */
	public double getCoveragePossibleNoWeight() {
		if (containedSubSeqs == null) {
			containedSubSeqs = SequenceTools.containedSubSequences(sequences,
					length);
		}
		if (allPossibleSubSeqs == null) {
			allPossibleSubSeqs = process.generateSequences(length);
		}
		return ((double) containedSubSeqs.size()) / allPossibleSubSeqs.size();
	}

	/**
	 * <p>
	 * Calculates the weight of the subsequences that occur with relation to
	 * {@link #process}, i.e., the mass of the subsequence probability covered
	 * by the subsequences.
	 * </p>
	 * 
	 * @return coverage weight
	 */
	public double getCoveragePossibleWeight() {
		if (containedSubSeqs == null) {
			containedSubSeqs = SequenceTools.containedSubSequences(sequences,
					length);
		}
		if (allPossibleSubSeqs == null) {
			allPossibleSubSeqs = process.generateSequences(length);
		}
		if (subSeqWeights == null) {
			subSeqWeights = SequenceTools.generateWeights(process,
					allPossibleSubSeqs);
		}
		double weight = 0.0;
		for (List<? extends Event<?>> subSeq : containedSubSeqs) {
			weight += subSeqWeights.get(subSeq);
		}
		return weight;
	}

	/**
	 * <p>
	 * Returns the number of covered subsequences of length k.
	 * </p>
	 * 
	 * @return number of covered subsequences
	 */
	public int getNumCovered() {
		if (containedSubSeqs == null) {
			containedSubSeqs = SequenceTools.containedSubSequences(sequences,
					length);
		}
		return containedSubSeqs.size();
	}

	/**
	 * <p>
	 * Returns the number of possible subsequences of length k according to the
	 * stochastic process.
	 * </p>
	 * 
	 * @return number of possible subsequences
	 */
	public int getNumPossible() {
		if (allPossibleSubSeqs == null) {
			allPossibleSubSeqs = process.generateSequences(length);
		}
		return allPossibleSubSeqs.size();
	}

	/**
	 * <p>
	 * Sets a new collection of sequences for which the coverage is analyzed.
	 * </p>
	 * 
	 * @param newSequences
	 *            new collection of sequences
	 * @throws InvalidParameterException
	 *             thrown is newSequences is null
	 */
	public void setSequences(Collection<List<? extends Event<?>>> newSequences) {
		if (newSequences == null) {
			throw new InvalidParameterException("sequences must not be null");
		}
		this.sequences = newSequences;
		containedSubSeqs = null;
	}

}
