package strucTypes5;

import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.strucTypes5.constraints.*;
import de.dhbwstuttgart.strucTypes5.solve.Rules;
import de.dhbwstuttgart.strucTypes5.typeVars.*;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.typecheck.JavaClassName;
import de.dhbwstuttgart.typeinference.constraints.Constraint;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

/**
	For Develop the solve Funktion
 */
public class SolveTest_Dev {
	private static final String rootDirectory = System.getProperty("user.dir") + "/test/strucTypes5/";


	RefType myInteger;

	@Test
	public void run() {

		// Initialisiere den gleichen RefType
		List<RefTypeOrTPHOrWildcardOrGeneric> list = new ArrayList<>();
		myInteger = new RefType(new JavaClassName("MyInteger"),list, new NullToken());


		TypeVarRefType typeVarRefType1 = TypeVarFactory.makeTypeVar(myInteger);
		TypeVarRefType typeVarRefType2 = TypeVarFactory.makeTypeVar(myInteger);

		System.out.println(typeVarRefType1.equals(typeVarRefType2));




		System.out.println("Starte Test der Regeln");

		/*
		Wie soll das Funktionieren
		- klein sigma ist der Unifikator
		- Rules ist solange anzuwenden bis alles unifiziert oder oder keine Regel mehr angewendet werden kann.
		-
		 */

		// Erstellen Beispiel:


		testSwap();

		testErase1();

		testErase2();

		testSubstition();

		reflectTest();

		reduceEqualTest();

		adapt1Test();

		adapt2Test();

	}






	public void testSwap() {

		System.out.println("Teste Swap");
		TypeVarRefType typeVarRefType = TypeVarFactory.makeTypeVar(myInteger);
		TypeVar typeVar = TypeVarFactory.makeTypeVar();

		List<ConstraintAbstract> constraints = new ArrayList<>();
		ConstraintShouldEqual cs = new ConstraintShouldEqual(typeVarRefType,typeVar);

		constraints.add(cs);

		System.out.println(constraints);
		Rules rules = new Rules();
		rules.swap(constraints);
		System.out.println(constraints);
	}


	// Subtype Relation
	public void testErase1() {
		System.out.println("Test Erase 1 ");
		TypeVarRefType typeVarRefType1 = TypeVarFactory.makeTypeVar(myInteger);
		TypeVarRefType typeVarRefType2 = TypeVarFactory.makeTypeVar(myInteger);
		ConstraintSubType constraintSubType = new ConstraintSubType(typeVarRefType1,typeVarRefType2);

		List<ConstraintAbstract> constraints = new ArrayList<>();
		constraints.add(constraintSubType);
		System.out.println(constraints);

		Rules rules = new Rules();
		rules.erase1(constraints);

		System.out.println(constraints);
	}


	// Erase SchouldEqual
	public void testErase2() {
		System.out.println("Test Erase 2 ");
		TypeVarRefType typeVarRefType1 = TypeVarFactory.makeTypeVar(myInteger);
		TypeVarRefType typeVarRefType2 = TypeVarFactory.makeTypeVar(myInteger);
		ConstraintShouldEqual constraintShouldEqual  = new ConstraintShouldEqual(typeVarRefType1,typeVarRefType2);

		List<ConstraintAbstract> constraints = new ArrayList<>();
		constraints.add(constraintShouldEqual);
		System.out.println(constraints);

		Rules rules = new Rules();
		rules.erase2(constraints);

		System.out.println(constraints);
	}


	public void testSubstition() {
		System.out.println("Test Substitions ");
		TypeVar typeVar = TypeVarFactory.makeTypeVar();
		TypeVarRefType typeVarRefType = TypeVarFactory.makeTypeVar(myInteger);
		ConstraintShouldEqual constraintShouldEqual  = new ConstraintShouldEqual(typeVar,typeVarRefType);

		List<ConstraintAbstract> constraints = new ArrayList<>();
		constraints.add(constraintShouldEqual);
		System.out.println(constraints);

		Rules rules = new Rules();
		rules.subst(constraints);
		System.out.println(rules.substitutions);

		System.out.println(constraints);
	}


	public void reflectTest() {
		System.out.println("Reflect Test");
		TypeVarRefType t1 = TypeVarFactory.makeTypeVar(myInteger);
		TypeVar t2 = TypeVarFactory.makeTypeVar();
		TypeVar t3 = TypeVarFactory.makeTypeVar();
		TypeVarRefType t4 = TypeVarFactory.makeTypeVar(myInteger);

		ConstraintSubType csub1 = new ConstraintSubType(t1,t2);
		ConstraintSubType csub2 = new ConstraintSubType(t2,t3);
		ConstraintSubType csub3 = new ConstraintSubType(t3,t4);

		List<ConstraintAbstract> constraints = new ArrayList<>();
		//constraints.add(csub2);
		constraints.add(csub1);
		constraints.add(csub3);

		System.out.println(constraints);

		Rules rules = new Rules();
		rules.reflect(constraints);

		System.out.println(constraints);
		System.out.println(rules.substitutions);
	}




