using System;
using System.Linq;
using System.Collections.Generic;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using HarmonyLib.Tools;
using System.Reflection.Emit;

namespace BanquetForCyka {
    [BepInPlugin(PluginGuid, PluginName, PluginVersion)]
    public class Main : BaseUnityPlugin {
        private const string PluginGuid = "BanquetForCyka";
        private const string PluginName = "BanquetForCyka";
        private const string PluginVersion = "1.0.0";

        public static ConfigEntry<bool> debug;
        public static ConfigEntry<bool> debugXp;
        public static ConfigEntry<bool> debugDodgeWindow;
        public static ConfigEntry<bool> debugStatEntry;
        public static ConfigEntry<bool> debugSkillEntry;
        public static ConfigEntry<bool> debugLevelUpEntry;

        public static ExpressionConfigEntry xpMultiplier;
        public static ExpressionConfigEntry dodgeWindowTotal;
        public static ExpressionConfigEntry dodgeWindowPerfect;
        public static ExpressionConfigEntry levelUpStatEntry;
        public static ExpressionConfigEntry levelUpSkillEntry;

        public void Awake() {
            debug = Config.Bind("Debug", "Global Debug", false);
            debugXp = Config.Bind("Debug", "XP Debug", false);
            debugDodgeWindow = Config.Bind("Debug", "Dodge Window Debug", false);
            debugStatEntry = Config.Bind("Debug", "Stat Entry Debug", false);
            debugSkillEntry = Config.Bind("Debug", "Skill Entry Debug", false);
            debugLevelUpEntry = Config.Bind("Debug", "Level Up Entry Debug", false);

            xpMultiplier = new ExpressionConfigEntry(
                Config, "General", "XP Multiplier", "v*1",
                "XP Multiplier expression. Use 'v' to represent the original value.", debugXp);
            dodgeWindowTotal = new ExpressionConfigEntry(
                Config, "General", "Dodge Window Total", "v*1",
                "Total dodge window duration expression. Use 'v' to represent the original value (20).",
                debugDodgeWindow);
            dodgeWindowPerfect = new ExpressionConfigEntry(
                Config, "General", "Dodge Window Perfect", "v*1",
                "Perfect dodge window percentage expression. Use 'v' to represent the original value (0.25).",
                debugDodgeWindow);
            levelUpStatEntry = new ExpressionConfigEntry(
                Config, "General", "Level Up Stat Entry", "v*1",
                "Stat Entry expression. Use 'v' to represent the original value.", debugStatEntry);
            levelUpSkillEntry = new ExpressionConfigEntry(
                Config, "General", "Level Up Skill Entry", "v*1",
                "Skill Entry expression. Use 'v' to represent the original value.", debugSkillEntry);

            Logger.LogInfo("BanquetForCyka loaded");
            HarmonyFileLog.Enabled = true;
            Harmony harmony = new Harmony(PluginGuid);
            harmony.PatchAll();
            var originalMethods = harmony.GetPatchedMethods();
            Logger.LogInfo("Patched " + originalMethods.Count() + " methods");
        }

        public static void LogDebug(string message, ConfigEntry<bool> debugFlag = null) {
            if (debug.Value || (debugFlag != null && debugFlag.Value))
                Console.WriteLine(message);
        }
    }

    [HarmonyPatch(typeof(Stats), "AddXP")]
    public class Stats_AddXP {
        public static void Prefix(ref int amt) {
            Main.LogDebug("Original XP amount: " + amt, Main.debugXp);
            float result = Main.xpMultiplier.Evaluate(amt);
            amt = (int)result;
            Main.LogDebug("Modified XP amount: " + amt, Main.debugXp);
        }
    }

    [HarmonyPatch(typeof(Actions), "DodgeCalculation")]
    public class Actions_DodgeCalculation_Transpiler {
        public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
            var codes = new List<CodeInstruction>(instructions);

            // Find the ldc.r4 20 instruction
            for (int i = 0; i < codes.Count; i++) {
                if (codes[i].opcode == OpCodes.Ldc_R4 && (float)codes[i].operand == 20f) {
                    // Replace with our expression evaluation
                    codes[i] = new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(Main), "dodgeWindowTotal"));
                    codes.Insert(i + 1, new CodeInstruction(OpCodes.Ldc_R4, 20f));
                    codes.Insert(i + 2,
                                 new CodeInstruction(OpCodes.Callvirt,
                                                     AccessTools.Method(typeof(ExpressionConfigEntry), "Evaluate")));
                    break;
                }
            }

            // Find the ldc.r4 0.25 instruction
            for (int i = 0; i < codes.Count; i++) {
                if (codes[i].opcode == OpCodes.Ldc_R4 && (float)codes[i].operand == 0.25f) {
                    // Replace with our expression evaluation
                    codes[i] =
                        new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(Main), "dodgeWindowPerfect"));
                    codes.Insert(i + 1, new CodeInstruction(OpCodes.Ldc_R4, 0.25f));
                    codes.Insert(i + 2,
                                 new CodeInstruction(OpCodes.Callvirt,
                                                     AccessTools.Method(typeof(ExpressionConfigEntry), "Evaluate")));
                    break;
                }
            }

            return codes;
        }
    }

    [HarmonyPatch(typeof(LevelUp), "ClickedStat")]
    public class Actions_ClickedStat {
        public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
            var codes = new List<CodeInstruction>(instructions);

            // Find the ldc.r4 1 instruction
            for (int i = codes.Count - 1; i >= 0; i--) {
                if (codes[i].opcode == OpCodes.Ldc_R4 && (float)codes[i].operand == 1f) {
                    // Replace with our expression evaluation
                    codes[i] = new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(Main), "levelUpStatEntry"));
                    codes.Insert(i + 1, new CodeInstruction(OpCodes.Ldc_R4, 1f));
                    codes.Insert(i + 2,
                                 new CodeInstruction(OpCodes.Callvirt,
                                                     AccessTools.Method(typeof(ExpressionConfigEntry), "Evaluate")));
                    break;
                }
            }

            return codes;
        }
    }

    [HarmonyPatch(typeof(LevelUp), "IncreaseAmt")]
    public class Actions_IncreaseAmount {
        public static void Postfix(ref int __result) {
            Main.LogDebug("Original amount: " + __result, Main.debugLevelUpEntry);
            float result = Main.levelUpSkillEntry.Evaluate(__result);
            __result = (int)result;
            Main.LogDebug("Modified amount: " + __result, Main.debugLevelUpEntry);
        }
    }
}
