package codechicken.lib.asm.proxy;

import codechicken.lib.util.ReflectionManager;
import com.google.common.base.Throwables;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.LoaderException;
import net.minecraftforge.fml.common.ModClassLoader;
import net.minecraftforge.fml.common.discovery.ASMDataTable;
import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Set;

/**
 * Created by covers1624 on 7/04/2017.
 */
public class ProxyInjector {

    private static Logger logger = LogManager.getLogger("CodeChickenLib Proxy Injection");

    public static void runInjector(ASMDataTable table) {

        Set<ASMData> targets = table.getAll(FunctionProxy.class.getName());
        ModClassLoader classLoader = Loader.instance().getModClassLoader();

        for (ASMData data : targets) {
            try {
                Class<?> proxyTarget = Class.forName(data.getClassName(), true, classLoader);
                Field target = proxyTarget.getDeclaredField(data.getObjectName());
                if (target == null) {
                    throw new RuntimeException(String.format("Attempted to load proxy into field %s.%s but field does not exist..", data.getClassName(), data.getObjectName()));
                }
                target.setAccessible(true);
                FunctionProxy annotation = target.getAnnotation(FunctionProxy.class);
                String targetClass = getInjectClass(annotation.injectClassCallback(), data, classLoader);
                Object proxyInstance = Class.forName(targetClass, true, classLoader).newInstance();

                if (!target.getType().isAssignableFrom(proxyInstance.getClass())) {
                    throw new RuntimeException(String.format("Attempted to load incompatible class type to proxy field!, Field Type: %s, Inject Type %s, Field %s.%s", target.getType().getName(), targetClass, data.getClassName(), data.getObjectName()));
                }
                target.set(null, proxyInstance);
                logger.debug("Successfully injected proxy to field %s.%s", data.getClassName(), data.getObjectName());

            } catch (Exception e) {
                logger.fatal("Fatal Exception thrown whilst loading proxy for class %s.%s", e, data.getClassName(), data.getObjectName());
                Throwables.propagate(e);
            }
        }
    }

    private static String getInjectClass(String test, ASMData data, ClassLoader classLoader) throws Exception {
        String className;
        String methodName;
        if (!test.equals("")) {
            int index = test.lastIndexOf('.');
            className = test.substring(0, index);
            methodName = test.substring(index + 1);

        } else {
            className = data.getClassName();
            methodName = "proxyCallback";
        }
        Class<?> clazz = Class.forName(className, true, classLoader);
        for (Method method : clazz.getMethods()) {
            if (method.getName().equals(methodName) && method.getReturnType().equals(String.class) && ReflectionManager.isStatic(method)) {
                method.setAccessible(true);
                return (String) method.invoke(null);
            }
        }
        throw new RuntimeException(String.format("Unable to find static method with string return type! Method: %s.%s", className, methodName));
    }

}

@@ -129,44 +129,38 @@
 ort 
-net.minecraftforge.fml.common.Loader
+com.google.common.collect.Sets
 ;%0Aim
@@ -200,25 +200,16 @@
 n.Loader
-Exception
 ;%0Aimport
@@ -385,16 +385,250 @@
 SMData;%0A
+import net.minecraftforge.fml.common.discovery.asm.ModAnnotation.EnumHolder;%0Aimport net.minecraftforge.fml.relauncher.FMLLaunchHandler;%0Aimport net.minecraftforge.fml.relauncher.Side;%0Aimport net.minecraftforge.fml.relauncher.SideOnly;%0A
 import o
@@ -769,16 +769,44 @@
 Method;%0A
+import java.util.Map.Entry;%0A
 import j
@@ -1043,24 +1043,74 @@
 e table) %7B%0A%0A
+        Side loadSide = FMLLaunchHandler.side();%0A%0A
         Set%3C
@@ -1165,32 +1165,1211 @@
 ass.getName());%0A
+        Set%3CASMData%3E sidedClasses = table.getAll(SideOnly.class.getName());%0A        Set%3CASMData%3E filteredTargets = Sets.filter(targets, input -%3E %7B%0A            for (ASMData data : sidedClasses) %7B%0A                //Narrow down to our class.%0A                if (data.getClassName().equals(input.getClassName())) %7B%0A                    //If the SideOnly annotation is on the class or the object we are attached to.%0A                    if (data.getObjectName().equalsIgnoreCase(input.getClassName()) %7C%7C data.getObjectName().equalsIgnoreCase(input.getObjectName())) %7B%0A                        for (Entry%3CString, Object%3E entry : data.getAnnotationInfo().entrySet()) %7B%0A                            if (entry.getKey().equals(%22value%22) && entry.getValue() instanceof EnumHolder) %7B%0A                                EnumHolder holder = ((EnumHolder) entry.getValue());%0A                                if (!loadSide.toString().equalsIgnoreCase(holder.getValue())) %7B%0A                                    return false;%0A                                %7D%0A                            %7D%0A                        %7D%0A                    %7D%0A                %7D%0A            %7D%0A            return true;%0A        %7D);%0A
         ModClass
@@ -2457,17 +2457,25 @@
  data : 
-t
+filteredT
 argets) 

Fix FunctionProxy injection not obeying SideOnly.