	public void reduceEqualTest() {
		System.out.println("ReduceEqualTest");

		// Gemeinsamer SrucType
		TypeVar t1 = TypeVarFactory.makeTypeVar();

		// Erstelle Interface 1
		List<TypeVarAbstract> genericsA = new ArrayList<>();
		TypeVar t2 = TypeVarFactory.makeTypeVar();
		TypeVar t3 = TypeVarFactory.makeTypeVar();
		genericsA.add(t3);
		genericsA.add(t2);
		InterfaceForConstraint if1 = new InterfaceForConstraint(t1,genericsA);


		// Erstelle Interface 2
		List<TypeVarAbstract> genericsB = new ArrayList<>();
		TypeVarRefType refType1 = TypeVarFactory.makeTypeVar(myInteger);
		TypeVarRefType refType2 = TypeVarFactory.makeTypeVar(myInteger);
		genericsB.add(refType1);
		genericsB.add(refType2);
		InterfaceForConstraint if2 = new InterfaceForConstraint(t1,genericsB);


		List<ConstraintAbstract> constraints = new ArrayList<>();

		constraints.add(new ConstraintShouldEqual(TypeVarFactory.makeTypeVar(if2) , TypeVarFactory.makeTypeVar(if1)));
		System.out.println(constraints);

		Rules rules = new Rules();
		rules.reduceEqual(constraints);
		System.out.println(constraints);
	}



	public void adapt1Test() {
		System.out.println("Adapt1 Test");

		// gemeinsamer Typ
		TypeVarAbstract t1 = TypeVarFactory.makeTypeVar();

		// implements Interface myInteger implements Interface
		List<TypeVarAbstract> generics = new ArrayList<>();
		generics.add(TypeVarFactory.makeTypeVar(myInteger));
		generics.add(TypeVarFactory.makeTypeVar(myInteger));
		InterfaceForConstraint if1 = new InterfaceForConstraint(t1, generics);
		ConstraintInterface constraintInterface = new ConstraintInterface(TypeVarFactory.makeTypeVar(myInteger) , TypeVarFactory.makeTypeVar(if1) );
		List<ConstraintInterface> interfaces = new ArrayList<>();
		interfaces.add(constraintInterface);

		// Erstelle zu testenden Constraint
		List<TypeVarAbstract> genericsA = new ArrayList<>();
		TypeVar t2 = TypeVarFactory.makeTypeVar();
		TypeVar t3 = TypeVarFactory.makeTypeVar();
		genericsA.add(t3);
		genericsA.add(t2);
		InterfaceForConstraint if2 = new InterfaceForConstraint(t1,genericsA);


		ConstraintSubType constraintSubType = new ConstraintSubType(TypeVarFactory.makeTypeVar(myInteger) , TypeVarFactory.makeTypeVar(if2));
		List<ConstraintAbstract> constraints = new ArrayList<>();
		constraints.add(constraintSubType);


		System.out.println("Implemented Interfaces: " + interfaces);

		System.out.println("ConstraintSet: " + constraints);

		Rules rules = new Rules();
		rules.setConstraintInterfaces(interfaces);

		rules.adapt1(constraints);
		System.out.println("Neue Constraints: " + constraints);

		rules.reduceEqual(constraints);
		System.out.println(constraints);
	}




	public void adapt2Test() {
		System.out.println("Adapt2 Test");

		// gemeinsamer Typ
		TypeVarAbstract t1 = TypeVarFactory.makeTypeVar();

		// extend
		TypeVarAbstract text1 = TypeVarFactory.makeTypeVar();
		TypeVarAbstract text2 = TypeVarFactory.makeTypeVar();
		ConstraintSubType csub = new ConstraintSubType(text1,text2);


		// implements Interface myInteger implements Interface
		List<TypeVarAbstract> generics = new ArrayList<>();
		generics.add(TypeVarFactory.makeTypeVar(myInteger));
		generics.add(TypeVarFactory.makeTypeVar(myInteger));
		InterfaceForConstraint if1 = new InterfaceForConstraint(t1, generics);
		ConstraintInterface constraintInterface = new ConstraintInterface(TypeVarFactory.makeTypeVar(myInteger) , TypeVarFactory.makeTypeVar(if1) );
		List<ConstraintInterface> interfaces = new ArrayList<>();
		interfaces.add(constraintInterface);

		// Erstelle zu testenden Constraint
		List<TypeVarAbstract> genericsA = new ArrayList<>();
		TypeVar t2 = TypeVarFactory.makeTypeVar();
		TypeVar t3 = TypeVarFactory.makeTypeVar();
		genericsA.add(t3);
		genericsA.add(t2);
		InterfaceForConstraint if2 = new InterfaceForConstraint(t1,genericsA);


		ConstraintSubType constraintSubType = new ConstraintSubType(TypeVarFactory.makeTypeVar(myInteger) , text1);
		ConstraintSubType constraintSubType1 = new ConstraintSubType(text2 , TypeVarFactory.makeTypeVar(if2));
		List<ConstraintAbstract> constraints = new ArrayList<>();
		constraints.add(constraintSubType);
		constraints.add(constraintSubType1);
		constraints.add(csub);


		System.out.println("Implemented Interfaces: " + interfaces);

		System.out.println("ConstraintSet: " + constraints);

		Rules rules = new Rules();
		rules.setConstraintInterfaces(interfaces);

		rules.adapt2(constraints);
		System.out.println("Neue Constraints: " + constraints);

		//rules.reduceEqual(constraints);
		//System.out.println(constraints);
	}



















}
