/**
 * 
 */
package de.dhbwstuttgart.bytecode.genericsGenerator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

import de.dhbwstuttgart.bytecode.TPHExtractor;
import de.dhbwstuttgart.bytecode.genericsGeneratorTypes.GenericGenratorResultForSourceFile;
import de.dhbwstuttgart.bytecode.genericsGeneratorTypes.GenericsGeneratorResultForClass;
import de.dhbwstuttgart.bytecode.genericsGeneratorTypes.MethodAndConstraints;
import de.dhbwstuttgart.bytecode.utilities.MethodAndTPH;
import de.dhbwstuttgart.bytecode.utilities.MethodUtility;
import de.dhbwstuttgart.bytecode.utilities.Resolver;
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal;
import de.dhbwstuttgart.syntaxtree.ASTVisitor;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Constructor;
import de.dhbwstuttgart.syntaxtree.Field;
import de.dhbwstuttgart.syntaxtree.FormalParameter;
import de.dhbwstuttgart.syntaxtree.GenericDeclarationList;
import de.dhbwstuttgart.syntaxtree.GenericTypeVar;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.ParameterList;
import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.syntaxtree.statement.ArgumentList;
import de.dhbwstuttgart.syntaxtree.statement.Assign;
import de.dhbwstuttgart.syntaxtree.statement.AssignToField;
import de.dhbwstuttgart.syntaxtree.statement.BinaryExpr;
import de.dhbwstuttgart.syntaxtree.statement.Block;
import de.dhbwstuttgart.syntaxtree.statement.CastExpr;
import de.dhbwstuttgart.syntaxtree.statement.DoStmt;
import de.dhbwstuttgart.syntaxtree.statement.EmptyStmt;
import de.dhbwstuttgart.syntaxtree.statement.ExpressionReceiver;
import de.dhbwstuttgart.syntaxtree.statement.FieldVar;
import de.dhbwstuttgart.syntaxtree.statement.ForStmt;
import de.dhbwstuttgart.syntaxtree.statement.IfStmt;
import de.dhbwstuttgart.syntaxtree.statement.InstanceOf;
import de.dhbwstuttgart.syntaxtree.statement.LambdaExpression;
import de.dhbwstuttgart.syntaxtree.statement.Literal;
import de.dhbwstuttgart.syntaxtree.statement.LocalVar;
import de.dhbwstuttgart.syntaxtree.statement.LocalVarDecl;
import de.dhbwstuttgart.syntaxtree.statement.MethodCall;
import de.dhbwstuttgart.syntaxtree.statement.NewArray;
import de.dhbwstuttgart.syntaxtree.statement.NewClass;
import de.dhbwstuttgart.syntaxtree.statement.Return;
import de.dhbwstuttgart.syntaxtree.statement.ReturnVoid;
import de.dhbwstuttgart.syntaxtree.statement.StaticClassName;
import de.dhbwstuttgart.syntaxtree.statement.Super;
import de.dhbwstuttgart.syntaxtree.statement.SuperCall;
import de.dhbwstuttgart.syntaxtree.statement.This;
import de.dhbwstuttgart.syntaxtree.statement.UnaryExpr;
import de.dhbwstuttgart.syntaxtree.statement.WhileStmt;
import de.dhbwstuttgart.syntaxtree.type.ExtendsWildcardType;
import de.dhbwstuttgart.syntaxtree.type.GenericRefType;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.SuperWildcardType;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.typeinference.result.ResultSet;

/**
 * @author fayez
 *
 */
public class GeneratedGenericsFinder implements ASTVisitor {
	private final TPHExtractor tphExtractor = new TPHExtractor();
	private Collection<ResultSet> listOfResultSets;
	private SourceFile sf;
	private List<String> tphsClass;
	private GenericGenratorResultForSourceFile generatedGenericsForSF;
	private ResultSet resultSet;
	private final List<String> methodNameAndParamsT = new ArrayList<>();
	
	private String pkgName;
	private String className;
	private Resolver resolver;

	/**
	 * @param sf
	 * @param listOfResultSets
	 */
	public GeneratedGenericsFinder(SourceFile sf, Collection<ResultSet> listOfResultSets) {
		this.sf = sf;
		this.listOfResultSets = listOfResultSets;
	}

