package de.ugoe.cs.eventbench.models;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import de.ugoe.cs.eventbench.data.Event;
import de.ugoe.cs.util.console.Console;

public class PredictionByPartialMatch extends TrieBasedModel {
	
	double probEscape;
	
	public PredictionByPartialMatch(int markovOrder, Random r) {
		this(markovOrder, r, 0.1);
	}
	
	public PredictionByPartialMatch(int markovOrder, Random r, double probEscape) {
		super(markovOrder, r);
		this.probEscape = probEscape;
	}
	
	public void setProbEscape(double probEscape) {
		this.probEscape = probEscape;
	}
	
	public double getProbEscape() {
		return probEscape;
	}
	
	@Override
	protected double getProbability(List<Event<?>> context, Event<?> symbol) {
		double result = 0.0d;
		double resultCurrentContex = 0.0d;
		double resultShorterContex = 0.0d;
		
		List<Event<?>> contextCopy = new LinkedList<Event<?>>(context); // defensive copy

	
		List<Event<?>> followers = trie.getFollowingSymbols(contextCopy); // \Sigma'
		int sumCountFollowers = 0; // N(s\sigma')
		for( Event<?> follower : followers ) {
			sumCountFollowers += trie.getCount(contextCopy, follower);
		}
		
		int countSymbol = trie.getCount(contextCopy, symbol); // N(s\sigma)
		if( contextCopy.size()==0 ) {
			resultCurrentContex = ((double) countSymbol) / sumCountFollowers;
		} else {
			resultCurrentContex = ((double) countSymbol / sumCountFollowers)*(1-probEscape);
			contextCopy.remove(0); 
			double probSuffix = getProbability(contextCopy, symbol);
			if( followers.size()==0 ) {
				resultShorterContex = probSuffix;
			} else {
				resultShorterContex = probEscape*probSuffix;
			}
		}
		result = resultCurrentContex+resultShorterContex;
		
		return result;
	}
	
	public void testStuff() {
		// basically an inline unit test without assertions but manual observation
		List<Event<?>> list = new ArrayList<Event<?>>();
		list.add(new Event<Object>("a"));
		list.add(new Event<Object>("b"));
		list.add(new Event<Object>("r"));
		list.add(new Event<Object>("a"));
		list.add(new Event<Object>("c"));
		list.add(new Event<Object>("a"));
		list.add(new Event<Object>("d"));
		list.add(new Event<Object>("a"));
		list.add(new Event<Object>("b"));
		list.add(new Event<Object>("r"));
		list.add(new Event<Object>("a"));
		
		int maxOrder = 3;
		PredictionByPartialMatch model = new PredictionByPartialMatch(maxOrder, new Random());
		model.trie = new Trie<Event<?>>();
		model.trie.train(list, maxOrder);
		model.trie.display();
		
		List<Event<?>> context = new ArrayList<Event<?>>();
		Event<Object> symbol = new Event<Object>("a");
		// expected: 5
		Console.traceln(""+model.trie.getCount(context, symbol));
		
		// expected: 0
		context.add(new Event<Object>("b"));
		Console.traceln(""+model.trie.getCount(context, symbol));
		
		// expected: 2
		context.add(new Event<Object>("r"));
		Console.traceln(""+model.trie.getCount(context, symbol));
		
		// exptected: [b, r]
		context = new ArrayList<Event<?>>();
		context.add(new Event<Object>("a"));
		context.add(new Event<Object>("b"));
		context.add(new Event<Object>("r"));
		Console.traceln(model.trie.getContextSuffix(context).toString());
		
		// exptected: []
		context = new ArrayList<Event<?>>();
		context.add(new Event<Object>("e"));
		Console.traceln(model.trie.getContextSuffix(context).toString());
		
		// exptected: {a, b, c, d, r}
		context = new ArrayList<Event<?>>();
		Console.traceln(model.trie.getFollowingSymbols(context).toString());
		
		// exptected: {b, c, d}
		context = new ArrayList<Event<?>>();
		context.add(new Event<Object>("a"));
		Console.traceln(model.trie.getFollowingSymbols(context).toString());
		
		// exptected: []
		context = new ArrayList<Event<?>>();
		context.add(new Event<Object>("a"));
		context.add(new Event<Object>("b"));
		context.add(new Event<Object>("r"));
		Console.traceln(model.trie.getFollowingSymbols(context).toString());
		
		// exptected: 0.0d
		context = new ArrayList<Event<?>>();
		context.add(new Event<Object>("a"));
		Console.traceln(""+model.getProbability(context, new Event<Object>("z")));
	}
}
