using HarmonyLib;
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;
using Verse.AI;

namespace Nanosuit
{
	[StaticConstructorOnStartup]
	internal static class HarmonyContainer
	{
        public static Harmony harmony;
		static HarmonyContainer()
		{
			harmony = new Harmony("Remo.Nanosuit");
			harmony.PatchAll();
		}
	}

    [HarmonyPatch(typeof(StatExtension), nameof(StatExtension.GetStatValue))]
    public static class GetStatValue_Patch
    {
        private static void Postfix(Thing thing, StatDef stat, bool applyPostProcess, ref float __result)
        {
            if (thing is Pawn pawn)
            {
                if (stat == StatDefOf.MoveSpeed)
                {
                    foreach (var apparel in pawn.GetNanosuits())
                    {
                        if (apparel.IsActive(ApparelMode.SpeedMode) && apparel.inSpeedState)
                        {
                            __result *= apparel.def.speedMode.movementSpeedFactor;
                        }
                    }
                }
            }
        }
    }
    [HarmonyPatch(typeof(VerbProperties), "AdjustedAccuracy")]
    public static class Patch_AdjustedAccuracy
    {
        public static void Postfix(ref float __result, Thing equipment)
        {
            if (equipment.ParentHolder is Pawn_EquipmentTracker pawn_EquipmentTracker && pawn_EquipmentTracker.pawn != null)
            {
                foreach (var apparel in pawn_EquipmentTracker.pawn.GetNanosuits())
                {
                    if (apparel.IsActive(ApparelMode.StrengthMode))
                    {
                        if (__result < apparel.def.strengthMode.minAccuracyValue)
                        {
                            __result = apparel.def.strengthMode.minAccuracyValue;
                        }
                    }
                }
            }
        }
    }


    [HarmonyPatch(typeof(Verb_MeleeAttackDamage), "ApplyMeleeDamageToTarget")]
    public static class Patch_ApplyMeleeDamageToTarget
    {
        public static void Prefix(Verb_MeleeAttackDamage __instance, LocalTargetInfo target)
        {
            if (__instance.CasterPawn != null && target.Thing is Pawn victim)
            {
                foreach (var apparel in __instance.CasterPawn.GetNanosuits())
                {
                    if (apparel.IsActive(ApparelMode.StrengthMode) && Rand.Chance(apparel.def.strengthMode.meleeCriticalChance))
                    {
                        if (apparel.def.strengthMode.meleeCriticalEnergyConsumption > 0 && apparel.Energy < apparel.def.strengthMode.meleeCriticalEnergyConsumption)
                        {
                            continue;
                        }
                        List<BodyPartRecord> list = (from x in victim.RaceProps.body.AllParts where !victim.health.hediffSet.PartIsMissing(x) 
                                                     && x.depth == BodyPartDepth.Outside && x.coverage > 0.1f select x).ToList();
                        if (list.Count == 0)
                        {
                            return;
                        }
                        BodyPartRecord bodyPartRecord;
                        if (GenCollection.TryRandomElement<BodyPartRecord>(list, out bodyPartRecord))
                        {
                            var missingBodyPart = HediffMaker.MakeHediff(HediffDefOf.MissingBodyPart, victim, bodyPartRecord);
                            victim.health.AddHediff(missingBodyPart);
                        }

                        if (apparel.def.strengthMode.meleeCriticalEnergyConsumption > 0)
                        {
                            apparel.Energy -= apparel.def.strengthMode.meleeCriticalEnergyConsumption;
                        }
                    }
                }
            }
        }
    }

    [HarmonyPatch(typeof(Tool), "AdjustedCooldown")]
    internal class AdjustedCooldownPatch
    {
        static void Postfix(ref float __result, Thing ownerEquipment)
        {
            if (ownerEquipment != null && __result > 0 && ownerEquipment.ParentHolder != null && ownerEquipment.ParentHolder.ParentHolder != null)
            {
                if (ownerEquipment.ParentHolder.ParentHolder is Pawn pawn && ownerEquipment.def != null && ownerEquipment.def.IsMeleeWeapon)
                {
                    foreach (var apparel in pawn.GetNanosuits())
                    {
                        if (apparel.IsActive(ApparelMode.SpeedMode))
                        {
                            if (apparel.def.speedMode.meleeCooldownFactor.HasValue && apparel.Energy >= apparel.def.speedMode.meleeCooldownFactorEnergyConsumption)
                            {
                                __result *= apparel.def.speedMode.meleeCooldownFactor.Value;
                                apparel.Energy -= apparel.def.speedMode.meleeCooldownFactorEnergyConsumption;
                            }
                        }
                    }
                }
            }
        }
    }


