package rapanui.dsl;

import java.util.HashMap;
import java.util.Map;

/**
 * Learns a translation dictionary from given translations.
 */
public class TranslationFinder {
	private final Map<String, Term> dictionary;
	private final Translator translator;

	/**
	 * Creates a new instance with an empty dictionary.
	 */
	public TranslationFinder() {
		this(new HashMap<String, Term>());
	}

	/**
	 * Creates a new instance with a given dictionary.
	 *
	 * @param dictionary The dictionary to use for translations.
	 *
	 * Note: The dictionary is copied by-reference. Therefore, all changes to the dictionary affect the instance.
	 */
	public TranslationFinder(Map<String, Term> dictionary) {
		assert dictionary != null;
		this.dictionary = dictionary;
		this.translator = new Translator(dictionary);
	}

	public Map<String, Term> getDictionary() {
		return dictionary;
	}

	/**
	 * Creates a new instance with the current instance's dictionary. Subsequent changes to the original
	 * or the clone will no affect the other.
	 */
	public TranslationFinder clone() {
		return new TranslationFinder(new HashMap<String, Term>(dictionary));
	}

	/**
	 * Translates the given term using the dictionary the instance has learned so far.
	 *
	 * @throws Translator.IncompleteDictionaryException
	 */
	public Term translate(Term original) {
		return translator.translate(original);
	}

	/**
	 * Informs the instance of a valid translation. From this, the instance derives dictionary entries
	 * and checks if the translation is compatible with what it has previously learned.
	 *
	 * @param original The original term
	 * @param translation The term the original term was translated to
	 * @return False if this translation conflicts with previously learned dictionary entries, true otherwise.
	 *
	 * Note that this method may affect the instance's dictionary even if false is returned. Currently,
	 * the only way to reset is to perform this operation on a clone.
	 */
	public boolean train(Term original, Term translation) {
		if (original instanceof VariableReference)
			return train((VariableReference)original, translation);
		else if (original instanceof ConstantReference)
			return train((ConstantReference)original, translation);
		else if (original instanceof UnaryOperation)
			return train((UnaryOperation)original, translation);
		else if (original instanceof BinaryOperation)
			return train((BinaryOperation)original, translation);
		throw new IllegalStateException("Unknown term type: " + original.eClass());
	}

	private boolean train(VariableReference from, Term to) {
		if (dictionary.containsKey(from.getVariable()))
			return dictionary.get(from.getVariable()).structurallyEquals(to);
		dictionary.put(from.getVariable(), to);
		return true;
	}

	private boolean train(ConstantReference from, Term to) {
		return to instanceof ConstantReference && from.getConstant().equals(((ConstantReference)to).getConstant());
	}

	private boolean train(UnaryOperation from, Term to) {
		if (!(to instanceof UnaryOperation) || ((UnaryOperation)to).getOperator() != from.getOperator())
			return false;
		return train(from.getOperand(), ((UnaryOperation)to).getOperand());
	}

	private boolean train(BinaryOperation from, Term to) {
		if (!(to instanceof BinaryOperation) || ((BinaryOperation)to).getOperator() != from.getOperator())
			return false;
		return train(from.getLeft(), ((BinaryOperation)to).getLeft())
				&& train(from.getRight(), ((BinaryOperation)to).getRight());
	}
}

@@ -1667,17 +1667,45 @@
 lated to
+. May be null or incomplete.
 %0A
-
 %09 * @ret
@@ -2034,24 +2034,121 @@
 nslation) %7B%0A
+%09%09assert original != null && original.isComplete();%0A%0A%09%09if (translation == null)%0A%09%09%09return true;%0A%0A
 %09%09if (origin
@@ -2692,24 +2692,64 @@
  Term to) %7B%0A
+%09%09String variable = from.getVariable();%0A
 %09%09if (dictio
@@ -2769,39 +2769,34 @@
 Key(
-from.getV
+v
 ariable
-(
 ))
-)
+ %7B
 %0A%09%09%09
-return 
+try %7B%0A%09%09%09%09
 dict
@@ -2806,56 +2806,147 @@
 ary.
-get(from
+put(variable, to.mergeTemplate(dictionary
 .get
-V
+(v
 ariable
-()).structurallyEquals(to);%0A
+)));%0A%09%09%09%7D catch (IncompatibleTemplateException e) %7B%0A%09%09%09%09return false;%0A%09%09%09%7D%0A%09%09%7D else%0A%09
 %09%09di
@@ -2958,34 +2958,24 @@
 ary.put(
-from.getV
+v
 ariable
-()
 , to);%0A%09

handle incomplete translations