package net.minecraftforge.gradle.tweakers;

import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import net.minecraftforge.gradle.GradleStartCommon;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

public class CoremodTweaker implements ITweaker {
    protected static final Logger LOGGER = LogManager.getLogger("GradleStart");
    private static final String COREMOD_CLASS = "fml.relauncher.CoreModManager";
    private static final String TWEAKER_SORT_FIELD = "tweakSorting";

    @Override
    public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
    }

    @SuppressWarnings("unchecked")
    @Override
    public void injectIntoClassLoader(LaunchClassLoader classLoader) {
        try {
            Field coreModList = GradleStartCommon.getFmlClass("fml.relauncher.CoreModManager", classLoader).getDeclaredField("loadPlugins");
            coreModList.setAccessible(true);

            // grab constructor.
            Class<ITweaker> clazz = (Class<ITweaker>) GradleStartCommon.getFmlClass("fml.relauncher.CoreModManager$FMLPluginWrapper", classLoader);
            Constructor<ITweaker> construct = (Constructor<ITweaker>) clazz.getConstructors()[0];
            construct.setAccessible(true);

            Field[] fields = clazz.getDeclaredFields();
            Field pluginField = fields[1];  // 1
            Field fileField = fields[3];  // 3
            Field listField = fields[2];  // 2

            Field.setAccessible(clazz.getConstructors(), true);
            Field.setAccessible(fields, true);

            List<ITweaker> oldList = (List<ITweaker>) coreModList.get(null);

            for (int i = 0; i < oldList.size(); i++) {
                ITweaker tweaker = oldList.get(i);

                if (clazz.isInstance(tweaker)) {
                    Object coreMod = pluginField.get(tweaker);
                    Object oldFile = fileField.get(tweaker);
                    File newFile = GradleStartCommon.coreMap.get(coreMod.getClass().getCanonicalName());

                    LOGGER.info("Injecting location in coremod {}", coreMod.getClass().getCanonicalName());

                    if (newFile != null && oldFile == null) {
                        // build new tweaker.
                        oldList.set(i, construct.newInstance(new Object[]{
                                (String) fields[0].get(tweaker), // name
                                coreMod, // coremod
                                newFile, // location
                                fields[4].getInt(tweaker), // sort index?
                                ((List<String>) listField.get(tweaker)).toArray(new String[0])
                        }));
                    }
                }
            }
        } catch (Throwable t) {
            LOGGER.log(Level.ERROR, "Something went wrong with the coremod adding.");
            t.printStackTrace();
        }

        // inject the additional AT tweaker
        String atTweaker = "net.minecraftforge.gradle.tweakers.AccessTransformerTweaker";
        ((List<String>) Launch.blackboard.get("TweakClasses")).add(atTweaker);

        // make sure its after the deobf tweaker
        try {
            Field f = GradleStartCommon.getFmlClass(COREMOD_CLASS, classLoader).getDeclaredField(TWEAKER_SORT_FIELD);
            f.setAccessible(true);
            ((Map<String, Integer>) f.get(null)).put(atTweaker, Integer.valueOf(1001));
        } catch (Throwable t) {
            LOGGER.log(Level.ERROR, "Something went wrong with the adding the AT tweaker adding.");
            t.printStackTrace();
        }
    }

    @Override
    public String getLaunchTarget() {
        // if it gets here... something went terribly wrong..
        return null;
    }

    @Override
    public String[] getLaunchArguments() {
        // if it gets here... something went terribly wrong.
        return new String[0];
    }
}
