package sourcecoded.core.configuration.gui;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.client.IModGuiFactory;
import cpw.mods.fml.client.config.IConfigElement;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.relauncher.ReflectionHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraftforge.common.config.ConfigElement;
import net.minecraftforge.common.util.EnumHelper;
import sourcecoded.core.Constants;
import sourcecoded.core.SourceCodedCore;
import sourcecoded.core.configuration.SourceConfig;

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

public enum SourceConfigGuiFactory implements IModGuiFactory {
    ;

    public static Field guiFactoriesField = ReflectionHelper.findField(FMLClientHandler.class, "guiFactories");
    public static BiMap<ModContainer, IModGuiFactory> guiFactories = HashBiMap.create();

    public static boolean injectionAllowed = true;

    //In my defence, all the GuiFactory stuff in FML doesn't let you pass your own instance, only Classes/Class names, hence the mess below

    @SuppressWarnings("unchecked")
    public static void finishLoading() throws IllegalAccessException {
        BiMap<ModContainer, IModGuiFactory> guiFactoriesOld = guiFactories;

        guiFactories = (BiMap<ModContainer, IModGuiFactory>) guiFactoriesField.get(FMLClientHandler.instance());
        guiFactories.remove(SourceCodedCore.getContainer(Constants.MODID));             //Remove the GUI factory dummy

        guiFactories.putAll(guiFactoriesOld);

        injectionAllowed = false;
    }

    public static void end() {
        try {
            SourceConfigGuiFactory.finishLoading();
        } catch (IllegalAccessException e) {
            SourceCodedCore.logger.warn("SourceConfigGuiFactory could not finish loading: Field Error");
            e.printStackTrace();
        }
    }

    String modid;
    Object modInstance;
    SourceConfig config;
    Class<? extends GuiScreen> configGui;

    @SuppressWarnings("unchecked")
    SourceConfigGuiFactory(String modid, Object instance, SourceConfig config, Class configGui) {
        this.modid = modid;
        this.modInstance = instance;
        this.config = config;
        this.configGui = configGui;
    }

    public static SourceConfigGuiFactory create(String modid, Object modInstance, SourceConfig config, Class<? extends GuiScreen> configGui) {
        Class[] types = new Class[] {String.class, Object.class, SourceConfig.class, Class.class};
        Object[] params = new Object[] {modid, modInstance, config, configGui};
        return EnumHelper.addEnum(SourceConfigGuiFactory.class, modid.toUpperCase(), types, params);
    }

    public static SourceConfigGuiFactory get(String modid) {
        for (SourceConfigGuiFactory factory : SourceConfigGuiFactory.values())
            if (factory.modid.equalsIgnoreCase(modid))
                return factory;

        return null;
    }

    public void inject() {
        if (injectionAllowed) {
            ModContainer container = SourceCodedCore.getContainer(modid);
            if (container != null) {
                guiFactories.put(container, this);
            } else {
                SourceCodedCore.logger.warn("Could not find container for mod: " + modid);
                SourceCodedCore.logger.warn("Skipping GuiFactory injection");
            }
        } else {
            SourceCodedCore.logger.warn("Could not inject GuiFactory for mod: " + modid);
            SourceCodedCore.logger.warn("Injection has already taken place");
        }
    }

    @Override
    public void initialize(Minecraft minecraftInstance) {
    }

    @Override
    public Class<? extends GuiScreen> mainConfigGuiClass() {
        return configGui;
    }

    @Override
    public Set<RuntimeOptionCategoryElement> runtimeGuiCategories() {
        return ImmutableSet.of();
    }

    @Override
    public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
        return null;
    }

    public static List<IConfigElement> createElements(SourceConfig config) {
        List<IConfigElement> list = Lists.newArrayList();

        for (String cat : config.config.getCategoryNames()) {
            list.add(new ConfigElement(config.config.getCategory(cat)));
        }

        return list;
    }
}

@@ -2101,16 +2101,23 @@
  %7D%0A%0A    
+public 
 String m
@@ -2122,24 +2122,31 @@
  modid;%0A    
+public 
 Object modIn
@@ -2153,24 +2153,31 @@
 stance;%0A    
+public 
 SourceConfig
@@ -2188,50 +2188,8 @@
 fig;
-%0A    Class%3C? extends GuiScreen%3E configGui;
 %0A%0A  
@@ -2302,25 +2302,8 @@
 nfig
-, Class configGui
 ) %7B%0A
@@ -2401,44 +2401,8 @@
 ig;%0A
-        this.configGui = configGui;%0A
     
@@ -2509,46 +2509,8 @@
 nfig
-, Class%3C? extends GuiScreen%3E configGui
 ) %7B%0A
@@ -2596,21 +2596,8 @@
 lass
-, Class.class
 %7D;%0A 
@@ -2665,19 +2665,8 @@
 nfig
-, configGui
 %7D;%0A 
@@ -3821,24 +3821,92 @@
     
-return configGui
+SourceConfigBaseGui.injectGuiContext(this);%0A        return SourceConfigBaseGui.class
 ;%0A  

Update GuiFactory to use new GuiBase contexts