package unify;

import junit.framework.Assert;

import org.junit.Test;

import de.dhbwstuttgart.syntaxtree.factory.UnifyPairMengenBuilder;
import de.dhbwstuttgart.syntaxtree.factory.UnifyTypeFactory;
import de.dhbwstuttgart.syntaxtree.factory.Unify_FC_TTO_Builder;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.typeinference.Menge;
import de.dhbwstuttgart.typeinference.Pair;
import de.dhbwstuttgart.typeinference.Pair.PairOperator;
import de.dhbwstuttgart.typeinference.unify.Unify;

public class UnifyOldTest {

	@Test
	public void unifyTestSimpleTypes() {
		// Init Factories and Builders
		UnifyTypeFactory typeFactory = new UnifyTypeFactory();
		Unify_FC_TTO_Builder fcBuilder = new Unify_FC_TTO_Builder();
		UnifyPairMengenBuilder assumptionBuilder = new UnifyPairMengenBuilder();
		UnifyPairMengenBuilder resultBuilder = new UnifyPairMengenBuilder();

		/*
		 * Test a <. Boolean
		 */

		// Init Types
		RefType boolT = typeFactory.GetSimpleType("java.lang.Boolean");
		TypePlaceholder aTph = typeFactory.GetTypePlaceholder("a");

		// Expected Result
		resultBuilder.clear();
		resultBuilder.addPair(aTph, boolT, PairOperator.Equal);
		resultBuilder.addPair(aTph, typeFactory.GetExtendsType(boolT),
				PairOperator.Equal);
		Menge<Menge<Pair>> expectedResult = resultBuilder.getNestedPairMenge();

		// Actual Result
		assumptionBuilder.clear();
		assumptionBuilder.addPair(aTph, boolT);
		Menge<Menge<Pair>> actualResult = Unify.unify(
				assumptionBuilder.getPairMenge(), fcBuilder.Get_FC_TTO());

		// System.out.println(expectedResult);
		// System.out.println(actualResult);

		Assert.assertTrue(mengeEquals(expectedResult, actualResult));

		/*
		 * Test b <. a, a <. Boolean
		 */

		// Init Types
		boolT = typeFactory.GetSimpleType("java.lang.Boolean");
		aTph = typeFactory.GetTypePlaceholder("a");
		TypePlaceholder bTph = typeFactory.GetTypePlaceholder("b");

		// Expected Result
		resultBuilder.clear();
		resultBuilder.addPair(aTph, boolT, PairOperator.Equal);
		resultBuilder.addPair(aTph, typeFactory.GetExtendsType(boolT),
				PairOperator.Equal);
		resultBuilder.addPair(bTph, boolT, PairOperator.Equal);
		resultBuilder.addPair(bTph, typeFactory.GetExtendsType(boolT),
				PairOperator.Equal);
		expectedResult = resultBuilder.getNestedPairMenge();

		// Actual Result
		assumptionBuilder.clear();
		assumptionBuilder.addPair(bTph, aTph);
		assumptionBuilder.addPair(aTph, boolT);
		actualResult = Unify.unify(assumptionBuilder.getPairMenge(),
				fcBuilder.Get_FC_TTO());

		// System.out.println(expectedResult);
		// System.out.println(actualResult);

		// NOTE: Elemente im actualResult sind nicht unique
		// Assert.assertTrue(mengeEquals(expectedResult, actualResult));

		/*
		 * Test b <. a, a <. b
		 */

		aTph = typeFactory.GetTypePlaceholder("a");
		bTph = typeFactory.GetTypePlaceholder("b");

		// Expected Result
		resultBuilder.clear();
		resultBuilder.addPair(bTph, aTph);
		resultBuilder.addPair(aTph, bTph);

		Menge<Pair> buffer = resultBuilder.getPairMenge();
		expectedResult = new Menge<Menge<Pair>>();
		expectedResult.add(buffer);

		// Actual Result
		assumptionBuilder.clear();
		assumptionBuilder.addPair(bTph, aTph);
		assumptionBuilder.addPair(aTph, bTph);
		actualResult = Unify.unify(assumptionBuilder.getPairMenge(),
				fcBuilder.Get_FC_TTO());

		// System.out.println(expectedResult);
		// System.out.println(actualResult);

		Assert.assertTrue(mengeEquals(expectedResult, actualResult));

		/*
		 * Test Integer <. a, a <. Boolean
		 */

		RefType intT = typeFactory.GetSimpleType("java.lang.Integer");
		boolT = typeFactory.GetSimpleType("java.lang.Boolean");
		aTph = typeFactory.GetTypePlaceholder("a");
		bTph = typeFactory.GetTypePlaceholder("b");

		// Expected Result
		resultBuilder.clear();
		expectedResult = resultBuilder.getNestedPairMenge();

		// Actual Result
		assumptionBuilder.clear();
		assumptionBuilder.addPair(intT, aTph);
		assumptionBuilder.addPair(aTph, boolT);
		actualResult = Unify.unify(assumptionBuilder.getPairMenge(),
				fcBuilder.Get_FC_TTO());

		// System.out.println(expectedResult);
		// System.out.println(actualResult);

		Assert.assertTrue(mengeEquals(expectedResult, actualResult));

	}