	public GenericGenratorResultForSourceFile findGeneratedGenerics() {
		sf.accept(this);
		return generatedGenericsForSF;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.
	 * SourceFile)
	 */
	@Override
	public void visit(SourceFile sourceFile) {
		pkgName = sf.getPkgName();
		generatedGenericsForSF = new GenericGenratorResultForSourceFile(pkgName);
		for (ClassOrInterface cl : sourceFile.getClasses()) {
			cl.accept(this);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.
	 * ClassOrInterface)
	 */
	@Override
	public void visit(ClassOrInterface classOrInterface) {
		className = classOrInterface.getClassName().toString();
		List<ResultSet> listOfResultSetsList = new ArrayList<>(listOfResultSets);
		
		boolean isVisited = false;
		
		ConstraintsSimplierResult simplifiedConstraints = null;
		GenericsGeneratorResultForClass ggResult = null;
		
		for (int i = 0; i < listOfResultSetsList.size(); i++) {
			resultSet = listOfResultSetsList.get(i);
			tphExtractor.setResultSet(resultSet);
			resolver = new Resolver(resultSet);
			classOrInterface.accept(tphExtractor);
			tphsClass = tphExtractor.tphsClass;
			simplifiedConstraints = GenericsGenerator.simplifyConstraints(tphExtractor, tphsClass);
			if(!isVisited) {
				ggResult = GenericsGenerator.generateConstraints(className, tphExtractor, tphsClass,simplifiedConstraints);
				isVisited = true;
			}

			for(Method m : classOrInterface.getMethods()) {
				addMethodConstraints(simplifiedConstraints, ggResult, m);
				
			}
			
			if(ggResult != null)
				generatedGenericsForSF.addGenericGeneratorResultClass(ggResult);
			
			
		}

	}

	/**
	 * @param simplifiedConstraints
	 * @param ggResult
	 * @param m
	 */
	public void addMethodConstraints(ConstraintsSimplierResult simplifiedConstraints,
			GenericsGeneratorResultForClass ggResult, Method m) {
		String id = MethodUtility.createID(resolver, m);
		boolean isInGGResult = (ggResult != null) && ggResult.contains(id);
		if(methodNameAndParamsT.contains(id) || isInGGResult)
			return;
		methodNameAndParamsT.add(id);
		Optional<MethodAndTPH> methodAndTPH = GenericsGeneratorUtility.getMethodAndTphs(tphExtractor.ListOfMethodsAndTph,id);
		methodAndTPH.ifPresent(mt->{
			MethodAndConstraints methodConstraints = GenericsGenerator.generateConstraintsForMethod(tphExtractor.allCons, mt , simplifiedConstraints, tphsClass);
			ggResult.getMethodsAndTheirConstraints().getMethodsAndConstraints().add(methodConstraints);
		});
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.
	 * Method)
	 */
	@Override
	public void visit(Method method) {
		
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.
	 * ParameterList)
	 */
	@Override
	public void visit(ParameterList formalParameters) {
		
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.
	 * Constructor)
	 */
	@Override
	public void visit(Constructor field) {
		
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.ArgumentList)
	 */
	@Override
	public void visit(ArgumentList argumentList) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.LambdaExpression)
	 */
	@Override
	public void visit(LambdaExpression lambdaExpression) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.Assign)
	 */
	@Override
	public void visit(Assign assign) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.BinaryExpr)
	 */
	@Override
	public void visit(BinaryExpr binary) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.Block)
	 */
	@Override
	public void visit(Block block) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.CastExpr)
	 */
	@Override
	public void visit(CastExpr castExpr) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.EmptyStmt)
	 */
	@Override
	public void visit(EmptyStmt emptyStmt) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.FieldVar)
	 */
	@Override
	public void visit(FieldVar fieldVar) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.ForStmt)
	 */
	@Override
	public void visit(ForStmt forStmt) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.IfStmt)
	 */
	@Override
	public void visit(IfStmt ifStmt) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.InstanceOf)
	 */
	@Override
	public void visit(InstanceOf instanceOf) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.LocalVar)
	 */
	@Override
	public void visit(LocalVar localVar) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.LocalVarDecl)
	 */
	@Override
	public void visit(LocalVarDecl localVarDecl) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.MethodCall)
	 */
	@Override
	public void visit(MethodCall methodCall) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.NewClass)
	 */
	@Override
	public void visit(NewClass methodCall) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.NewArray)
	 */
	@Override
	public void visit(NewArray newArray) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.Return)
	 */
	@Override
	public void visit(Return aReturn) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.ReturnVoid)
	 */
	@Override
	public void visit(ReturnVoid aReturn) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.StaticClassName)
	 */
	@Override
	public void visit(StaticClassName staticClassName) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.Super)
	 */
	@Override
	public void visit(Super aSuper) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.This)
	 */
	@Override
	public void visit(This aThis) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.WhileStmt)
	 */
	@Override
	public void visit(WhileStmt whileStmt) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.DoStmt)
	 */
	@Override
	public void visit(DoStmt whileStmt) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.AssignToField)
	 */
	@Override
	public void visit(AssignToField assignLeftSide) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.parser.
	 * SyntaxTreeGenerator.AssignToLocal)
	 */
	@Override
	public void visit(AssignToLocal assignLeftSide) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.SuperCall)
	 */
	@Override
	public void visit(SuperCall superCall) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.ExpressionReceiver)
	 */
	@Override
	public void visit(ExpressionReceiver expressionReceiver) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.UnaryExpr)
	 */
	@Override
	public void visit(UnaryExpr unaryExpr) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see de.dhbwstuttgart.syntaxtree.StatementVisitor#visit(de.dhbwstuttgart.
	 * syntaxtree.statement.Literal)
	 */
	@Override
	public void visit(Literal literal) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.
	 * GenericTypeVar)
	 */
	@Override
	public void visit(GenericTypeVar genericTypeVar) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.
	 * FormalParameter)
	 */
	@Override
	public void visit(FormalParameter formalParameter) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.
	 * GenericDeclarationList)
	 */
	@Override
	public void visit(GenericDeclarationList genericTypeVars) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.
	 * Field)
	 */
	@Override
	public void visit(Field field) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.type
	 * .RefType)
	 */
	@Override
	public void visit(RefType refType) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.type
	 * .SuperWildcardType)
	 */
	@Override
	public void visit(SuperWildcardType superWildcardType) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.type
	 * .TypePlaceholder)
	 */
	@Override
	public void visit(TypePlaceholder typePlaceholder) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.type
	 * .ExtendsWildcardType)
	 */
	@Override
	public void visit(ExtendsWildcardType extendsWildcardType) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * de.dhbwstuttgart.syntaxtree.ASTVisitor#visit(de.dhbwstuttgart.syntaxtree.type
	 * .GenericRefType)
	 */
	@Override
	public void visit(GenericRefType genericRefType) {
		// TODO Auto-generated method stub

	}

}
