package codechicken.lib.asm;

import codechicken.lib.config.ConfigFile;
import codechicken.lib.config.DefaultingConfigFile;
import cpw.mods.fml.relauncher.FMLInjectionData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.util.TraceClassVisitor;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ASMHelper
{
    public static ConfigFile config = loadConfig();
    public static Logger logger = LogManager.getLogger("CCL ASM");

    private static ConfigFile loadConfig() {
        File file = new File((File) FMLInjectionData.data()[6], "config/CodeChickenLib.cfg");
        if(ObfMapping.obfuscated)
            return new DefaultingConfigFile(file);
        else
            return new ConfigFile(file).setComment("CodeChickenLib development configuration file.");
    }

    public static interface Acceptor
    {
        public void accept(ClassVisitor cv) throws IOException;
    }

    public static MethodNode findMethod(ObfMapping methodmap, ClassNode cnode) {
        for (MethodNode mnode : cnode.methods)
            if (methodmap.matches(mnode))
                return mnode;
        return null;
    }

    public static FieldNode findField(ObfMapping fieldmap, ClassNode cnode) {
        for (FieldNode fnode : cnode.fields)
            if (fieldmap.matches(fnode))
                return fnode;
        return null;
    }

    public static ClassNode createClassNode(byte[] bytes) {
        return createClassNode(bytes, 0);
    }

    public static ClassNode createClassNode(byte[] bytes, int flags) {
        ClassNode cnode = new ClassNode();
        ClassReader reader = new ClassReader(bytes);
        reader.accept(cnode, flags);
        return cnode;
    }

    public static byte[] createBytes(ClassNode cnode, int flags) {
        ClassWriter cw = new CC_ClassWriter(flags);
        cnode.accept(cw);
        return cw.toByteArray();
    }

    public static Map<LabelNode, LabelNode> cloneLabels(InsnList list) {
        return new InsnListSection(list).cloneLabels();
    }

    public static InsnList cloneInsnList(InsnList list) {
        return new InsnListSection(list).copy().list;
    }

    public static InsnList cloneInsnList(Map<LabelNode, LabelNode> labelMap, InsnList list) {
        return new InsnListSection(list).copy(labelMap).list;
    }

    public static List<TryCatchBlockNode> cloneTryCatchBlocks(Map<LabelNode, LabelNode> labelMap, List<TryCatchBlockNode> tcblocks) {
        ArrayList<TryCatchBlockNode> clone = new ArrayList<TryCatchBlockNode>();
        for (TryCatchBlockNode node : tcblocks)
            clone.add(new TryCatchBlockNode(
                    labelMap.get(node.start),
                    labelMap.get(node.end),
                    labelMap.get(node.handler),
                    node.type));

        return clone;
    }

    public static List<LocalVariableNode> cloneLocals(Map<LabelNode, LabelNode> labelMap, List<LocalVariableNode> locals) {
        ArrayList<LocalVariableNode> clone = new ArrayList<LocalVariableNode>(locals.size());
        for (LocalVariableNode node : locals)
            clone.add(new LocalVariableNode(
                    node.name, node.desc, node.signature,
                    labelMap.get(node.start),
                    labelMap.get(node.end),
                    node.index));

        return clone;
    }

    public static void copy(MethodNode src, MethodNode dst) {
        Map<LabelNode, LabelNode> labelMap = cloneLabels(src.instructions);
        dst.instructions = cloneInsnList(labelMap, src.instructions);
        dst.tryCatchBlocks = cloneTryCatchBlocks(labelMap, src.tryCatchBlocks);
        if (src.localVariables != null)
            dst.localVariables = cloneLocals(labelMap, src.localVariables);
        dst.visibleAnnotations = src.visibleAnnotations;
        dst.invisibleAnnotations = src.invisibleAnnotations;
        dst.visitMaxs(src.maxStack, src.maxLocals);
    }

    public static String toString(InsnList list) {
        return new InsnListSection(list).toString();
    }

    public static int getLocal(List<LocalVariableNode> list, String name) {
        int found = -1;
        for (LocalVariableNode node : list) {
            if (node.name.equals(name)) {
                if (found >= 0)
                    throw new RuntimeException("Duplicate local variable: " + name + " not coded to handle this scenario.");

                found = node.index;
            }
        }
        return found;
    }

    public static void replaceMethod(MethodNode original, MethodNode replacement) {
        original.instructions.clear();
        if (original.localVariables != null)
            original.localVariables.clear();
        if (original.tryCatchBlocks != null)
            original.tryCatchBlocks.clear();
        replacement.accept(original);
    }

    public static void dump(Acceptor acceptor, File file, boolean filterImportant, boolean sortLocals) {
        try {
            if(!file.getParentFile().exists())
                file.getParentFile().mkdirs();
            if(!file.exists())
                file.createNewFile();

            PrintWriter pout = new PrintWriter(file);
            ClassVisitor cv = new TraceClassVisitor(pout);
            if(filterImportant) cv = new ImportantInsnVisitor(cv);
            if(sortLocals) cv = new LocalVariablesSorterVisitor(cv);
            acceptor.accept(cv);
            pout.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void dump(final byte[] bytes, File file, boolean filterImportant, boolean sortLocals) {
        dump(new Acceptor()
        {
            @Override
            public void accept(ClassVisitor cv) {
                new ClassReader(bytes).accept(cv, ClassReader.EXPAND_FRAMES);
            }
        }, file, filterImportant, sortLocals);
    }

    public static void dump(final InputStream is, File file, boolean filterImportant, boolean sortLocals) {
        dump(new Acceptor()
        {
            @Override
            public void accept(ClassVisitor cv) throws IOException {
                new ClassReader(is).accept(cv, ClassReader.EXPAND_FRAMES);
            }
        }, file, filterImportant, sortLocals);
    }

    public static void dump(final ClassNode cnode, File file, boolean filterImportant, boolean sortLocals) {
        dump(new Acceptor()
        {
            @Override
            public void accept(ClassVisitor cv) {
                cnode.accept(cv);
            }
        }, file, filterImportant, sortLocals);
    }
}

@@ -121,57 +121,8 @@
 le;%0A
-import cpw.mods.fml.relauncher.FMLInjectionData;%0A
 impo
@@ -780,62 +780,233 @@
     
-File file = new File((File) FMLInjectionData.data()%5B6%5D
+try %7B//weak reference for environments without FML%0A            File mcDir = (File)((Object%5B%5D)Class.forName(%22cpw.mods.fml.relauncher.FMLInjectionData%22).getMethod(%22data%22).invoke(null))%5B6%5D;%0A            File file = new File(mcDir
 , %22c
@@ -1029,24 +1029,28 @@
 nLib.cfg%22);%0A
+    
         if(O
@@ -1075,32 +1075,36 @@
 ed)%0A            
+    
 return new Defau
@@ -1138,13 +1138,21 @@
     
+    
 else%0A
+    
     
@@ -1245,24 +1245,126 @@
 on file.%22);%0A
+        %7D catch (Exception ignored) %7B%0A            return null;//no config for these systems%0A        %7D%0A
     %7D%0A%0A    p

Fix crash with tool system when loading ASMHelper