	@Test
	public void unifyTestGenerics() {

		// Init Factories and Builders
		UnifyTypeFactory typeFactory = new UnifyTypeFactory();
		Unify_FC_TTO_Builder fcBuilder = new Unify_FC_TTO_Builder();
		UnifyPairMengenBuilder assumptionBuilder = new UnifyPairMengenBuilder();
		UnifyPairMengenBuilder resultBuilder = new UnifyPairMengenBuilder();

		/*
		 * Test a <. MyClass<T, F>
		 */

		TypePlaceholder aTph = typeFactory.GetTypePlaceholder("a");
		RefType myType = typeFactory.GetSimpleType("MyClass",
				typeFactory.GetTypePlaceholder("T"),
				typeFactory.GetTypePlaceholder("F"));

		// Expected Result
		resultBuilder.clear();
		resultBuilder.addPair(aTph, myType, PairOperator.Equal);
		resultBuilder.addPair(aTph, typeFactory.GetExtendsType(myType));
		Menge<Menge<Pair>> expectedResult = resultBuilder.getNestedPairMenge();

		// Actual Result
		assumptionBuilder.clear();
		assumptionBuilder.addPair(aTph, myType);
		Menge<Menge<Pair>> actualResult = Unify.unify(
				assumptionBuilder.getPairMenge(), fcBuilder.Get_FC_TTO());

		// System.out.println(expectedResult);
		// System.out.println(actualResult);

		Assert.assertTrue(mengeEquals(expectedResult, actualResult));

		/*
		 * Test List<List<T>> <. List<T>
		 */

		TypePlaceholder tTph = typeFactory.GetTypePlaceholder("T");
		RefType list = typeFactory.GetSimpleType("List", tTph);
		RefType listlist = typeFactory.GetSimpleType("List", list);

		// Expected Result
		resultBuilder.clear();
		resultBuilder.addPair(typeFactory.GetExtendsType(list), tTph,
				PairOperator.Equal);
		expectedResult = resultBuilder.getNestedPairMenge();

		// Actual Result
		assumptionBuilder.clear();
		assumptionBuilder.addPair(listlist, list);
		actualResult = Unify.unify(assumptionBuilder.getPairMenge(),
				fcBuilder.Get_FC_TTO());

		System.out.println(expectedResult);
		System.out.println(actualResult);

		Assert.assertTrue(mengeEquals(expectedResult, actualResult));

		/*
		 * Test List<T> <. List<List<T>>
		 */
	}

	@Test
	public void unifyTestInheritance() {

		// Init Factories and Builders
		UnifyTypeFactory typeFactory = new UnifyTypeFactory();
		Unify_FC_TTO_Builder fcBuilder = new Unify_FC_TTO_Builder();
		UnifyPairMengenBuilder assumptionBuilder = new UnifyPairMengenBuilder();
		UnifyPairMengenBuilder resultBuilder = new UnifyPairMengenBuilder();

		// Init Types
		RefType tBool = typeFactory.GetSimpleType("java.lang.Boolean");
		RefType tString = typeFactory.GetSimpleType("java.lang.String");
		RefType tInt = typeFactory.GetSimpleType("java.lang.Integer");
		TypePlaceholder tphA = typeFactory.GetTypePlaceholder("a");

		// Build inheritance hierachy
		// Bool <. String <. Int
		fcBuilder.AddInheritance(tBool, tString);
		fcBuilder.AddInheritance(tString, tInt);

		// Build Assumptions
		assumptionBuilder.addPair(tphA, tString);

		// Build expected result
		resultBuilder.addPair(tphA, tBool, PairOperator.Equal);
		resultBuilder.addPair(tphA, typeFactory.GetExtendsType(tBool),
				PairOperator.Equal);
		resultBuilder.addPair(tphA, tString, PairOperator.Equal);
		resultBuilder.addPair(tphA, typeFactory.GetExtendsType(tString),
				PairOperator.Equal);

		// Assert
		Menge<Menge<Pair>> actualResult = Unify.unify(
				assumptionBuilder.getPairMenge(), fcBuilder.Get_FC_TTO());

		// System.out.println(actualResult);
		// System.out.println("-------------------");
		// System.out.println(resultBuilder.getNestedPairMenge());

		Assert.assertTrue(mengeEquals(resultBuilder.getNestedPairMenge(),
				actualResult));
	}

	@Test
	public void unifyTestWildcards() {

	}

	private static boolean mengeEquals(Menge<Menge<Pair>> m1,
			Menge<Menge<Pair>> m2) {
		if (m1.size() != m2.size())
			return false;

		return containsAll(m1, m2) && containsAll(m2, m1);
	}

	private static boolean containsAll(Menge<Menge<Pair>> m1,
			Menge<Menge<Pair>> m2) {
		for (Menge<Pair> elem : m2)
			if (!contains(m1, elem))
				return false;
		return true;
	}

	private static boolean contains(Menge<Menge<Pair>> m1, Menge<Pair> m2) {
		for (Menge<Pair> elem : m1)
			if (mengePairEquals(elem, m2))
				return true;
		return false;
	}

	private static boolean mengePairEquals(Menge<Pair> m1, Menge<Pair> m2) {
		if (m1.size() != m2.size())
			return false;

		return containsAllPair(m1, m2) && containsAllPair(m2, m1);
	}

	private static boolean containsAllPair(Menge<Pair> m1, Menge<Pair> m2) {
		for (Pair elem : m1)
			if (contains(m2, elem))
				return true;
		return false;
	}

	private static boolean contains(Menge<Pair> m, Pair p) {
		for (Pair elem : m)
			if (pairEquals(elem, p))
				return true;
		return false;

	}

	private static boolean pairEquals(Pair p1, Pair p2) {
		return (p1.TA1.equals(p2.TA1) && p1.TA2.equals(p2.TA2))
				|| (p1.TA1.equals(p2.TA2) && p1.TA2.equals(p2.TA1));
	}

}