package Model;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

/**
 * Model. This class provides a container for the data in the LtMoW tool.
 *
 */
public class Model implements Observer {

	private List<Situation> situations_;
	private Parameters parameters_;
	private WordFilter wordFilter_;
	private HashSet<Word> focusedWords_;
	private HashSet<Word> wordTable_;
	private List<Word> learnedWords_;
	private Map<Integer, Integer> wordsLearnedOverTime_;
	private int processIteration_;

	/**
	 * Standard constructor
	 */
	public Model() {
		situations_ = new LinkedList<Situation>();
		parameters_ = new Parameters();
		wordFilter_ = new WordFilter();
		focusedWords_ = new HashSet<Word>();
		wordTable_ = new HashSet<Word>();
		learnedWords_ = new LinkedList<Word>();
		wordsLearnedOverTime_ = new HashMap<Integer, Integer>();
		wordsLearnedOverTime_.put(1, 0);
		processIteration_ = 1;
	}

	/**
	 * Explicit constructor
	 * 
	 * @param situations
	 * 			Situations in this Model
	 * @param parameters
	 * 			Parameters for this Model (set in GUI)
	 * @param wordfilter
	 * 			WordFilter of this Model
	 */
	public Model(List<Situation> situations, Parameters parameters,
			WordFilter wordfilter) {
		situations_ = situations;
		parameters_ = parameters;
		wordFilter_ = wordfilter;
		focusedWords_ = new HashSet<Word>();
		wordTable_ = new HashSet<Word>();
		learnedWords_ = new LinkedList<Word>();
		wordsLearnedOverTime_ = new HashMap<Integer, Integer>();
		wordsLearnedOverTime_.put(1, 0);
		processIteration_ = 1;
	}

	// Setter
	/**
	 * @param a
	 * 			List of Situations
	 */
	public void setSituations(List<Situation> a) {
		situations_ = a;
	}

	/**
	 * @param a
	 * 			Parameters object
	 */
	public void setParameters(Parameters a) {
		parameters_ = a;
	}

	/**
	 * @param a
	 * 			WordFilter object
	 */
	public void setWordFilter(WordFilter a) {
		wordFilter_ = a;
	}

	/**
	 * @param a
	 * 			HashSet of focused words
	 */
	public void setFocusedWords(HashSet<Word> a) {
		focusedWords_ = a;
	}

	/**
	 * @param a
	 * 			HashSet of words in WordTable
	 */
	public void setWordTable(HashSet<Word> a) {
		wordTable_ = a;
	}

	/**
	 * @param w
	 * 			List of learned Words
	 */
	public void setLearnedWords(List<Word> w) {
		learnedWords_ = w;
	}

	/**
	 * @param m
	 * 			Map from Integers to Integer, which shows how many words were learned in which iteration
	 */
	public void setWordsLearnedOverTime(Map<Integer, Integer> m) {
		wordsLearnedOverTime_ = m;
	}

	// Getter
	/**
	 * @return	List of Situations
	 */
	public List<Situation> getSituations() {
		return situations_;
	}

	/**
	 * @return Parameters object
	 */
	public Parameters getParameters() {
		return parameters_;
	}

	/**
	 * @return wordFilter object
	 */
	public WordFilter getWordFilter() {
		return wordFilter_;
	}

	/**
	 * @return focused words
	 */
	public HashSet<Word> getFocusedWords() {
		return focusedWords_;
	}

	/**
	 * @return word table
	 */
	public HashSet<Word> getWordTable() {
		return wordTable_;
	}

	/**
	 * @return learned words
	 */
	public List<Word> getLearnedWords() {
		return learnedWords_;
	}

	/**
	 * @return words learned over time map
	 */
	public Map<Integer, Integer> getWordsLearnedOverTime() {
		return wordsLearnedOverTime_;
	}

	/**
	 * @param s 
	 * 			String you want to derive a Meaning for
	 * @return
	 * 			Meaning object for string s
	 */
	public Meaning getMeaningForString(String s) {
		for (Situation situ : situations_) {
			for (Meaning m : situ.getScene()) {
				if (m.getStringRep() == s) {
					return m;
				}
			}
		}
		// If this branch is taken something went terribly wrong! Arr!
		return new Meaning("Error: No meaning with this string representation");
	}

	// ---------------------------------------------------

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
	 */

	public void update(Observable o, Object arg) {
		Word w;
		if (o instanceof Word) {
			w = (Word) o;
		} else {
			return;
		}

		Pair<Integer, HashMap<Meaning, Double>> p = w.getLastState();
		int iteration = p.getFirst();

		/*
		 * Check if the currently updated word belongs to the same iteration as
		 * the word before. If this is the case, no accumulation of values will
		 * be done. If this is not the case, the initial value of the current
		 * iteration will be set to the final wlot value of the previous
		 * iteration.
		 * 
		 * Since processIteration_ is initialized with 1, this conditional will
		 * evaluate to true for the first time in the second iteration, where a
		 * value for the previous iteration is available.
		 */
		if (iteration > processIteration_) {
			int value = wordsLearnedOverTime_.get(processIteration_);
			wordsLearnedOverTime_.put(iteration, value);

			processIteration_ = iteration;
		}

		Map<Meaning, Double> probabilityDistribution = p.getSecond();

		// Get correct meaning by checking for a meaning with the same string
		// representation as the word
		Meaning correctMeaning = getMeaningForString(w.getStringRep());
		if (correctMeaning == null) {
			// Since no correct meaning is found, the comprehension_score is set
			// to 0
			return;
		}

		if (!probabilityDistribution.containsKey(correctMeaning))
			return;
		
		if (probabilityDistribution.get(correctMeaning) >= getWordFilter()
				.getLT()) {
			if (!getLearnedWords().contains(w)) {
				// comprehension_score >= T and word not learned yet
				// WLOT[i] = WLOT[i] + 1
				int wlot_i = wordsLearnedOverTime_.get(iteration);
				wordsLearnedOverTime_.put(iteration, ++wlot_i);
				// mark word as learned
				getLearnedWords().add(w);
			}
		} else {
			if (getLearnedWords().contains(w)) {
				// comprehension_score < T and word is learned
				// WLOT[i] = WLOT[i] - 1
				int wlot_i = wordsLearnedOverTime_.get(iteration);
				wordsLearnedOverTime_.put(iteration, --wlot_i);
				// mark word as not learned
				getLearnedWords().remove(w);
			}
		}
	}
}
