using HarmonyLib;
using RimWorld;
using RimWorld.Planet;
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
{
    public enum ApparelMode
    {
        None,
        ArmorMode,
        StrengthMode,
        SpeedMode,
    }
    public class ArmorMode
    {
        public float? meleeDamageAbsorbingRate;
        public float? rangeDamageAbsorbingRate;
        public float? explosionDamageAbsorbingRate;
    }

    public class StrengthMode
    {
        public float meleeCriticalChance;
        public float meleeCriticalEnergyConsumption;
        public float minAccuracyValue;
        public bool jumpAbility;
        public float jumpMaxDistance;
        public float jumpEnergyConsumption;
    }

    public class SpeedMode
    {
        public float movementSpeedFactor;
        public float energyConsumptionPerTickWhenActive;

        public float jumpChance;
        public bool canJumpInCombat;
        public bool canJumpOutsideCombat;

        public float jumpMaxDistance;
        public float jumpEnergyConsumption;

        public float? meleeCooldownFactor;
        public float meleeCooldownFactorEnergyConsumption;
    }

    public class CloakMode
    {
        public float energyConsumptionPerTickMoving;
        public float energyConsumptionPerTick;
    }

    public class Symbiosis
    {
        public int healsPerTick;
        public float energyConsumption;
    }
    public class HardRemoval
    {
        public List<RecipeDef> surgeryList;
    }
    public class PsychicWaveControl
    {
        public float energyGainPerTickWhenActive;
    }
    public class EnvironmentalControl
    {
        public FloatRange temperatureProtectionRange;
        public float energyConsumptionWhenActive;
    }

    public class TransceiverDevice
    {
        public float maxRangeEffect;
        public float energyConsumption;
    }

    public class NightVisor
    {
        public float accuracyBonus;
        public float energyConsumptionPerTickWhenActive;
    }
    public class Apparel_Nanosuit : Apparel
    {
        public ApparelMode activeMode;
        public Dictionary<ApparelMode, bool> activeModes;

        private float energy;
        private float energyBonus;
        public float Energy
        {
            get
            {
                if (energyBonus < 0)
                {
                    energyBonus = 0;
                }
                if (energy > this.def.maxEnergyAmount)
                {
                    energy = this.def.maxEnergyAmount;
                }
                return energy + energyBonus;
            }
            set
            {
                this.energy = value;
                if (this.energy < 0 && this.energyBonus > 0)
                {
                    this.energyBonus -= Mathf.Abs(this.energy);
                    if (this.energyBonus < 0)
                    {
                        this.energyBonus = 0;
                    }
                }
            }
        }
        public bool jumpModeOutsideCombat;
        public bool jumpModeInCombat;
        public bool symbiosisActive;
        public bool nightVisorActive;
        public NanosuitDef def => base.def as NanosuitDef;
        public override bool CheckPreAbsorbDamage(DamageInfo dinfo)
        {
            bool absorbed = false;
            if (IsActive(ApparelMode.ArmorMode))
            {
                if (dinfo.Def.isRanged && def.armorMode.rangeDamageAbsorbingRate.HasValue)
                {
                    var energyToConsume = dinfo.Amount * def.armorMode.rangeDamageAbsorbingRate.Value;
                    if (Energy >= energyToConsume)
                    {
                        Energy -= energyToConsume;
                        absorbed = true;
                    }
                    Log.Message("RANGE: dinfo: " + dinfo + " - " + dinfo.Weapon + " - " + dinfo.Def.isRanged + " - " + dinfo.Category);

                }
                else if (dinfo.Def.isExplosive && def.armorMode.explosionDamageAbsorbingRate.HasValue)
                {
                    var energyToConsume = dinfo.Amount * def.armorMode.explosionDamageAbsorbingRate.Value;
                    if (Energy >= energyToConsume)
                    {
                        Energy -= energyToConsume;
                        absorbed = true;
                    }
                    Log.Message("EXP: dinfo: " + dinfo + " - " + dinfo.Weapon + " - " + dinfo.Def.isRanged + " - " + dinfo.Category);

                }
                else if ((dinfo.Weapon == null && dinfo.Instigator is Pawn || dinfo.Weapon.IsMeleeWeapon || dinfo.Weapon == ThingDefOf.Human || !dinfo.Def.isRanged) && def.armorMode.meleeDamageAbsorbingRate.HasValue)
                {
                    var energyToConsume = dinfo.Amount * def.armorMode.meleeDamageAbsorbingRate.Value;
                    if (Energy >= energyToConsume)
                    {
                        Energy -= energyToConsume;
                        absorbed = true;
                    }
                    Log.Message("MELEE: dinfo: " + dinfo + " - " + dinfo.Weapon + " - " + dinfo.Def.isRanged + " - " + dinfo.Category);
                }
                else
                {
                    Log.Message("UNK: dinfo: " + dinfo + " - " + dinfo.Weapon + " - " + dinfo.Def.isRanged + " - " + dinfo.Category);
                }
            }

            return absorbed;
        }

        public bool inSpeedState;

        public int skipTicks;
        public override void Tick()
        {
            base.Tick();
            Energy += def.energyRegenerationPerTick;
            if (Energy > def.maxEnergyAmount)
            {
                Energy = def.maxEnergyAmount;
            }
            if (this.Wearer != null)
            {
                if (IsActive(ApparelMode.SpeedMode) && (this.Wearer.pather?.MovingNow ?? false))
                {
                    if (Energy >= def.speedMode.energyConsumptionPerTickWhenActive)
                    {
                        Energy -= def.speedMode.energyConsumptionPerTickWhenActive;
                        inSpeedState = true;
                    }
                    else
                    {
                        inSpeedState = false;
                    }
                }
                if (this.def.symbiosis != null && symbiosisActive && this.Wearer.IsHashIntervalTick(this.def.symbiosis.healsPerTick) && energy >= this.def.symbiosis.energyConsumption)
                {
                    List<BodyPartRecord> missingParts = (from x in this.Wearer.RaceProps.body.AllParts where this.Wearer.health.hediffSet.PartIsMissing(x)select x).ToList();
                    if (missingParts.Any())
                    {
                        var partToRestore = missingParts.RandomElement();
                        this.Wearer.health.RestorePart(partToRestore, null, true);
                        Energy -= this.def.symbiosis.energyConsumption;
                        Log.Message("REstoreing " + partToRestore);
                    }
                    else
                    {
                        var badHediffs = this.Wearer.health.hediffSet.hediffs.Where(x => (x.TryGetComp<HediffComp_GetsPermanent>()?.IsPermanent ?? false) || x.def.isBad);
                        if (badHediffs.Any())
                        {
                            var hediff = badHediffs.RandomElement(); ;
                            var comp = hediff.TryGetComp<HediffComp_GetsPermanent>();
                            if (comp != null && comp.IsPermanent)
                            {
                                this.Wearer.health.hediffSet.hediffs.Remove(hediff);
                                Energy -= this.def.symbiosis.energyConsumption;
                                Log.Message("Removing " + hediff);

                            }
                            else if (hediff.def.isBad)
                            {
                                this.Wearer.health.hediffSet.hediffs.Remove(hediff);
                                Energy -= this.def.symbiosis.energyConsumption;
                                Log.Message("Removing " + hediff);
                            }
                        }
                    }

                    if (this.Wearer.Downed && !this.Wearer.ShouldBeDowned())
                    {
                        Traverse.Create(this.Wearer.health).Method("MakeUndowned").GetValue();
                    }
                    this.Wearer.health.hediffSet.DirtyCache();
                }

                if (this.def.psychicWaveControl != null)
                {
                    var psychicDroneLevel = GetPsychicDroneLevel();
                    if (psychicDroneLevel > PsychicDroneLevel.None)
                    {
                        energyBonus += this.def.psychicWaveControl.energyGainPerTickWhenActive;
                    }
                }

                if (this.def.environmentalControl != null && this.energy > 0)
                {
                    ComfortableTemperatureRangePatch.dontCheckThis = true;
                    var ambientTemperature = this.Wearer.AmbientTemperature;
                    var confortableTemperature = this.Wearer.ComfortableTemperatureRange();
                    if (!confortableTemperature.Includes(ambientTemperature))
                    {
                        this.Energy -= this.def.environmentalControl.energyConsumptionWhenActive;
                    }
                    ComfortableTemperatureRangePatch.dontCheckThis = false;
                }
                var cloakHediff = this.Wearer.health.hediffSet.GetFirstHediffOfDef(NS_DefOf.NS_CloakMode);
                if (this.def.cloakMode != null && cloakHediff != null)
                {
                    if (this.Wearer.pather.Moving)
                    {
                        if (this.Energy >= this.def.cloakMode.energyConsumptionPerTickMoving)
                        {
                            this.Energy -= this.def.cloakMode.energyConsumptionPerTickMoving;
                        }
                        else
                        {
                            this.Wearer.health.RemoveHediff(cloakHediff);
                        }
                    }
                    else
                    {
                        if (this.Energy >= this.def.cloakMode.energyConsumptionPerTick)
                        {
                            this.Energy -= this.def.cloakMode.energyConsumptionPerTick;
                        }
                        else
                        {
                            this.Wearer.health.RemoveHediff(cloakHediff);
                        }
                    }
                }

                if (this.def.nightVisor != null && NightVisionWorks())
                {
                    this.Energy -= this.def.nightVisor.energyConsumptionPerTickWhenActive;
                    Traverse.Create(this.Wearer.needs.mood.recentMemory).Field("lastLightTick").SetValue(Find.TickManager.TicksGame);
                }

                if (this.ability != null)
                {
                    this.ability.AbilityTick();
                    if (skipTicks == 0)
                    {
                        Wearer.jobs?.curDriver?.Notify_PatherArrived();
                    }
                    if (skipTicks > -2)
                    {
                        skipTicks--;
                    }
                }
            }
        }
        public bool NightVisionWorks()
        {
            var pawn = Wearer;
            if (pawn != null && pawn.Map != null && pawn.Map.glowGrid.PsychGlowAt(pawn.Position) == PsychGlow.Dark && this.nightVisorActive && this.energy >= this.def.nightVisor.energyConsumptionPerTickWhenActive)
            {
                return true;
            }
            return false;
        }
        private PsychicDroneLevel GetPsychicDroneLevel()
        {
            Pawn p = this.Wearer;
            PsychicDroneLevel psychicDroneLevel = PsychicDroneLevel.None;
            if (p.Map != null)
            {
                PsychicDroneLevel highestPsychicDroneLevelFor = p.Map.gameConditionManager.GetHighestPsychicDroneLevelFor(p.gender);
                if ((int)highestPsychicDroneLevelFor > (int)psychicDroneLevel)
                {
                    psychicDroneLevel = highestPsychicDroneLevelFor;
                }
            }
            else if (p.IsCaravanMember())
            {
                foreach (Site site in Find.World.worldObjects.Sites)
                {
                    foreach (SitePart part in site.parts)
                    {
                        if (!part.conditionCauser.DestroyedOrNull() && part.def.Worker is SitePartWorker_ConditionCauser_PsychicDroner)
                        {
                            CompCauseGameCondition_PsychicEmanation compCauseGameCondition_PsychicEmanation = part.conditionCauser.TryGetComp<CompCauseGameCondition_PsychicEmanation>();
                            if (compCauseGameCondition_PsychicEmanation.ConditionDef.conditionClass == typeof(GameCondition_PsychicEmanation) && compCauseGameCondition_PsychicEmanation.InAoE(p.GetCaravan().Tile) && compCauseGameCondition_PsychicEmanation.gender == p.gender && (int)compCauseGameCondition_PsychicEmanation.Level > (int)psychicDroneLevel)
                            {
                                psychicDroneLevel = compCauseGameCondition_PsychicEmanation.Level;
                            }
                        }
                    }
                }
                foreach (Map map in Find.Maps)
                {
                    foreach (GameCondition activeCondition in map.gameConditionManager.ActiveConditions)
                    {
                        CompCauseGameCondition_PsychicEmanation compCauseGameCondition_PsychicEmanation2 = activeCondition.conditionCauser.TryGetComp<CompCauseGameCondition_PsychicEmanation>();
                        if (compCauseGameCondition_PsychicEmanation2 != null && compCauseGameCondition_PsychicEmanation2.InAoE(p.GetCaravan().Tile) && compCauseGameCondition_PsychicEmanation2.gender == p.gender && (int)compCauseGameCondition_PsychicEmanation2.Level > (int)psychicDroneLevel)
                        {
                            psychicDroneLevel = compCauseGameCondition_PsychicEmanation2.Level;
                        }
                    }
                }
            }
            return psychicDroneLevel;
        }

        public TargetingParameters ForLoc(Pawn user)
        {
            TargetingParameters targetingParameters = new TargetingParameters();
            targetingParameters.canTargetLocations = true;
            if (this.def.strengthMode.jumpMaxDistance > 0)
            {
                targetingParameters.validator = (TargetInfo x) => user.Position.DistanceTo(x.Cell) <= this.def.strengthMode.jumpMaxDistance;
            }
            return targetingParameters;
        }

        public TargetingParameters ForHackableShields(Pawn user)
        {
            TargetingParameters targetingParameters = new TargetingParameters();
            targetingParameters.canTargetBuildings = true;
            targetingParameters.canTargetPawns = true;
            targetingParameters.validator = (TargetInfo x) => IsHackableShield(x.Thing) && user.Position.DistanceTo(x.Cell) <= this.def.transceiverDevice.maxRangeEffect;
            return targetingParameters;
        }

        private bool IsHackableShield(Thing target)
        {
            if (target is Pawn pawn)
            {
                var shieldBelts = pawn.apparel?.WornApparel.OfType<ShieldBelt>();
                if (shieldBelts?.Any() ?? false)
                {
                    return true;
                }
                foreach (var comp in pawn.AllComps)
                {
                    if (IsCustomShieldBeltComp(comp))
                    {
                        return true;
                    }
                }
                foreach (var apparel in pawn.apparel?.WornApparel ?? new List<Apparel>())
                {
                    if (IsCustomShieldBelt(apparel))
                    {
                        return true;
                    }
                    foreach (var comp in apparel.AllComps)
                    {
                        if (IsCustomShieldBeltComp(comp))
                        {
                            return true;
                        }
                    }
                }
            }
            else
            {
                var comp = target.TryGetComp<CompProjectileInterceptor>();
                if (comp != null)
                {
                    return true;
                }
            }
            return false;
        }

        private bool IsCustomShieldBelt(Thing thing)
        {
            var type = thing.GetType();
            if (type == ModCompatibility.ShieldMechBubbleType || type == ModCompatibility.ArchotechShieldBelt || type == ModCompatibility.RangedShieldBelt)
            {
                return true;
            }
            return false;
        }
        private bool IsCustomShieldBeltComp(ThingComp thingComp)
        {
            var type = thingComp.GetType();
            if (type == ModCompatibility.ShieldMechBubbleType || type == ModCompatibility.ArchotechShieldBelt || type == ModCompatibility.RangedShieldBelt)
            {
                return true;
            }
            return false;
        }

        public override IEnumerable<Gizmo> GetWornGizmos()
        {
            foreach (Gizmo wornGizmo in base.GetWornGizmos())
            {
                yield return wornGizmo;
            }
            if (Find.Selector.SingleSelectedThing == base.Wearer && base.Wearer.IsColonistPlayerControlled)
            {
                Gizmo_NanosuitEnergyStatus gizmo_NanosuitEnergyStatus = new Gizmo_NanosuitEnergyStatus();
                gizmo_NanosuitEnergyStatus.nanosuit = this;
                yield return gizmo_NanosuitEnergyStatus;
            }
            if (this.def.armorMode != null)
            {
                yield return new Command_Toggle
                {
                    defaultLabel = "NS.ArmorMode".Translate(),
                    defaultDesc = "NS.ArmorModeDesc".Translate(),
                    hotKey = NS_DefOf.NS_ArmorMode,
                    icon = ContentFinder<Texture2D>.Get("UI/Buttons/armor"),
                isActive = () => IsActive(ApparelMode.ArmorMode),
                    toggleAction = delegate
                    {
                        SwitchApparelMode(ApparelMode.ArmorMode);
                    }
                };
            }
            if (this.def.strengthMode != null)
            {
                yield return new Command_Toggle
                {
                    defaultLabel = "NS.StrengthMode".Translate(),
                    defaultDesc = "NS.StrengthModeDesc".Translate(),
                    icon = ContentFinder<Texture2D>.Get("UI/Buttons/strength"),
                    isActive = () => IsActive(ApparelMode.StrengthMode),
                    hotKey = NS_DefOf.NS_StrengthMode,
                    toggleAction = delegate
                    {
                        SwitchApparelMode(ApparelMode.StrengthMode);
                    }
                };
                if (this.def.strengthMode.jumpAbility)
                {
                    var command = new Command_Action
                    {
                        defaultLabel = "NS.Jump".Translate(),
                        defaultDesc = "NS.JumpDesc",
                        hotKey = NS_DefOf.NS_JumpMode,
                        icon = ContentFinder<Texture2D>.Get("UI/Buttons/jump"),
                        action = delegate ()
                        {
                            Find.Targeter.BeginTargeting(ForLoc(this.Wearer), delegate (LocalTargetInfo x)
                            {
                                Jump(x.Cell, this.def.strengthMode.jumpEnergyConsumption);
                            }, null, null);
                        }
                    };
                    if (this.def.strengthMode.jumpEnergyConsumption > 0 && Energy < this.def.strengthMode.jumpEnergyConsumption)
                    {
                        command.Disable("NS.LowEnergy".Translate());
                    }
                    yield return command;
                }
            }
            if (this.def.speedMode != null)
            {
                yield return new Command_Toggle
                {
                    defaultLabel = "NS.SpeedMode".Translate(),
                    defaultDesc = "NS.SpeedModeModeDesc".Translate(),
                    hotKey = NS_DefOf.NS_SpeedMode,
                    icon = ContentFinder<Texture2D>.Get("UI/Buttons/speed"),
                    isActive = () => IsActive(ApparelMode.SpeedMode),
                    toggleAction = delegate
                    {
                        SwitchApparelMode(ApparelMode.SpeedMode);
                    }
                };
                if (IsActive(ApparelMode.SpeedMode))
                {
                    if (this.def.speedMode.canJumpOutsideCombat)
                    {
                        yield return new Command_Toggle
                        {
                            defaultLabel = "NS.SpeedModeJumpModeOutsideCombat".Translate(),
                            defaultDesc = "NS.SpeedModeJumpModeOutsideCombatDesc".Translate(),
                            icon = ContentFinder<Texture2D>.Get("UI/Buttons/speed_non_combat"),
                            isActive = () => jumpModeOutsideCombat,
                            toggleAction = delegate
                            {
                                jumpModeOutsideCombat = !jumpModeOutsideCombat;
                            }
                        };
                    }
                    if (this.def.speedMode.canJumpInCombat)
                    {
                        yield return new Command_Toggle
                        {
                            defaultLabel = "NS.SpeedModeJumpModeInCombat".Translate(),
                            defaultDesc = "NS.SpeedModeJumpModeInCombatDesc".Translate(),
                            icon = ContentFinder<Texture2D>.Get("UI/Buttons/speed_in_combat_2"),
                            isActive = () => jumpModeInCombat,
                            toggleAction = delegate
                            {
                                jumpModeInCombat = !jumpModeInCombat;
                            }
                        };
                    }
                }
            }
            if (this.def.cloakMode != null)
            {
                var command = new Command_Toggle
                {
                    defaultLabel = "NS.CloakMode".Translate(),
                    defaultDesc = "NS.CloakModeDesc",
                    hotKey = NS_DefOf.NS_CloakModeKey,
                    icon = ContentFinder<Texture2D>.Get("UI/Buttons/cloak"),
                    isActive = () => Wearer.health.hediffSet.GetFirstHediffOfDef(NS_DefOf.NS_CloakMode) != null,
                    toggleAction = delegate ()
                    {
                        var hediff = Wearer.health.hediffSet.GetFirstHediffOfDef(NS_DefOf.NS_CloakMode);
                        if (hediff != null)
                        {
                            Wearer.health.RemoveHediff(hediff);
                        }
                        else
                        {
                            hediff = HediffMaker.MakeHediff(NS_DefOf.NS_CloakMode, Wearer);
                            this.Wearer.health.AddHediff(hediff);
                        }
                    }
                };
                if (this.def.cloakMode.energyConsumptionPerTick > 0 && Energy < this.def.cloakMode.energyConsumptionPerTick)
                {
                    command.Disable("NS.LowEnergy".Translate());
                }
                yield return command;
            }
            if (this.def.transceiverDevice != null)
            {
                var command = new Command_Action
                {
                    defaultLabel = "NS.HackShields".Translate(),
                    defaultDesc = "NS.HackShieldsDesc",
                    hotKey = NS_DefOf.NS_HackShields,
                    icon = ContentFinder<Texture2D>.Get("UI/Buttons/transceiverDevice1"),
                    action = delegate ()
                    {
                        Find.Targeter.BeginTargeting(ForHackableShields(this.Wearer), delegate (LocalTargetInfo x)
                        {
                            DisableShield(x.Thing);
                        }, null, null);
                    }
                };
                if (this.def.transceiverDevice.energyConsumption > 0 && Energy < this.def.transceiverDevice.energyConsumption)
                {
                    command.Disable("NS.LowEnergy".Translate());
                }
                yield return command;


            }
            if (this.def.symbiosis != null)
            {
                yield return new Command_Toggle
                {
                    defaultLabel = "NS.SymbiosysMode".Translate(),
                    defaultDesc = "NS.SymbiosysModeDesc".Translate(),
                    icon = ContentFinder<Texture2D>.Get("UI/Buttons/symbiosis"),
                    isActive = () => symbiosisActive,
                    hotKey = NS_DefOf.NS_SymbiosysMode,
                    toggleAction = delegate
                    {
                        symbiosisActive = !symbiosisActive;
                    }
                };
            }
        }

        private void DisableShield(Thing target)
        {
            if (target is Pawn pawn)
            {
                var shieldBelts = pawn.apparel?.WornApparel.OfType<ShieldBelt>();
                if (shieldBelts != null)
                {
                    foreach (var shield in shieldBelts)
                    {
                        Traverse.Create(shield).Method("Break").GetValue();
                    }
                }

                foreach (var comp in pawn.AllComps)
                {
                    if (IsCustomShieldBeltComp(comp))
                    {
                        Traverse.Create(comp).Method("Break").GetValue();
                    }
                }
                var list = pawn.apparel?.WornApparel ?? new List<Apparel>();
                for (int num = list.Count - 1; num >= 0; num--)
                {
                    var apparel = list[num];
                    foreach (var comp in apparel.AllComps)
                    {
                        if (IsCustomShieldBeltComp(comp))
                        {
                            Traverse.Create(comp).Method("Break").GetValue();
                        }
                    }
                    if (IsCustomShieldBelt(apparel))
                    {
                        Traverse.Create(apparel).Method("Break").GetValue();
                    }
                }
            }
            else
            {
                var comp = target.TryGetComp<CompProjectileInterceptor>();
                Traverse.Create(comp).Field("shutDown").SetValue(true);
            }
            Energy -= this.def.transceiverDevice.energyConsumption;
        }

        private CompAbilityEffect_Teleport customTeleportComp;
        private Ability ability;
        public void Jump(IntVec3 target, float energyToConsume)
        {
            if (NanosuitMod.settings.useSkipForJumping)
            {
                SkipTo(target, energyToConsume);
            }
            else
            {
                TryDropCarriedThingPatch.pawn = this.Wearer;
                JumpTo(target, energyToConsume);
            }
        }

        public void SkipTo(IntVec3 target, float energyToConsume)
        {
            Notify_Teleported.preventEndingJob = true;
            if (customTeleportComp is null)
            {
                ability = new Ability(Wearer, DefDatabase<AbilityDef>.GetNamed("Skip"));
                customTeleportComp = new CompAbilityEffect_Teleport();
                customTeleportComp.parent = ability;
                customTeleportComp.Initialize(new CompProperties_AbilityTeleport
                {
                    destination = AbilityEffectDestination.Selected,
                    requiresLineOfSight = true,
                    range = 27.9f,
                    clamorType = ClamorDefOf.Ability,
                    clamorRadius = 10,
                    destClamorType = ClamorDefOf.Ability,
                    destClamorRadius = 10,
                    stunTicks = new IntRange(18, 60),
                    goodwillImpact = -15,
                    applyGoodwillImpactToLodgers = false
                });
            }
            customTeleportComp.Apply(this.Wearer, target);
            if (energyToConsume > 0)
            {
                this.Energy -= energyToConsume;
            }
            skipTicks = 60;
            Notify_Teleported.preventEndingJob = false;
            Log.Message("Wearer.CurJob: " + Wearer.CurJob);
        }
        public void JumpTo(IntVec3 target, float energyToConsume)
        {
            var map = this.Wearer?.Map;
            if (map != null && target.IsValid)
            {
                this.Wearer.rotationTracker.FaceCell(target);
                PawnFlyer pawnFlyer = PawnFlyer.MakeFlyer(ThingDefOf.PawnJumper, this.Wearer, target);
                if (pawnFlyer != null)
                {
                    GenSpawn.Spawn(pawnFlyer, target, map);
                }
                if (energyToConsume > 0)
                {
                    this.Energy -= energyToConsume;
                }
            }
        }
        public bool IsActive(ApparelMode apparelMode)
        {
            if (NanosuitMod.settings.nanosuitModesAtTheSameTime)
            {
                if (activeModes is null)
                {
                    activeModes = new Dictionary<ApparelMode, bool>();
                }
                if (!activeModes.TryGetValue(apparelMode, out bool value))
                {
                    value = false;
                    activeModes[apparelMode] = value;
                }
                return value;
            }
            else
            {
                return activeMode == apparelMode;
            }
        }
        private void SwitchApparelMode(ApparelMode apparelMode)
        {
            if (NanosuitMod.settings.nanosuitModesAtTheSameTime)
            {
                if (activeModes is null)
                {
                    activeModes = new Dictionary<ApparelMode, bool>();
                }
                if (activeModes.ContainsKey(apparelMode))
                {
                    activeModes[apparelMode] = !activeModes[apparelMode];
                }
                else
                {
                    activeModes[apparelMode] = true;
                }
            }
            else
            {
                if (activeMode == apparelMode)
                {
                    activeMode = ApparelMode.None;
                }
                else
                {
                    activeMode = apparelMode;
                }
            }
        }
        public override void ExposeData()
        {
            base.ExposeData();
            Scribe_Values.Look(ref energy, "energy");
            Scribe_Values.Look(ref energyBonus, "energyBonus");
            Scribe_Values.Look(ref activeMode, "activeMode");
            Scribe_Collections.Look(ref activeModes, "activeModes", LookMode.Value, LookMode.Value, ref modeKeys, ref boolValues);
            Scribe_Values.Look(ref jumpModeOutsideCombat, "jumpModeOutsideCombat");
            Scribe_Values.Look(ref jumpModeInCombat, "jumpModeInCombat");
            Scribe_Values.Look(ref symbiosisActive, "symbiosisActive");
            Scribe_Values.Look(ref nightVisorActive, "nightVisorActive");
            Scribe_Values.Look(ref skipTicks, "skipTicks");
        }

        private List<ApparelMode> modeKeys;
        private List<bool> boolValues;
    }

    public class NanosuitDef : ThingDef
    {
        public float energyRegenerationPerTick;
        public float maxEnergyAmount;
        public ArmorMode armorMode;
        public StrengthMode strengthMode;
        public SpeedMode speedMode;
        public CloakMode cloakMode;
        public TransceiverDevice transceiverDevice;
        public Symbiosis symbiosis;
        public HardRemoval hardRemoval;
        public PsychicWaveControl psychicWaveControl;
        public EnvironmentalControl environmentalControl;
        public NightVisor nightVisor;
        public bool rebreather;
    }

    [StaticConstructorOnStartup]
    public class Gizmo_NanosuitEnergyStatus : Gizmo
    {
        public Apparel_Nanosuit nanosuit;

        private static readonly Texture2D FullShieldBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.2f, 0.2f, 0.24f));

        private static readonly Texture2D EmptyShieldBarTex = SolidColorMaterials.NewSolidColorTexture(Color.clear);

        public Gizmo_NanosuitEnergyStatus()
        {
            order = -100f;
        }

        public override float GetWidth(float maxWidth)
        {
            return 140f;
        }

        public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth)
        {
            Rect rect = new Rect(topLeft.x, topLeft.y, GetWidth(maxWidth), 75f);
            Rect rect2 = rect.ContractedBy(6f);
            Widgets.DrawWindowBackground(rect);
            Rect rect3 = rect2;
            rect3.height = rect.height / 2f;
            Text.Font = GameFont.Tiny;
            Widgets.Label(rect3, nanosuit.LabelShortCap);
            Rect rect4 = rect2;
            rect4.yMin = rect2.y + rect2.height / 2f;
            float fillPercent = nanosuit.Energy / nanosuit.def.maxEnergyAmount;
            Widgets.FillableBar(rect4, fillPercent, FullShieldBarTex, EmptyShieldBarTex, doBorder: false);
            Text.Font = GameFont.Small;
            Text.Anchor = TextAnchor.MiddleCenter;
            Widgets.Label(rect4, nanosuit.Energy.ToString("F0") + " / " + nanosuit.def.maxEnergyAmount);
            Text.Anchor = TextAnchor.UpperLeft;
            return new GizmoResult(GizmoState.Clear);
        }
    }
}
