package de.dhbwstuttgart.target.tree;

import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.target.generate.IGenerics;
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
import de.dhbwstuttgart.target.tree.expression.TargetPattern;
import de.dhbwstuttgart.target.tree.type.TargetType;
import org.objectweb.asm.Opcodes;

import java.util.List;
import java.util.Objects;
import java.util.Set;

public record TargetMethod(int access, String name, TargetBlock block, Signature signature, Signature txSignature, Method base, IGenerics generics) {
    public TargetMethod(int access, String name, TargetBlock block, Signature signature, Signature txSignature) {
        this(access, name, block, signature, txSignature, null, null);
    }

    public record Signature(Set<TargetGeneric> generics, List<MethodParameter> parameters, TargetType returnType) {
        public String getSignature() {
            return TargetMethod.getSignature(generics, parameters, returnType);
        }

        public String getDescriptor() {
            return TargetMethod.getDescriptor(returnType, parameters.stream().map(MethodParameter::pattern).map(TargetPattern::type).toArray(TargetType[]::new));
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Signature signature = (Signature) o;
            return Objects.equals(parameters, signature.parameters);
        }

        @Override
        public int hashCode() {
            return Objects.hash(parameters);
        }
    }

    public static String getDescriptor(TargetType returnType, TargetType... parameters) {
        String ret = "(";
        for (var parameterType : parameters) {
            ret += parameterType.toDescriptor();
        }
        ret += ")";
        if (returnType == null) ret += "V";
        else ret += returnType.toDescriptor();
        return ret;
    }

    public static String getSignature(Set<TargetGeneric> generics, List<MethodParameter> parameters, TargetType returnType) {
        String ret = "";
        if (!generics.isEmpty()) {
            ret += "<";
            for (var generic : generics) {
                ret += generic.name() + ":" + generic.bound().toSignature();
            }
            ret += ">";
        }
        ret += "(";
        for (var param : parameters) {
            ret += param.pattern().type().toSignature();
        }
        ret += ")";
        if (returnType == null) ret += "V";
        else ret += returnType.toSignature();
        return ret;
    }

    public String getDescriptor() {
        return getDescriptor(signature.returnType, signature.parameters.stream().map(mp -> mp.pattern().type()).toArray(TargetType[]::new));
    }

    public String getSignature() {
        return getSignature(signature.generics, signature.parameters, signature.returnType);
    }

    public String getTXSignature() {
        return getSignature(txSignature.generics, txSignature.parameters, txSignature.returnType);
    }

    public boolean isStatic() {
        return (access & Opcodes.ACC_STATIC) != 0;
    }

    public boolean isPrivate() {
        return (access & Opcodes.ACC_PRIVATE) != 0;
    }

    @Override
    public boolean equals(Object other) {
        if (!(other instanceof TargetMethod otherMethod)) return false;
        return otherMethod.signature.equals(this.signature) && otherMethod.name.equals(this.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, signature);
    }
}

