package de.dhbwstuttgart.typeinference.assumptions;

import de.dhbwstuttgart.exceptions.NotImplementedException;
import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Field;
import de.dhbwstuttgart.syntaxtree.FormalParameter;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.statement.ArgumentList;
import de.dhbwstuttgart.syntaxtree.type.GenericRefType;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;

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

/*
Anmerkung:
Zwei Möglichkeiten, die TypeAssumptions zu speichern.
1. Die Klassen mit bedeutenden Informationen generieren diese und übergeben sie dann dieser Klasse.
2. Oder es werden nur Referenzen auf den SyntaxTree übergeben, welche diese Klasse hier dann verarbeitet.

Bei Änderungen des SyntaxTrees müssen beide Methoden angepasst werden.
Zweiteres hat den Vorteil, dass bei der Entwicklung leichter Dinge hinzugefügt werden können.
Die ganze Logik steckt in dieser Klasse.
 */
public class TypeInferenceInformation {
    private Set<ClassOrInterface> classes;

    public TypeInferenceInformation(Set<ClassOrInterface> availableClasses){
        classes = availableClasses;
    }

    private RefTypeOrTPHOrWildcardOrGeneric checkGTV(RefTypeOrTPHOrWildcardOrGeneric type){
        if(type instanceof GenericRefType){
            return TypePlaceholder.fresh(new NullToken());
        }else{
            return type;
        }
    }

    public List<MethodAssumption> getMethods(String name, ArgumentList arglist) {
        List<MethodAssumption> ret = new ArrayList<>();
        for(ClassOrInterface cl : classes){
            for(Method m : cl.getMethods()){
                if(m.getName().equals(name) &&
                        m.getParameterList().getFormalparalist().size() == arglist.getArguments().size()){
                    RefTypeOrTPHOrWildcardOrGeneric retType = checkGTV(m.getType());

                    List<RefTypeOrTPHOrWildcardOrGeneric> params = new ArrayList<>();
                    for(FormalParameter fp : m.getParameterList().getFormalparalist()){
                        params.add(checkGTV(fp.getType()));
                    }
                    ret.add(new MethodAssumption(cl.getType(), retType, params));
                }
            }
        }
        return ret;
    }

    public List<FieldAssumption> getFields(String name){
        List<FieldAssumption> ret = new ArrayList<>();
        for(ClassOrInterface cl : classes){
            for(Field m : cl.getFieldDecl()){
                if(m.getName().equals(name)){
                    ret.add(new FieldAssumption(cl.getType(), checkGTV(m.getType())));
                }
            }
        }
        return ret;
    }

    public Set<ClassOrInterface> getAvailableClasses() {
        return classes;
    }

}
