package de.dhbwstuttgart.strucTypes3;

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

import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.statement.Expression;
import de.dhbwstuttgart.syntaxtree.statement.FieldVar;
import de.dhbwstuttgart.syntaxtree.statement.LocalVar;
import de.dhbwstuttgart.syntaxtree.statement.MethodCall;
import de.dhbwstuttgart.syntaxtree.statement.Receiver;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.strucTypes3.AssTuple;
import de.dhbwstuttgart.strucTypes3.ConstraintAbstract;
import de.dhbwstuttgart.strucTypes3.FieldConstraint;
import de.dhbwstuttgart.strucTypes3.GleichConstraint;
import de.dhbwstuttgart.strucTypes3.MethodConstraint;

public class HelpMethoden {

	public static String expression2String(Expression expression) {

		// LocalVar
		if (expression.getClass().equals(LocalVar.class)) {
			String resultStr = "";
			LocalVar lvar = (LocalVar) expression;
			resultStr = resultStr + lvar.get_expression();
			return resultStr;
		}
		// FeldVar
		else if (expression.getClass().equals(FieldVar.class)) {
			FieldVar fieldVar = (FieldVar) expression;
			Expression receiver = fieldVar.getReceiver();
			String result = HelpMethoden.expression2String(receiver);
			result = result + "." + fieldVar.getFieldVarName();
			return result;
		}
		// MethodCall
		else if (expression.getClass().equals(MethodCall.class)) {
			MethodCall methodCall = (MethodCall) expression;

			// nur bei MethodCall reciever = Typ Reciver mit eigenem TPH
			Expression receiver = methodCall.get_Receiver();

			String result;
			if (receiver.getClass().equals(Receiver.class)) {
				Receiver r_cast = (Receiver) receiver;
				result = HelpMethoden.expression2String(r_cast.get_Expression());
			} else {
				result = HelpMethoden.expression2String(receiver);
			}

			// Jetzt muss noch die ArgumentListe ermittelt werden
			// Problem, Diese kann alles enthalten
			String argumentsStr = "";
			List<Expression> arguments = methodCall.get_ArgList().getArguments();
			for (Expression arg : arguments) {
				argumentsStr = argumentsStr + " " + HelpMethoden.expression2String(arg);
			}

			result = String.format("%s.%s(%s)", result, methodCall.get_Name(), argumentsStr);
			return result;
		} else {
			System.err.println("Fehler");
			return null;
		}
	}

