package de.dhbwstuttgart.typeinference.typeAlgo;

import de.dhbwstuttgart.exceptions.DebugException;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.syntaxtree.factory.ASTFactory;
import de.dhbwstuttgart.syntaxtree.statement.Statement;
import de.dhbwstuttgart.typeinference.assumptions.TypeInferenceBlockInformation;
import de.dhbwstuttgart.typeinference.assumptions.TypeInferenceInformation;
import de.dhbwstuttgart.typeinference.constraints.ConstraintSet;
import java.util.*;

public class TYPE {

    private final Collection<SourceFile> sfs;
    private final TypeInferenceInformation typeInferenceInformation;

    public TYPE(Collection<SourceFile> sourceFiles, Collection<ClassOrInterface> allAvailableClasses){
        sfs = sourceFiles;
        this.typeInferenceInformation = new TypeInferenceInformation(allAvailableClasses);
    }


    public ConstraintSet getConstraints() {
        ConstraintSet ret = new ConstraintSet();
        for(SourceFile sf : sfs)
        for (ClassOrInterface cl : sf.KlassenVektor) {
            ret.addAll(getConstraintsClass(cl ,typeInferenceInformation));
        }
        return ret;
    }

    private ConstraintSet getConstraintsClass(ClassOrInterface cl, TypeInferenceInformation info) {
        ConstraintSet ret = new ConstraintSet();
        for(Method m : cl.getMethods()){
            ret.addAll(getConstraintsMethod(m,info, cl));
        }
        for(Constructor m : cl.getConstructors()){
            ret.addAll(getConstraintsConstructor(m,info, cl));
        }
        if (cl.getfieldInitializations().isPresent()) {
        	ret.addAll(getConstraintsConstructor(cl.getfieldInitializations().get(), info, cl));       	
        }
        return ret;
    }
    /*
    TODO: Hier eine Information erstellen nur mit den importierte Klassen einer einzigen SourceFile
        private TypeInferenceInformation getTypeInferenceInformation(sourceFile) {
            ClassLoader classLoader = ClassLoader.getSystemClassLoader();
            Set<ClassOrInterface> classes = new HashSet<>();

            for(SourceFile sourceFile : sfs){
                for(JavaClassName importName : sourceFile.imports){
                    System.out.println(importName);
                    try {
                        classes.add(ASTFactory.createClass(classLoader.loadClass(importName.toString())));
                    } catch (ClassNotFoundException e) {
                        throw new DebugException("Klasse " + importName + " konnte nicht geladen werden");
                    }
                }
                classes.addAll(sourceFile.KlassenVektor);
            }

            return new TypeInferenceInformation(classes);
        }
    */
 
    private ConstraintSet getConstraintsMethod(Method m, TypeInferenceInformation info, ClassOrInterface currentClass) {
        if(m.block == null)return new ConstraintSet(); //Abstrakte Methoden generieren keine Constraints
        TypeInferenceBlockInformation blockInfo = new TypeInferenceBlockInformation(info.getAvailableClasses(), currentClass, m);
        TYPEStmt methodScope = new TYPEStmt(blockInfo);
        m.block.accept(methodScope);
        return methodScope.getConstraints();
    }

    private ConstraintSet getConstraintsConstructor(Constructor m, TypeInferenceInformation info, ClassOrInterface currentClass) {
        TypeInferenceBlockInformation blockInfo = new TypeInferenceBlockInformation(info.getAvailableClasses(), currentClass, m);
        TYPEStmt methodScope = new TYPEStmt(blockInfo);
        //for(Statement stmt : m.fieldInitializations)stmt.accept(methodScope);
        ConstraintSet ret = this.getConstraintsMethod(m, info, currentClass);
        ret.addAll(methodScope.getConstraints());
        return ret;
    }
}
