package openmods.depcheck;

import java.util.Optional;

import openmods.depcheck.TargetParser.TargetClassVisitor;
import openmods.depcheck.utils.Field;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.Method;

public class TargetClassBytecodeVisitor extends ClassVisitor {

    private class MethodDependencyVisitor extends MethodVisitor {

        public MethodDependencyVisitor() {
            super(Opcodes.ASM5);
        }

        @Override
        public void visitTypeInsn(int opcode, String type) {
            String clsName = type.replace('/', '.');
            visitor.visitRequiredClass(clsName);
        }

        @Override
        public void visitMultiANewArrayInsn(String desc, int dims) {
            addClsIfNeeded(Type.getType(desc));
        }

        @Override
        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
            visitor.visitRequiredField(owner.replace('/', '.'), new Field(name, desc));
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
            visitor.visitRequiredMethod(owner.replace('/', '.'), new Method(name, desc));
        }

    }

    private final TargetClassVisitor visitor;

    public TargetClassBytecodeVisitor(TargetClassVisitor visitor) {
        super(Opcodes.ASM5);
        this.visitor = visitor;
    }

    private static Optional<String> extractReferenceType(Type type) {
        if (type.getSort() == Type.OBJECT) {
            return Optional.of(type.getClassName());
        } else if (type.getSort() == Type.ARRAY)
            return extractReferenceType(type.getElementType());
        else {
            return Optional.empty();
        }
    }

    private void addClsIfNeeded(final Type type) {
        final Optional<String> refType = extractReferenceType(type);
        refType.ifPresent(visitor::visitRequiredClass);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        for (String intf : interfaces)
            visitor.visitRequiredClass(intf.replace('/', '.'));

        visitor.visitRequiredClass(superName.replace('/', '.'));
    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        addClsIfNeeded(Type.getType(desc));
        return null;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        final Type type = Type.getMethodType(desc);
        addClsIfNeeded(type.getReturnType());

        for (Type argType : type.getArgumentTypes())
            addClsIfNeeded(argType);

        return new MethodDependencyVisitor();
    }

}

@@ -38,24 +38,33 @@
 va.util.
-Optional
+function.Consumer
 ;%0A%0Aimpor
@@ -435,32 +435,183 @@
 M5);%0A        %7D%0A%0A
+        @Override%0A        public void visitLdcInsn(Object cst) %7B%0A            if (cst instanceof Type)%0A                visitType((Type)cst);%0A        %7D%0A%0A
         @Overrid
@@ -881,38 +881,33 @@
             
-addClsIfNeeded
+visitType
 (Type.getTyp
@@ -1073,38 +1073,36 @@
 edField(
-owner.replace('/', '.'
+internalToJava(owner
 ), new F
@@ -1296,30 +1296,28 @@
 hod(
-owner.replace('/', '.'
+internalToJava(owner
 ), n
@@ -1564,55 +1564,174 @@
 tic 
-Optional%3CString%3E extractReferenceType(Type type
+String internalToJava(String cls) %7B%0A        return cls.replace('/', '.');%0A    %7D%0A%0A    private static void extractReferenceType(Type type, Consumer%3CString%3E typeConsumer
 ) %7B%0A
@@ -1791,26 +1791,27 @@
     
-return Optional.of
+typeConsumer.accept
 (typ
@@ -1890,23 +1890,16 @@
         
-return 
 extractR
@@ -1932,16 +1932,30 @@
 ntType()
+, typeConsumer
 );%0A     
@@ -1966,210 +1966,326 @@
 lse 
-%7B%0A            return Optional.empty();%0A        %7D%0A    %7D%0A%0A    private void addClsIfNeeded(final Type type) %7B%0A        final Optional%3CString%3E refType = extractReferenceType(type);%0A        refType.ifPresent(
+if (type.getSort() == Type.METHOD) %7B%0A            extractReferenceType(type.getReturnType(), typeConsumer);%0A%0A            for (Type argType : type.getArgumentTypes())%0A                extractReferenceType(argType, typeConsumer);%0A        %7D%0A    %7D%0A%0A    private void visitType(Type type) %7B%0A        extractReferenceType(type, 
 visi
@@ -2535,26 +2535,24 @@
 (int
-f.replace('/', '.'
+ernalToJava(intf
 ));%0A
@@ -2591,34 +2591,32 @@
 ass(
-superName.replace('/', '.'
+internalToJava(superName
 ));%0A
@@ -2751,30 +2751,25 @@
         
-addClsIfNeeded
+visitType
 (Type.ge
@@ -2952,26 +2952,18 @@
     
-final Type type = 
+visitType(
 Type
@@ -2986,147 +2986,10 @@
 esc)
+)
 ;
-%0A        addClsIfNeeded(type.getReturnType());%0A%0A        for (Type argType : type.getArgumentTypes())%0A            addClsIfNeeded(argType);%0A
 %0A   

Include LDC instruction in scans