	public static String expression_typed2String(Expression expression) {

		// LocalVar
		if (expression.getClass().equals(LocalVar.class)) {
			String resultStr = "";
			LocalVar lvar = (LocalVar) expression;
			resultStr = resultStr + lvar.get_expression() + ":" + lvar.getType().toString();
			return resultStr;
		}
		// FeldVar
		else if (expression.getClass().equals(FieldVar.class)) {
			FieldVar fieldVar = (FieldVar) expression;
			Expression receiver = fieldVar.getReceiver();
			String result = HelpMethoden.expression_typed2String(receiver);
			result = result + "." + fieldVar.getFieldVarName() + ":" + fieldVar.getType().toString();
			return result;
		}
		// MethodCall
		else if (expression.getClass().equals(MethodCall.class)) {
			MethodCall methodCall = (MethodCall) expression;

			// nur bei MethodCall reciever = Typ Reciver mit eigenem TPH
			Expression receiver = methodCall.get_Receiver();

			String result;
			if (receiver.getClass().equals(Receiver.class)) {
				Receiver r_cast = (Receiver) receiver;
				result = HelpMethoden.expression_typed2String(r_cast.get_Expression());
			} else {
				result = HelpMethoden.expression_typed2String(receiver) + ":" + receiver.getType().toString();
			}

			// Jetzt muss noch die ArgumentListe ermittelt werden
			// Problem, Diese kann alles enthalten
			String argumentsStr = "";
			List<Expression> arguments = methodCall.get_ArgList().getArguments();
			for (Expression arg : arguments) {
				argumentsStr = argumentsStr + " " + HelpMethoden.expression_typed2String(arg) ;//+ arg.getType();
			}

			result = String.format("%s.%s(%s)", result, methodCall.get_Name(), argumentsStr);
			return result;
		} else {
			System.err.println("Fehler");
			return null;
		}
	}

	
	// Diese Methode übernimmt die Berechnung der Typen
	public static List<ConstraintAbstract> TypeExpr(AssTuple ass, Expression expression, Method method, ClassOrInterface cl) {

		// LocalVar (Constraints für den Algo, Constraints für die Interfaces
		// usw. )
		// LocalVar ------------------------------------------------------------------------------------------
		if (expression.getClass().equals(LocalVar.class)) {
			LocalVar lvar = (LocalVar) expression;
			List<ConstraintAbstract> constraints = new ArrayList<ConstraintAbstract>();
			RefTypeOrTPHOrWildcardOrGeneric type = ass.getTypeOfLocalVar(lvar.get_expression(), method); 
			GleichConstraint constraint = new GleichConstraint(lvar.getType(), type); 
			constraints.add(constraint); 
			return constraints; 			
		}
		// FeldVar ------------------------------------------------------------------------------------------
		else if (expression.getClass().equals(FieldVar.class)) {
			FieldVar fieldVar = (FieldVar) expression;
			Expression receiver = fieldVar.getReceiver();
			List<ConstraintAbstract> constraints = HelpMethoden.TypeExpr(ass, receiver, method, cl); 
			
			// Hier muss der Constraint angelegt werden. Der Type receiver X besitzt ein feld mit dem Typen Y 
			// constriant(c,f,c') 
			// constraint(TypeKlasse, feldname, typdesfelde) 
			// constriant(typReveier , besitzt ein feld mit dem typen , typdesfeldes); 
			// constriant(typReceiver, name des Feldes , TypDesFeldes). 
			FieldConstraint constraint = new FieldConstraint(receiver.getType(), fieldVar.getFieldVarName() , fieldVar.getType()); 
			constraints.add(constraint); 
			return constraints;		
			
		}
		// MethodCall ------------------------------------------------------------------------------------------
		else if (expression.getClass().equals(MethodCall.class)) {
			MethodCall methodCall = (MethodCall) expression;

			// nur bei MethodCall reciever = Typ Reciver mit eigenem TPH
			Expression receiver = methodCall.get_Receiver();

			List<ConstraintAbstract> constraints; 
			if (receiver.getClass().equals(Receiver.class)) {
				Receiver r_cast = (Receiver) receiver;
				constraints = HelpMethoden.TypeExpr(ass, r_cast.get_Expression(), method, cl);
			} else {
				constraints = HelpMethoden.TypeExpr(ass, receiver, method, cl); 
			}
			
			// An dieser Stelle wird der Methoden Constraint erstellt 
			/*
			 *    
			 */
			List<MyTypeVar> argumentTypesVars = new ArrayList<>(); 
			List<RefTypeOrTPHOrWildcardOrGeneric> argumentTypes = new ArrayList<>(); 
			Integer index_arg = 1; 
			for (Expression arg : methodCall.get_ArgList().getArguments()) {
				argumentTypesVars.add(new MyTypeVar(methodCall.get_Name(), index_arg , receiver.getType())); 
				index_arg = index_arg +1; 
				constraints.addAll(HelpMethoden.TypeExpr(ass, arg, method, cl)); 
				argumentTypes.add(arg.getType()); 
			}
			
			/*
			// Es wird der Typ des übergabeparameter erstellt
			List<SubTypeConstraint> puffer = new ArrayList<>();  
			for (RefTypeOrTPHOrWildcardOrGeneric type : argumentTypesVars) {
				for (ConstraintAbstract c : constraints) {
					if (c.getClass().equals(GleichConstraint.class)) {
						GleichConstraint gc = (GleichConstraint) c; 
						if (gc.getA().equals(type)) {
							argumentTypes.add(gc.getB()); 
							
							// subtype
							puffer.add(new SubTypeConstraint(gc.getB() , gc.getA() )); 							
						}
					}
				}
			}
			
			
			constraints.addAll(puffer); 
			*/		
			
			MethodConstraint methodConstraint = new MethodConstraint(receiver.getType(), methodCall.get_Name(), argumentTypes, methodCall.getType(), argumentTypesVars );
			constraints.add(methodConstraint); 
			return constraints; 
			

			
		} else {
			System.err.println("Fehler");
			return null;
		}

	}

}