using HarmonyLib;
using System;
using UnityEngine;

namespace MuzikaGromche
{
    [HarmonyPatch(typeof(RoundManager))]
    static class SpawnRatePatch
    {
        const string JesterEnemyName = "Jester";

        // GetRandomWeightedIndex is not only called from AssignRandomEnemyToVent,
        // so in order to differentiate it from other calls, prefix assigns these
        // global variables, and postfix cleans them up.

        // If set to null, do not override spawn chances.
        // Otherwise, it is an index of Jester in RoundManager.currentLevel.Enemies
        static int? EnemyIndex = null;
        static float SpawnTime = 0f;

        [HarmonyPatch(nameof(RoundManager.AssignRandomEnemyToVent))]
        [HarmonyPrefix]
        static void AssignRandomEnemyToVentPrefix(RoundManager __instance, EnemyVent vent, float spawnTime)
        {
            if (!Config.OverrideSpawnRates.Value)
            {
                return;
            }

            var index = __instance.currentLevel.Enemies.FindIndex(enemy => enemy.enemyType.enemyName == JesterEnemyName);
            if (index == -1)
            {
                return;
            }

            EnemyIndex = index;
            SpawnTime = spawnTime;
        }

        [HarmonyPatch(nameof(RoundManager.AssignRandomEnemyToVent))]
        [HarmonyPostfix]
        static void AssignRandomEnemyToVentPostfix(RoundManager __instance, EnemyVent vent, float spawnTime)
        {
            EnemyIndex = null;
            SpawnTime = 0f;
        }

        [HarmonyPatch(nameof(RoundManager.GetRandomWeightedIndex))]
        [HarmonyPrefix]
        static void GetRandomWeightedIndexPostfix(RoundManager __instance, int[] weights, System.Random randomSeed)
        {
            if (EnemyIndex is int index)
            {
                if (__instance.EnemyCannotBeSpawned(index))
                {
                    return;
                }

                //    0 ==  6:00 AM
                //   60 ==  7:00 AM
                //  100 ==  7:40 AM (Cycle #1)
                //  120 ==  8:00 AM
                //  180 ==  9:00 AM (Cycle #2)
                //  300 == 11:00 AM (Cycle #3)
                //  420 ==  1:00 PM (Cycle #4)
                //  540 ==  3:00 PM (Cycle #5)
                //  660 ~=  5:00 PM
                //  780 ~=  7:00 PM
                //  900 ~=  9:00 PM
                // 1020 ~= 11:00 PM
                // 1080 == 12:00 AM
                const float minMultiplierTime = 200f; // 9:20 AM
                const float maxMultiplierTime = 500f; // 2:00 PM
                var normalizedMultiplierTime = Mathf.Clamp((SpawnTime - minMultiplierTime) / (maxMultiplierTime - minMultiplierTime), 0f, 1f);
                // Start slowly, then escalate it quickly
                normalizedMultiplierTime = Easing.InCubic.Eval(normalizedMultiplierTime);

                const float minMultiplier = 1f;
                const float maxMultiplier = 15f;
                var multiplier = Mathf.Lerp(minMultiplier, maxMultiplier, normalizedMultiplierTime);

                var newWeight = Mathf.FloorToInt(weights[index] * multiplier);
                Debug.Log($"{nameof(MuzikaGromche)} {nameof(SpawnRatePatch)} Overriding spawn weight[{index}] {weights[index]} * {multiplier} => {newWeight} for t={SpawnTime}");
                weights[index] = newWeight;
            }
        }
    }
}
