package de.dhbwstuttgart.target.generate;

import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.target.tree.type.TargetType;

import java.util.*;

public class GenericsResult {
    private final IGenerics generics;

    GenericsResult(IGenerics generics) {
        this.generics = generics;
    }

    public IGenerics getGenerics() {
        return this.generics;
    }

    public GenericsResultSet get(ClassOrInterface clazz) {
        var generics = this.generics.generics(clazz);
        return new GenericsResultSet(generics, this.generics);
    }

    public GenericsResultSet get(ClassOrInterface clazz, Method method) {
        var generics = this.generics.generics(clazz, method);
        return new GenericsResultSet(generics, this.generics);
    }

    public BoundsList getBounds(RefTypeOrTPHOrWildcardOrGeneric type, ClassOrInterface clazz) {
        return getBounds(type, clazz, null);
    }

    public BoundsList getBounds(RefTypeOrTPHOrWildcardOrGeneric type, ClassOrInterface clazz, Method method) {
        var resolvedType = resolve(type);
        type = resolvedType;
        if (type instanceof TypePlaceholder) {
            var methodGenerics = get(clazz, method);
            var classGenerics = get(clazz);
            List<Bound> result = new ArrayList<>();

            Optional<Bound> bound = Optional.empty();
            do {
                bound = Optional.empty();
                for (var pair : methodGenerics) {
                    if (pair.left.resolve().equals(type)) {
                        type = pair.resolveRight();
                        bound = Optional.of(new Bound(true, type));
                        break;
                    }
                }
                if (bound.isEmpty()) {
                    for (var pair : classGenerics) {
                        if (pair.left.resolve().equals(type)) {
                            type = pair.resolveRight();
                            bound = Optional.of(new Bound(false, type));
                            break;
                        }
                    }
                }
                bound.ifPresent(result::add);
            } while (bound.isPresent());
            return new BoundsList(resolvedType, result);
        }
        return new BoundsList(resolvedType, List.of());
    }

    public RefTypeOrTPHOrWildcardOrGeneric resolve(RefTypeOrTPHOrWildcardOrGeneric type) {
        if (type instanceof TypePlaceholder tph)
            return this.generics.getType(tph);
        return type;
    }
}
