package de.ugoe.cs.eventbench.commands;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import de.ugoe.cs.eventbench.CommandHelpers;
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.console.Command;
import de.ugoe.cs.util.console.Console;

/**
 * <p>
 * Command to generate sequences of a given length.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public class CMDgenerateHybrid implements Command {

	/*
	 * (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 length;
		int maxLengthall;
		int numSequences;
		int maxIterOuter = 100;
		int maxIterInner = 1000;
		try {
			modelname = (String) parameters.get(0);
			sequencesName = (String) parameters.get(1);
			length = Integer.parseInt((String) parameters.get(2));
			maxLengthall = Integer.parseInt((String) parameters.get(3));
			numSequences = Integer.parseInt((String) parameters.get(4));
		} 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;
		Collection<List<? extends Event<?>>> sequences = new LinkedHashSet<List<? extends Event<?>>>();
		
		List<List<? extends Event<?>>> seqsTmp = new ArrayList<List<? extends Event<?>>>(model.generateSequences(maxLengthall+1, true));
		
		Console.traceln("" + seqsTmp.size() + " of length " + maxLengthall + " possible");
		List<Double> probabilities = new ArrayList<Double>(seqsTmp.size());
		double probSum = 0.0;
		for (List<? extends Event<?>> sequence : seqsTmp) {
			double prob = model.getProbability(sequence);
			probabilities.add(prob);
			probSum += prob;
		}
		
		Random r = new Random();
		int j=0;
		while (sequences.size() < numSequences && j<=maxIterOuter) {
			j++;
			double randVal = r.nextDouble() * probSum;
			double sum = 0.0d;
			int index = -1;
			while (sum < randVal) {
				index++;
				double currentProb = probabilities.get(index);
				sum += currentProb;
			}
			List<? extends Event<?>> seqTmp = seqsTmp.get(index);
			if( seqTmp.get(seqTmp.size()-1)!=Event.ENDEVENT ) {
				for( int i=0 ; i<maxIterInner ; i++ ) {
					List<? extends Event<?>> sequence = finishSequence(seqTmp, model);
					if( sequence.size() == length+2 ) {
						j=0;
						sequences.add(sequence);
						break;
					}
				}
			}
		}
		if (GlobalDataContainer.getInstance().addData(sequencesName, sequences)) {
			CommandHelpers.dataOverwritten(sequencesName);
		}
		Console.println("" + sequences.size() + " sequences generated");
	}
	
	public List<? extends Event<?>> finishSequence(List<? extends Event<?>> sequence, IStochasticProcess model) {
		Random r = new Random();
		List<Event<?>> sequenceCopy =  new LinkedList<Event<?>>(sequence);

		boolean endFound = false;

		while (!endFound) {
			double randVal = r.nextDouble();
			double probSum = 0.0;
			for (Event<?> symbol : model.getEvents()) {
				probSum += model.getProbability(sequenceCopy, symbol);
				if (probSum >= randVal) {
					endFound = (symbol == Event.ENDEVENT);
					if (!(symbol == Event.STARTEVENT )) {
						// only add the symbol the sequence if it is not START
						// or END
						sequenceCopy.add(symbol);
					}
					break;
				}
			}
		}
		return sequenceCopy;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.ugoe.cs.util.console.Command#help()
	 */
	@Override
	public void help() {
		Console.println("Usage: generateFixedLengthSequences <modelname> <sequencesName> <lenght> <maxlengthAll> <numSequences>");
	}

}
