//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package de.ugoe.cs.autoquest.coverage;

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

import de.ugoe.cs.autoquest.eventcore.Event;
import de.ugoe.cs.autoquest.usageprofiles.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<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<Event>> containedSubSeqs = null;

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

    /**
     * <p>
     * The probabilities of all subsequences of {@link #length} according to {@link #process}.
     * </p>
     */
    private Map<List<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 &gt;0
     * @throws IllegalArgumentException
     *             thrown if process or sequences is null or length less than or equal to 0
     */
    public CoverageCalculatorProcess(IStochasticProcess process,
                                     Collection<List<Event>> sequences,
                                     int length)
    {
        if (process == null) {
            throw new IllegalArgumentException("process must not be null");
        }
        if (sequences == null) {
            throw new IllegalArgumentException("sequences must not be null");
        }
        if (length <= 0) {
            throw new IllegalArgumentException("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<Event> subSeq : containedSubSeqs) {
            Double curWeight = subSeqWeights.get(subSeq);
            if (curWeight != null) {
                weight += curWeight;
            }
        }
        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 IllegalArgumentException
     *             thrown is newSequences is null
     */
    public void setSequences(Collection<List<Event>> newSequences) {
        if (newSequences == null) {
            throw new IllegalArgumentException("sequences must not be null");
        }
        this.sequences = newSequences;
        containedSubSeqs = null;
    }

}