    [HarmonyPatch(typeof(Pawn_JobTracker), "StartJob")]
    public class StartJobPatch
    {
        private static bool Prefix(Pawn_JobTracker __instance, Pawn ___pawn, Job newJob, JobTag? tag)
        {
            if (newJob.def == JobDefOf.Goto)
            {
                foreach (var apparel in ___pawn.GetNanosuits())
                {
                    if (apparel.IsActive(ApparelMode.SpeedMode))
                    {
                        bool inCombat = ___pawn.InCombat();
                        if (inCombat && apparel.jumpModeInCombat || !inCombat && apparel.jumpModeOutsideCombat)
                        {
                            var distance = newJob.targetA.Cell.DistanceTo(___pawn.Position);
                            if (apparel.Energy >= apparel.def.speedMode.jumpEnergyConsumption 
                                && distance <= apparel.def.speedMode.jumpMaxDistance && distance > 3
                                && Rand.Chance(apparel.def.speedMode.jumpChance))
                            {
                                Log.Message("Jump 1");
                                apparel.Jump(newJob.targetA.Cell, apparel.def.speedMode.jumpEnergyConsumption);
                                return false;
                            }
                        }

                    }
                }
            }
            return true;
        }
    }


    [HarmonyPatch(typeof(Pawn_PathFollower), "StartPath")]
    public class StartPathPatch
    {
        private static void Postfix(Pawn_PathFollower __instance, Pawn ___pawn, LocalTargetInfo dest, PathEndMode peMode)
        {
            foreach (var apparel in ___pawn.GetNanosuits())
            {
                if (apparel.IsActive(ApparelMode.SpeedMode))
                {
                    bool inCombat = ___pawn.InCombat();
                    if (inCombat && apparel.jumpModeInCombat || !inCombat && apparel.jumpModeOutsideCombat)
                    {
                        var distance = __instance.Destination.Cell.DistanceTo(___pawn.Position);
                        if (apparel.Energy >= apparel.def.speedMode.jumpEnergyConsumption
                            && distance <= apparel.def.speedMode.jumpMaxDistance && distance > 3
                            && Rand.Chance(apparel.def.speedMode.jumpChance))
                        {
                            Log.Message("Jump 2");
                            apparel.Jump(__instance.Destination.Cell, apparel.def.speedMode.jumpEnergyConsumption);
                        }
                    }
                }
            }
        }
    }

    [HarmonyPatch(typeof(Pawn_ApparelTracker), "Wear")]
    public static class ApparelTracker_Wear
    {
        public static void Postfix(Apparel newApparel, Pawn_ApparelTracker __instance)
        {
            Pawn pawn = (__instance != null) ? __instance.pawn : null;
            if (pawn != null && newApparel is Apparel_Nanosuit nanosuit && nanosuit.def.hardRemoval != null)
            {
                __instance.Lock(newApparel);
            }
        }
    }

    [HarmonyPatch(typeof(ThoughtWorker_PsychicDrone), "CurrentStateInternal")]
    public static class CurrentStateInternalPatch
    {
        public static void Postfix(Pawn p, ref ThoughtState __result)
        {
            if (__result.StageIndex >= 0)
            {
                foreach (var apparel in p.GetNanosuits())
                {
                    if (apparel.def.psychicWaveControl != null)
                    {
                        __result = false;
                    }
                }
            }
        }
    }

    [HarmonyPatch(typeof(GenTemperature), "ComfortableTemperatureRange", new Type[] { typeof(Pawn) })]
    public static class ComfortableTemperatureRangePatch
    {
        public static bool dontCheckThis;
        public static void Postfix(Pawn p, ref FloatRange __result)
        {
            if (!dontCheckThis)
            {
                var ambientTemperature = p.AmbientTemperature;
                if (!__result.Includes(ambientTemperature))
                {
                    foreach (var apparel in p.GetNanosuits())
                    {
                        if (apparel.def.environmentalControl != null && apparel.Energy >= apparel.def.environmentalControl.energyConsumptionWhenActive)
                        {
                            __result = apparel.def.environmentalControl.temperatureProtectionRange;
                        }
                    }
                }
            }
        }
    }

    [HarmonyPatch(typeof(StatWorker), "StatOffsetFromGear")]
    public class StatOffsetFromGear_Patch
    {
        public static void Postfix(ref float __result, Thing gear, StatDef stat)
        {
            if (stat == StatDefOf.ShootingAccuracyPawn && gear is Apparel_Nanosuit nanosuit)
            {
                if (nanosuit.NightVisionWorks())
                {
                    __result += nanosuit.def.nightVisor.accuracyBonus;
                }
            }
        }
    }

    [HarmonyPatch(typeof(FireUtility), "CanEverAttachFire")]
    public class CanEverAttachFire_Patch
    {
        public static void Postfix(Thing t, ref bool __result)
        {
            if (t is Pawn pawn && pawn.GetNanosuits().Any())
            {
                __result = false;
            }
        }
    }

