package de.dhbwstuttgart.typedeployment;

import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.syntaxtree.statement.LambdaExpression;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.target.generate.GenericsResult;
import de.dhbwstuttgart.target.generate.GenericsResultSet;
import de.dhbwstuttgart.typeinference.result.ResultPair;
import de.dhbwstuttgart.typeinference.result.ResultSet;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class TypeInsertPlacer extends AbstractASTWalker {
    Set<TypeInsert> inserts = new HashSet<>();
    private ResultSet withResults;
    String pkgName;
    private GenericsResult genericsResult;

    public Set<TypeInsert> getTypeInserts(SourceFile forSourceFile, ResultSet withResults, GenericsResult genericsResult){
        this.withResults = withResults;
        this.genericsResult = genericsResult;
        pkgName = forSourceFile.getPkgName();
        forSourceFile.accept(this);
        return inserts;
    }

    @Override
    public void visit(ClassOrInterface classOrInterface) {
        TypeInsertPlacerClass cl = new TypeInsertPlacerClass(classOrInterface, withResults, genericsResult);
        this.inserts.addAll(cl.inserts);
    }
}

class TypeInsertPlacerClass extends AbstractASTWalker{
    protected final ResultSet results;
    private GenericsResult generatedGenerics;
    protected final ClassOrInterface cl;
    public final Set<TypeInsert> inserts = new HashSet<>();
    private Method method;

    GenericsResultSet constraints;
	GenericsResultSet classConstraints;

    TypeInsertPlacerClass(ClassOrInterface forClass, ResultSet withResults, GenericsResult generatedGenerics){
        this.cl = forClass;
        this.method = null;
        this.results = withResults;
        this.generatedGenerics = generatedGenerics;
        forClass.accept(this);
    }

    @Override
    public void visit(Method method) {
        this.method = method;
        constraints = generatedGenerics.get(cl, method);
		classConstraints = generatedGenerics.get(cl);
        if(method.getReturnType() instanceof TypePlaceholder)
            inserts.add(TypeInsertFactory.createInsertPoints(
                    method.getReturnType(), method.getReturnType().getOffset(), cl, method, results, constraints, classConstraints));
        super.visit(method);
    }

    @Override
    public void visit(Field field) {
        if(field.getType() instanceof TypePlaceholder){
        	classConstraints = generatedGenerics.get(cl);
                inserts.add(TypeInsertFactory.createInsertPoints(
                        field.getType(), field.getType().getOffset(), cl, method, results, null, classConstraints));
        }
        super.visit(field);
    }

    @Override
    public void visit(FormalParameter param) {
            if(param.getType() instanceof TypePlaceholder)
                inserts.add(TypeInsertFactory.createInsertPoints(
                        param.getType(), param.getType().getOffset(), cl, method, results, constraints, classConstraints));
        super.visit(param);
    }

    @Override
    public void visit(LambdaExpression lambdaExpression) {
        //Lambda-Ausdrücke brauchen keine Typeinsetzungen
    }
}
