package net.md_5.specialsource;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.Type;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class ReflectionRemapper {

    private JarMapping jarMapping;
    private IInheritanceProvider inheritanceProvider;

    public ReflectionRemapper(JarMapping jarMapping, IInheritanceProvider inheritanceProvider) {
        this.jarMapping = jarMapping;
        this.inheritanceProvider = inheritanceProvider;
    }

    @SuppressWarnings("unchecked")
    public byte[] remapClassFile(InputStream is) throws IOException {
        ClassReader cr = new ClassReader(is);
        ClassNode classNode = new ClassNode();
        ClassWriter cw = new ClassWriter(0);

        cr.accept(classNode, ClassReader.SKIP_DEBUG);

        for (MethodNode methodNode : (List<MethodNode>) classNode.methods) {
            AbstractInsnNode insn = methodNode.instructions.getFirst();
            while (insn != null) {
                if (insn.getOpcode() == Opcodes.INVOKEVIRTUAL) {
                    remapGetDeclaredField(insn);
                }

                insn = insn.getNext();
            }
        }

        classNode.accept(cw);

        return cw.toByteArray();
    }

    private void remapGetDeclaredField(AbstractInsnNode insn) {
        MethodInsnNode mi = (MethodInsnNode) insn;

        System.out.println("methodnode "+mi.owner+" "+mi.name+" "+mi.desc);
        if (!mi.owner.equals("java/lang/Class") || !mi.name.equals("getDeclaredField") || !mi.desc.equals("(Ljava/lang/String;)Ljava/lang/reflect/Field;")) {
            return;
        }

        System.out.println("found getDeclaredField!");

        if (insn.getPrevious() == null || insn.getPrevious().getOpcode() != Opcodes.LDC) {
            System.out.println("- not constant field; skipping");
            return;
        }
        LdcInsnNode ldcField = (LdcInsnNode) insn.getPrevious();
        System.out.println("field constant = "+ldcField.cst);
        if (!(ldcField.cst instanceof String)) {
            System.out.println("- not field string; skipping");
            return;
        }
        String fieldName = (String) ldcField.cst;

        if (ldcField.getPrevious() == null || ldcField.getPrevious().getOpcode() != Opcodes.LDC) {
            System.out.println("- not constant class; skipping");
            return;
        }
        LdcInsnNode ldcClass = (LdcInsnNode) ldcField.getPrevious();
        if (!(ldcClass.cst instanceof Type)) {
            System.out.println("- not class type; skipping");
            return;
        }
        System.out.println("class = "+ldcClass.cst);
        String className = ((Type) ldcClass.cst).getInternalName();

        System.out.println("Remapping "+className+"/"+fieldName);

        String newName = jarMapping.fields.get(className + "/" + fieldName); // TODO: tryClimb etc
        if (newName != null) {
            // Change the string literal to the correct name
            ldcField.cst = newName;
            //ldcClass.cst = className; // not remapped here - taken care of by JarRemapper
            System.out.println(" -> to "+newName);
        }
    }
}

@@ -355,252 +355,100 @@
 ng;%0A
-    private IInheritanceProvider inheritanceProvider;%0A%0A    public ReflectionRemapper(JarMapping jarMapping, IInheritanceProvider inheritanceProvider) %7B%0A        this.jarMapping = jarMapping;%0A        this.inheritanceProvider = inheritanceProvider
+%0A    public ReflectionRemapper(JarMapping jarMapping) %7B%0A        this.jarMapping = jarMapping
 ;%0A  
@@ -2780,19 +2780,52 @@
 ing.
-fields.get(
+tryClimb(jarMapping.fields, NodeType.FIELD, 
 clas
@@ -2833,16 +2833,9 @@
 Name
- + %22/%22 +
+,
  fie
@@ -2846,30 +2846,8 @@
 me);
- // TODO: tryClimb etc
 %0A   

Use tryClimb in ReflectionRemapper