    [HarmonyPatch(typeof(Pawn_CarryTracker))]
    [HarmonyPatch("TryDropCarriedThing")]
    [HarmonyPatch(new Type[]
    {
            typeof(IntVec3),
            typeof(ThingPlaceMode),
            typeof(Thing),
            typeof(Action<Thing, int>)
    }, new ArgumentType[]
    {
            0,
            0,
            ArgumentType.Out,
            0
    })]
    public static class TryDropCarriedThingPatch
    {
        public static Pawn pawn;
        public static bool Prefix(Pawn_CarryTracker __instance, IntVec3 dropLoc, ThingPlaceMode mode, Thing resultingThing, Action<Thing, int> placedAction = null)
        {
            if (__instance.pawn == pawn)
            {
                pawn = null;
                return false;
            }
            return true;
        }
    }

    [HarmonyPatch(typeof(Pawn_CarryTracker))]
    [HarmonyPatch("TryDropCarriedThing")]
    [HarmonyPatch(new Type[]
    {
        typeof(IntVec3),
        typeof(int),
        typeof(ThingPlaceMode),
        typeof(Thing),
        typeof(Action<Thing, int>)
    }, new ArgumentType[]
    {
        0,
        0,
        0,
         ArgumentType.Out,
        0
    })]
    public static class TryDropCarriedThingPatch2
    {
        public static bool Prefix(Pawn_CarryTracker __instance, int count, IntVec3 dropLoc, ThingPlaceMode mode, Thing resultingThing, Action<Thing, int> placedAction = null)
        {
            if (__instance.pawn == TryDropCarriedThingPatch.pawn)
            {
                TryDropCarriedThingPatch.pawn = null;
                return false;
            }
            return true;
        }
    }

    [HarmonyPatch(typeof(PawnFlyer), "RespawnPawn")]
    public class RespawnPawn_Patch
    {
        public static void Prefix(PawnFlyer __instance, out Pawn __state)
        {
            TryDropCarriedThingPatch.pawn = null;
            __state = __instance.FlyingPawn;
        }

        public static void Postfix(Pawn __state)
        {
            if (__state != null && __state.GetNanosuits().Any())
            {
                if (__state.carryTracker.CarriedThing != null)
                {
                    __state.carryTracker.TryDropCarriedThing(__state.Position, ThingPlaceMode.Direct, out Thing resultingThing);
                }
            }
        }
    }


    [HarmonyPatch(typeof(Pawn), "Notify_Teleported")]
    public class Notify_Teleported
    {
        public static bool preventEndingJob;
        private static void Prefix(ref bool endCurrentJob)
        {
            if (preventEndingJob)
            {
                endCurrentJob = false;
            }
        }
    }

    [HarmonyPatch(typeof(StunHandler), "StunFor_NewTmp")]
    public class StunFor_NewTmp
    {
        private static bool Prefix()
        {
            if (Notify_Teleported.preventEndingJob)
            {
                return false;
            }
            return true;
        }
    }

    [HarmonyPatch(typeof(ThingOwner<Thing>))]
    [HarmonyPatch("TryAdd")]
    [HarmonyPatch(new Type[]
    {
            typeof(Thing),
            typeof(bool)
    })]
    internal static class ThingOwner_TryAdd
    {
        private static void Postfix(ThingOwner<Thing> __instance, bool __result, Thing item)
        {
            if (__result && __instance.Owner is Pawn_ApparelTracker apparelTracker && item?.def == NS_DefOf.NS_Apparel_Nanosuit)
            {
                var apparel = ThingMaker.MakeThing(NS_DefOf.NS_Apparel_NanosuitHelmet) as Apparel;
                apparelTracker.pawn.apparel.Wear(apparel);
            }
        }
    }
    [HarmonyPatch(typeof(Pawn_ApparelTracker), "TryDrop", 
        new Type[] { typeof(Apparel), typeof(Apparel), typeof(IntVec3), typeof(bool) },
        new ArgumentType[] { ArgumentType.Normal, ArgumentType.Out, ArgumentType.Normal, ArgumentType.Normal })]
    public static class Patch_TryDrop
    {
        private static void Postfix(Pawn_ApparelTracker __instance, Apparel ap)
        {
            if (ap?.def == NS_DefOf.NS_Apparel_Nanosuit)
            {
                var apparel = __instance.pawn.apparel?.WornApparel.FirstOrDefault(x => x.def == NS_DefOf.NS_Apparel_NanosuitHelmet);
                if (apparel != null)
                {
                    __instance.pawn.apparel.TryDrop(apparel, out var resultingAp);
                    resultingAp.Destroy();
                }
            }
        }
    }
}
