package rapanui.ui.models;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.AbstractListModel;
import javax.swing.SwingUtilities;

import rapanui.core.Transformation;
import rapanui.dsl.BINARY_RELATION;

public class SuggestionListModel extends AbstractListModel<Transformation> {
	private static final long serialVersionUID = 1L;

	private List<Transformation> elements = new ArrayList<Transformation>();
	private Map<String, Transformation> termMap = new HashMap<String, Transformation>();

	public void addSuggestion(Transformation suggestion) {
		String term = suggestion.getOutput().serialize();
		if (!termMap.containsKey(term)) {
			termMap.put(term, suggestion);
			insertElement(suggestion);
		} else if (isPreferable(suggestion, termMap.get(term))) {
			removeElement(termMap.get(term));
			termMap.put(term, suggestion);
			insertElement(suggestion);
		}
	}

	public void clear() {
		int size = elements.size();
		elements.clear();
		termMap.clear();
		fireIntervalRemoved(this, 0, size - 1);
	}

	protected void insertElement(Transformation suggestion) {
		// TODO: maintain ordered list
		elements.add(suggestion);
		fireIntervalAdded(this, elements.size() - 1, elements.size() - 1);
	}

	protected void removeElement(Transformation suggestion) {
		int oldIndex = elements.indexOf(suggestion);
		elements.remove(oldIndex);
		fireIntervalRemoved(this, oldIndex, oldIndex);
	}

	protected boolean isPreferable(Transformation newSuggestion, Transformation oldSuggestion) {
		if (oldSuggestion.getFormulaType() == BINARY_RELATION.INCLUSION
				&& newSuggestion.getFormulaType() == BINARY_RELATION.EQUATION)
			return true; // prefer equation over inclusion
		return false; // prefer earlier suggestions over later ones
		// TODO: prefer simpler over more complex
	}

	@Override
	public Transformation getElementAt(int index) {
		return elements.get(index);
	}

	@Override
	public int getSize() {
		return elements.size();
	}

	@Override
	protected void fireIntervalAdded(Object source, int index0, int index1) {
		SwingUtilities.invokeLater(() -> super.fireIntervalAdded(source, index0, index1));
	}

	@Override
	protected void fireIntervalRemoved(Object source, int index0, int index1) {
		SwingUtilities.invokeLater(() -> super.fireIntervalRemoved(source, index0, index1));
	}

	@Override
	protected void fireContentsChanged(Object source, int index0, int index1) {
		SwingUtilities.invokeLater(() -> super.fireContentsChanged(source, index0, index1));
	}
}

@@ -987,16 +987,35 @@
 size();%0A
+%09%09if (size %3E 0) %7B%0A%09
 %09%09elemen
@@ -1028,16 +1028,17 @@
 ar();%0A%09%09
+%09
 termMap.
@@ -1046,16 +1046,17 @@
 lear();%0A
+%09
 %09%09fireIn
@@ -1085,24 +1085,28 @@
  size - 1);%0A
+%09%09%7D%0A
 %09%7D%0A%0A%09protect
@@ -1396,16 +1396,39 @@
 stion);%0A
+%09%09if (oldIndex %3E 0) %7B%0A%09
 %09%09elemen
@@ -1448,16 +1448,17 @@
 Index);%0A
+%09
 %09%09fireIn
@@ -1494,24 +1494,28 @@
  oldIndex);%0A
+%09%09%7D%0A
 %09%7D%0A%0A%09protect

add safeguards against negative indices