package dev.crucifixbeta.mod.modules.impl.combat;

import asm.accessors.IEntity;
import asm.accessors.ILivingEntity;
import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.ClientTickEvent;
import dev.crucifixbeta.api.events.impl.PacketEvent;
import dev.crucifixbeta.api.events.impl.RotationEvent;
import dev.crucifixbeta.api.utils.combat.CombatUtil;
import dev.crucifixbeta.api.utils.math.Animation;
import dev.crucifixbeta.api.utils.math.Easing;
import dev.crucifixbeta.api.utils.math.MathUtil;
import dev.crucifixbeta.api.utils.math.Timer;
import dev.crucifixbeta.api.utils.player.EntityUtil;
import dev.crucifixbeta.api.utils.player.InventoryUtil;
import dev.crucifixbeta.api.utils.render.ColorUtil;
import dev.crucifixbeta.api.utils.render.JelloUtil;
import dev.crucifixbeta.api.utils.render.Render3DUtil;
import dev.crucifixbeta.mod.features.SpearPort;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.exploit.Blink;
import dev.crucifixbeta.mod.modules.impl.movement.ElytraFly;
import dev.crucifixbeta.mod.modules.settings.enums.SwingSide;
import dev.crucifixbeta.mod.modules.settings.enums.Timing;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.ColorSetting;
import dev.crucifixbeta.mod.modules.settings.impl.EnumSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import java.awt.Color;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.decoration.EndCrystalEntity;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.mob.SlimeEntity;
import net.minecraft.entity.passive.AnimalEntity;
import net.minecraft.entity.passive.VillagerEntity;
import net.minecraft.entity.passive.WanderingTraderEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.ProjectileEntity;
import net.minecraft.item.AxeItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.MaceItem;
import net.minecraft.item.PickaxeItem;
import net.minecraft.item.SwordItem;
import net.minecraft.item.TridentItem;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket;
import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket;
import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket.InteractType;
import net.minecraft.util.Hand;
import net.minecraft.util.math.Vec3d;

@Environment(EnvType.CLIENT)
public class Aura extends Module {
   public static Aura INSTANCE;
   public static Entity target;
   public final EnumSetting<Aura.Page> page = this.add(new EnumSetting<>("Page", Aura.Page.General));
   public final SliderSetting range = this.add(new SliderSetting("Range", 6.0, 0.1F, 7.0, () -> this.page.getValue() == Aura.Page.General));
   private final SliderSetting targetRange = this.add(
      new SliderSetting("TargetRange", 8.0, 0.1F, 14.0, () -> this.page.getValue() == Aura.Page.General)
   );
   private final EnumSetting<Aura.Cooldown> cooldownMode = this.add(
      new EnumSetting<>("CooldownMode", Aura.Cooldown.Delay, () -> this.page.getValue() == Aura.Page.General)
   );
   private final BooleanSetting reset = this.add(
      new BooleanSetting("Reset", true, () -> this.page.getValue() == Aura.Page.General && this.cooldownMode.is(Aura.Cooldown.Delay))
   );
   private final EnumSetting<SwingSide> swingMode = this.add(
      new EnumSetting<>("Swing", SwingSide.All, () -> this.page.getValue() == Aura.Page.General)
   );
   private final SliderSetting hurtTime = this.add(
      new SliderSetting("HurtTime", 10.0, 0.0, 10.0, 1.0, () -> this.page.getValue() == Aura.Page.General)
   );
   private final SliderSetting cooldown = this.add(
      new SliderSetting("Cooldown", 1.1F, 0.0, 1.2F, 0.01, () -> this.page.getValue() == Aura.Page.General)
   );
   private final SliderSetting wallRange = this.add(
      new SliderSetting("WallRange", 6.0, 0.1F, 7.0, () -> this.page.getValue() == Aura.Page.General)
   );
   private final BooleanSetting whileEating = this.add(new BooleanSetting("WhileUsing", true, () -> this.page.getValue() == Aura.Page.General));
   private final BooleanSetting weaponOnly = this.add(new BooleanSetting("WeaponOnly", true, () -> this.page.getValue() == Aura.Page.General));
   private final EnumSetting<Aura.WeaponMode> weapon = this.add(
      new EnumSetting<>("Weapon", Aura.WeaponMode.Require, () -> this.page.getValue() == Aura.Page.General)
   );
   private final EnumSetting<Aura.SwapMode> swap = this.add(
      new EnumSetting<>(
         "Swap", Aura.SwapMode.Silent, () -> this.page.getValue() == Aura.Page.General && this.weapon.is(Aura.WeaponMode.Swap)
      )
   );
   private final BooleanSetting silentSwordSwitch = this.add(
      new BooleanSetting("SilentSwordSwitch", false, () -> this.page.getValue() == Aura.Page.General)
   );
   private final EnumSetting<Aura.MaceMode> mace = this.add(
      new EnumSetting<>(
         "Mace", Aura.MaceMode.Smart, () -> this.page.getValue() == Aura.Page.General && this.weapon.is(Aura.WeaponMode.Swap)
      )
   );
   private final BooleanSetting pauseEating = this.add(
      new BooleanSetting("PauseEating", true, () -> this.page.getValue() == Aura.Page.General && this.weapon.is(Aura.WeaponMode.Swap))
   );
   private final BooleanSetting tpsSync = this.add(new BooleanSetting("TPSSync", true, () -> this.page.getValue() == Aura.Page.General));
   private final BooleanSetting stopSprint = this.add(new BooleanSetting("StopSprint", true, () -> this.page.getValue() == Aura.Page.General));
   private final BooleanSetting grimReach = this.add(new BooleanSetting("Grim Reach", false, () -> this.page.getValue() == Aura.Page.General));
   private final BooleanSetting swordFix = this.add(new BooleanSetting("SwordFix", true, () -> this.page.getValue() == Aura.Page.General));
   private final EnumSetting<Timing> timing = this.add(
      new EnumSetting<>("Timing", Timing.All, () -> this.page.getValue() == Aura.Page.General)
   );
   private final BooleanSetting Players = this.add(
      new BooleanSetting("Players", true, () -> this.page.getValue() == Aura.Page.Target).setParent()
   );
   private final BooleanSetting armorLow = this.add(
      new BooleanSetting("ArmorLow", true, () -> this.page.getValue() == Aura.Page.Target && this.Players.isOpen())
   );
   private final BooleanSetting Mobs = this.add(new BooleanSetting("Mobs", true, () -> this.page.getValue() == Aura.Page.Target));
   private final BooleanSetting Animals = this.add(new BooleanSetting("Animals", true, () -> this.page.getValue() == Aura.Page.Target));
   private final BooleanSetting Villagers = this.add(new BooleanSetting("Villagers", true, () -> this.page.getValue() == Aura.Page.Target));
   private final BooleanSetting Slimes = this.add(new BooleanSetting("Slimes", true, () -> this.page.getValue() == Aura.Page.Target));
   private final BooleanSetting Crystals = this.add(new BooleanSetting("Crystals", false, () -> this.page.getValue() == Aura.Page.Target));
   private final BooleanSetting Projectiles = this.add(new BooleanSetting("Projectiles", false, () -> this.page.getValue() == Aura.Page.Target));
   private final BooleanSetting ignoreNakeds = this.add(new BooleanSetting("IgnoreNakeds", false, () -> this.page.getValue() == Aura.Page.Target));
   private final BooleanSetting ignoreNamed = this.add(new BooleanSetting("IgnoreNamed", false, () -> this.page.getValue() == Aura.Page.Target));
   private final BooleanSetting fovCheck = this.add(new BooleanSetting("FovCheck", false, () -> this.page.getValue() == Aura.Page.Target));
   private final EnumSetting<Aura.TargetMode> targetMode = this.add(
      new EnumSetting<>("Filter", Aura.TargetMode.DISTANCE, () -> this.page.getValue() == Aura.Page.Target)
   );
   private final EnumSetting<Aura.TargetESP> mode = this.add(
      new EnumSetting<>("TargetESP", Aura.TargetESP.Fill, () -> this.page.getValue() == Aura.Page.Render)
   );
   private final SliderSetting animationTime = this.add(
      new SliderSetting("AnimationTime", 200.0, 0.0, 2000.0, 1.0, () -> this.page.getValue() == Aura.Page.Render)
   );
   private final EnumSetting<Easing> ease = this.add(
      new EnumSetting<>("Ease", Easing.CubicInOut, () -> this.page.getValue() == Aura.Page.Render)
   );
   private final ColorSetting color = this.add(
      new ColorSetting("Color", new Color(255, 255, 255, 50), () -> this.page.getValue() == Aura.Page.Render)
   );
   private final ColorSetting outlineColor = this.add(
      new ColorSetting("OutlineColor", new Color(255, 255, 255, 50), () -> this.page.getValue() == Aura.Page.Render)
   );
   private final ColorSetting hitColor = this.add(
      new ColorSetting("HitColor", new Color(255, 255, 255, 150), () -> this.page.getValue() == Aura.Page.Render)
   );
   private final ColorSetting hitOutlineColor = this.add(
      new ColorSetting("HitOutlineColor", new Color(255, 255, 255, 150), () -> this.page.getValue() == Aura.Page.Render)
   );
   private final Animation animation = new Animation();
   private final BooleanSetting rotate = this.add(new BooleanSetting("Rotate", true, () -> this.page.getValue() == Aura.Page.Rotate));
   private final BooleanSetting yawStep = this.add(
      new BooleanSetting("YawStep", false, () -> this.rotate.isOpen() && this.page.getValue() == Aura.Page.Rotate).setParent()
   );
   private final BooleanSetting whenElytra = this.add(
      new BooleanSetting("FallFlying", true, () -> this.rotate.isOpen() && this.yawStep.isOpen() && this.page.getValue() == Aura.Page.Rotate)
   );
   private final SliderSetting steps = this.add(
      new SliderSetting("Steps", 0.05, 0.0, 1.0, 0.01, () -> this.page.getValue() == Aura.Page.Rotate && this.yawStep.isOpen())
   );
   private final BooleanSetting checkFov = this.add(
      new BooleanSetting("OnlyLooking", true, () -> this.page.getValue() == Aura.Page.Rotate && this.yawStep.isOpen())
   );
   private final SliderSetting fov = this.add(
      new SliderSetting(
         "Fov",
         20.0,
         0.0,
         360.0,
         0.1,
         () -> this.checkFov.getValue() && this.page.getValue() == Aura.Page.Rotate && this.yawStep.isOpen()
      )
   );
   private final SliderSetting priority = this.add(
      new SliderSetting("Priority", 10, 0, 100, () -> this.page.getValue() == Aura.Page.Rotate && this.yawStep.isOpen())
   );
   private final Timer tick = new Timer();
   private boolean swordSpoofIgnore = false;
   private int swapBackSlot = -1;
   private int silentSwordSlot = -1;
   public Vec3d directionVec = null;

   public Aura() {
      super("KillAura", Module.Category.Combat);
      this.setChinese("\u6740\u622e\u5149\u73af");
      INSTANCE = this;
   }

   public static void doRender(MatrixStack stack, float partialTicks, Entity entity, Color color, Color outlineColor, Aura.TargetESP mode) {
      switch (mode) {
         case Fill:
            Render3DUtil.draw3DBox(
               stack,
               ((IEntity)entity)
                  .getDimensions()
                  .getBoxAt(
                     new Vec3d(
                        MathUtil.interpolate(entity.lastRenderX, entity.getX(), (double)partialTicks),
                        MathUtil.interpolate(entity.lastRenderY, entity.getY(), (double)partialTicks),
                        MathUtil.interpolate(entity.lastRenderZ, entity.getZ(), (double)partialTicks)
                     )
                  )
                  .expand(0.0, 0.1, 0.0),
               color,
               outlineColor,
               false,
               true
            );
            break;
         case Box:
            Render3DUtil.draw3DBox(
               stack,
               ((IEntity)entity)
                  .getDimensions()
                  .getBoxAt(
                     new Vec3d(
                        MathUtil.interpolate(entity.lastRenderX, entity.getX(), (double)partialTicks),
                        MathUtil.interpolate(entity.lastRenderY, entity.getY(), (double)partialTicks),
                        MathUtil.interpolate(entity.lastRenderZ, entity.getZ(), (double)partialTicks)
                     )
                  )
                  .expand(0.0, 0.1, 0.0),
               color,
               outlineColor,
               true,
               true
            );
            break;
         case Jello:
            JelloUtil.drawJello(stack, entity, color);
            break;
         case ThunderHack:
            Render3DUtil.drawTargetEsp(stack, target, color);
      }
   }

   public static float getAttackCooldownProgressPerTick() {
      return (float)(1.0 / mc.player.getAttributeValue(EntityAttributes.GENERIC_ATTACK_SPEED) * 20.0);
   }

   @Override
   public void onDisable() {
      this.restoreSwapBack();
      this.restoreSilentSwordSwitch();
      target = null;
   }

   @Override
   public void onRender3D(MatrixStack matrixStack) {
      if (target != null) {
         this.doRender(matrixStack, mc.getRenderTickCounter().getTickDelta(true), target, this.mode.getValue());
      }
   }

   public void doRender(MatrixStack stack, float partialTicks, Entity entity, Aura.TargetESP mode) {
      switch (mode) {
         case Fill:
            Render3DUtil.draw3DBox(
               stack,
               ((IEntity)entity)
                  .getDimensions()
                  .getBoxAt(
                     new Vec3d(
                        MathUtil.interpolate(entity.lastRenderX, entity.getX(), (double)partialTicks),
                        MathUtil.interpolate(entity.lastRenderY, entity.getY(), (double)partialTicks),
                        MathUtil.interpolate(entity.lastRenderZ, entity.getZ(), (double)partialTicks)
                     )
                  )
                  .expand(0.0, 0.1, 0.0),
               ColorUtil.fadeColor(
                  this.color.getValue(), this.hitColor.getValue(), this.animation.get(0.0, (long)this.animationTime.getValueInt(), this.ease.getValue())
               ),
               ColorUtil.fadeColor(
                  this.outlineColor.getValue(),
                  this.hitOutlineColor.getValue(),
                  this.animation.get(0.0, (long)this.animationTime.getValueInt(), this.ease.getValue())
               ),
               false,
               true
            );
            break;
         case Box:
            Render3DUtil.draw3DBox(
               stack,
               ((IEntity)entity)
                  .getDimensions()
                  .getBoxAt(
                     new Vec3d(
                        MathUtil.interpolate(entity.lastRenderX, entity.getX(), (double)partialTicks),
                        MathUtil.interpolate(entity.lastRenderY, entity.getY(), (double)partialTicks),
                        MathUtil.interpolate(entity.lastRenderZ, entity.getZ(), (double)partialTicks)
                     )
                  )
                  .expand(0.0, 0.1, 0.0),
               ColorUtil.fadeColor(
                  this.color.getValue(), this.hitColor.getValue(), this.animation.get(0.0, (long)this.animationTime.getValueInt(), this.ease.getValue())
               ),
               ColorUtil.fadeColor(
                  this.outlineColor.getValue(),
                  this.hitOutlineColor.getValue(),
                  this.animation.get(0.0, (long)this.animationTime.getValueInt(), this.ease.getValue())
               ),
               true,
               true
            );
            break;
         case Jello:
            JelloUtil.drawJello(stack, entity, this.color.getValue());
            break;
         case ThunderHack:
            Render3DUtil.drawTargetEsp(stack, target, this.color.getValue());
      }
   }

   @Override
   public String getInfo() {
      return target == null ? null : target.getName().getString();
   }

   @EventListener
   public void onTick(ClientTickEvent event) {
      if (!nullCheck()) {
         if ((!this.timing.is(Timing.Pre) || !event.isPost()) && (!this.timing.is(Timing.Post) || !event.isPre())) {
            if (!this.silentSwordSwitch.getValue()) {
               this.restoreSilentSwordSwitch();
            }

            if (!this.hasRequiredWeapon()) {
               this.restoreSilentSwordSwitch();
               target = null;
            } else {
               target = this.getTarget((double)this.getAttackRange());
               if (target == null) {
                  this.restoreSilentSwordSwitch();
                  target = this.getTarget((double)this.targetRange.getValueFloat());
               } else {
                  this.doAura();
               }
            }
         }
      }
   }

   @EventListener
   public void onRotate(RotationEvent event) {
      if (target != null && this.rotate.getValue() && this.shouldYawStep()) {
         this.directionVec = this.getAttackVec(target);
         event.setTarget(this.directionVec, this.steps.getValueFloat(), this.priority.getValueFloat());
      }
   }

   @EventListener
   public void onPacket(PacketEvent.Send event) {
      Packet var2 = event.getPacket();
      if (this.reset.getValue()
         && (
            var2 instanceof HandSwingC2SPacket
               || var2 instanceof PlayerInteractEntityC2SPacket && Criticals.getInteractType((PlayerInteractEntityC2SPacket)var2) == InteractType.ATTACK
         )) {
         this.tick.reset();
      }

      if (!this.swordSpoofIgnore
         && this.swordFix.getValue()
         && var2 instanceof PlayerInteractEntityC2SPacket var3
         && Criticals.getInteractType(var3) == InteractType.ATTACK) {
         int var4 = InventoryUtil.findItem(Items.NETHERITE_SWORD);
         if (var4 == -1) {
            return;
         }

         event.cancel();
         this.swordSpoofIgnore = true;

         try {
            this.doSwordSpoof(var3, var4);
         } finally {
            this.swordSpoofIgnore = false;
         }

         return;
      }
   }

   private boolean check() {
      if (AutoCrystal.INSTANCE != null && AutoCrystal.INSTANCE.shouldPauseKillAura()) {
         return false;
      } else if (Blink.INSTANCE.isOn() && Blink.INSTANCE.pauseModule.getValue()) {
         return false;
      } else {
         int var1 = (int)(this.tick.getMs() / 50L);
         if (this.cooldownMode.getValue() == Aura.Cooldown.Vanilla) {
            var1 = ((ILivingEntity)mc.player).getLastAttackedTicks();
         }

         if (this.tpsSync.getValue()) {
            var1 = (int)((float)var1 * CrucifixBeta.SERVER.getTPSFactor());
         }

         if (!((double)Math.max((float)var1 / this.getAttackCooldownProgressPerTickForAura(), 0.0F) >= this.cooldown.getValue())) {
            return false;
         } else {
            if (target instanceof LivingEntity var3 && (double)var3.hurtTime > this.hurtTime.getValue()) {
               return false;
            }

            return !this.isPausedByUsingItemOwner() && !this.isHandItemPauseActive() ? this.whileEating.getValue() || !this.shouldPauseForUsingItem() : false;
         }
      }
   }

   private boolean isHandItemPauseActive() {
      return MainHand.INSTANCE != null && MainHand.INSTANCE.isUsingItemPauseActive() || Offhand.INSTANCE != null && Offhand.INSTANCE.isUsingItemPauseActive();
   }

   private void doAura() {
      if (this.check()) {
         if (!this.isAttackInRange(target)) {
            this.restoreSwapBack();
            this.restoreSilentSwordSwitch();
         } else if (!this.rotate.getValue() || this.faceVector(this.getAttackVec(target))) {
            this.animation.to = 1.0;
            this.animation.from = 1.0;
            if (this.tryUseCombatTask()) {
               this.sendOpenMioAttack();
               if (this.rotate.getValue() && !this.shouldYawStep()) {
                  CrucifixBeta.ROTATION.snapBack();
               }
            }
         }
      }
   }

   private void sendOpenMioAttack() {
      int var1 = mc.player.getInventory().selectedSlot;
      int var2 = this.findSwordSlot();
      boolean var3 = this.silentSwordSwitch.getValue() && var2 != -1;
      int var4 = var3 ? var2 : this.findAttackSlot();
      boolean var5 = !var3 && this.weapon.is(Aura.WeaponMode.Swap) && var4 != -1 && var4 != var1;
      if (!var5
         || !this.pauseEating.getValue()
         || !mc.player.isUsingItem()
         || mc.player.getActiveHand() != Hand.MAIN_HAND
         || this.swap.getValue() == Aura.SwapMode.Silent) {
         boolean var6 = this.swordSpoofIgnore;
         boolean var7 = mc.player.isSprinting();
         if (var7 && this.stopSprint.getValue()) {
            mc.getNetworkHandler().sendPacket(new ClientCommandC2SPacket(mc.player, ClientCommandC2SPacket.Mode.STOP_SPRINTING));
         }

         try {
            if (var3) {
               this.spoofSilentSwordSwitch(var2);
            } else if (var5) {
               if (this.swap.getValue() == Aura.SwapMode.Silent) {
                  mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(var4));
               } else {
                  InventoryUtil.switchToSlot(var4);
                  if (this.swap.getValue() == Aura.SwapMode.SwapBack) {
                     this.swapBackSlot = var1;
                  }
               }
            }

            this.swordSpoofIgnore = true;
            mc.getNetworkHandler().sendPacket(PlayerInteractEntityC2SPacket.attack(target, mc.player.isSneaking()));
            mc.player.resetLastAttackedTicks();
            EntityUtil.swingHand(Hand.MAIN_HAND, this.swingMode.getValue());
            this.tick.reset();
         } finally {
            this.swordSpoofIgnore = var6;
            if (!var3 && var5 && this.swap.getValue() == Aura.SwapMode.Silent) {
               mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(var1));
            } else if (!var3 && var5 && this.swap.getValue() == Aura.SwapMode.SwapBack) {
               this.restoreSwapBack();
            }

            if (var7 && this.stopSprint.getValue()) {
               mc.getNetworkHandler().sendPacket(new ClientCommandC2SPacket(mc.player, ClientCommandC2SPacket.Mode.START_SPRINTING));
            }
         }
      }
   }

   private void sendAttackWithWeaponFix() {
      int var1 = this.swordFix.getValue() ? InventoryUtil.findItem(Items.NETHERITE_SWORD) : -1;
      if (var1 != -1) {
         mc.getNetworkHandler().sendPacket(PlayerInteractEntityC2SPacket.attack(target, mc.player.isSneaking()));
      } else {
         int var2 = this.grimReach.getValue() ? SpearPort.findSpearHotbarSlot() : -1;
         if (var2 != -1) {
            int var3 = mc.player.getInventory().selectedSlot;

            try {
               InventoryUtil.switchToSlot(var2);
               mc.getNetworkHandler().sendPacket(PlayerInteractEntityC2SPacket.attack(target, mc.player.isSneaking()));
               mc.player.resetLastAttackedTicks();
               EntityUtil.swingHand(Hand.MAIN_HAND, this.swingMode.getValue());
               this.tick.reset();
            } finally {
               InventoryUtil.switchToSlot(var3);
            }
         } else {
            mc.getNetworkHandler().sendPacket(PlayerInteractEntityC2SPacket.attack(target, mc.player.isSneaking()));
         }
      }
   }

   private void doSwordSpoof(PlayerInteractEntityC2SPacket packet, int swordSlot) {
      int var3 = mc.player.getInventory().selectedSlot;

      try {
         if (swordSlot != var3) {
            mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(swordSlot));
         }

         mc.getNetworkHandler().sendPacket(packet);
         mc.player.resetLastAttackedTicks();
         EntityUtil.swingHand(Hand.MAIN_HAND, this.swingMode.getValue());
         this.tick.reset();
      } finally {
         if (swordSlot != var3) {
            mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(var3));
         }
      }
   }

   private float getAttackCooldownProgressPerTickForAura() {
      return this.hasSwordAttackSpeedSpoof() ? 12.5F : getAttackCooldownProgressPerTick();
   }

   private float getAttackRange() {
      return this.grimReach.getValue() ? 4.5F : this.range.getValueFloat();
   }

   private boolean hasRequiredWeapon() {
      if (!this.weaponOnly.getValue() && this.weapon.is(Aura.WeaponMode.Any)) {
         return true;
      } else if (this.canUseGrimReach() || this.canUseSwordFix()) {
         return true;
      } else if (this.canUseSilentSwordSwitch()) {
         return true;
      } else {
         return this.isWeapon(mc.player.getMainHandStack().getItem()) ? true : this.weapon.is(Aura.WeaponMode.Swap) && this.findAttackSlot() != -1;
      }
   }

   private boolean canUseGrimReach() {
      return this.grimReach.getValue() && (SpearPort.isSpear(mc.player.getMainHandStack()) || SpearPort.findSpearHotbarSlot() != -1);
   }

   private boolean canUseSwordFix() {
      return this.swordFix.getValue() && InventoryUtil.findItem(Items.NETHERITE_SWORD) != -1;
   }

   private boolean canUseSilentSwordSwitch() {
      return this.silentSwordSwitch.getValue() && this.findSwordSlot() != -1;
   }

   private boolean hasNetheriteSwordCooldownFix() {
      return this.swordFix.getValue() && InventoryUtil.findItem(Items.NETHERITE_SWORD) != -1;
   }

   private boolean hasSwordAttackSpeedSpoof() {
      return this.hasNetheriteSwordCooldownFix() || this.canUseSilentSwordSwitch();
   }

   private int findAttackSlot() {
      if (this.silentSwordSwitch.getValue()) {
         int var1 = this.findSwordSlot();
         if (var1 != -1) {
            return var1;
         }
      }

      int var8 = this.findMaceSlot();
      if (var8 != -1) {
         return var8;
      } else {
         int var2 = this.grimReach.getValue() ? SpearPort.findSpearHotbarSlot() : -1;
         if (var2 != -1) {
            return var2;
         } else {
            int var3 = this.swordFix.getValue() ? InventoryUtil.findItem(Items.NETHERITE_SWORD) : -1;
            if (var3 != -1) {
               return var3;
            } else {
               int var4 = InventoryUtil.findClass(SwordItem.class);
               if (var4 != -1) {
                  return var4;
               } else {
                  int var5 = InventoryUtil.findClass(AxeItem.class);
                  if (var5 != -1) {
                     return var5;
                  } else {
                     int var6 = InventoryUtil.findClass(TridentItem.class);
                     if (var6 != -1) {
                        return var6;
                     } else {
                        int var7 = InventoryUtil.findClass(MaceItem.class);
                        return var7 != -1 ? var7 : InventoryUtil.findClass(PickaxeItem.class);
                     }
                  }
               }
            }
         }
      }
   }

   private int findSwordSlot() {
      int var1 = InventoryUtil.findItem(Items.NETHERITE_SWORD);
      return var1 != -1 ? var1 : InventoryUtil.findClass(SwordItem.class);
   }

   private void spoofSilentSwordSwitch(int swordSlot) {
      if (swordSlot != -1 && mc.getNetworkHandler() != null) {
         mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(swordSlot));
         this.silentSwordSlot = swordSlot;
      }
   }

   private void restoreSilentSwordSwitch() {
      if (this.silentSwordSlot != -1 && mc.player != null && mc.getNetworkHandler() != null) {
         int var1 = mc.player.getInventory().selectedSlot;
         if (var1 != this.silentSwordSlot) {
            mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(var1));
         }

         this.silentSwordSlot = -1;
      } else {
         this.silentSwordSlot = -1;
      }
   }

   private int findMaceSlot() {
      if (this.mace.is(Aura.MaceMode.Off)) {
         return -1;
      } else {
         return this.mace.is(Aura.MaceMode.Smart) && mc.player.fallDistance <= 2.5F ? -1 : InventoryUtil.findItem(Items.MACE);
      }
   }

   private boolean isWeapon(Item item) {
      return item instanceof SwordItem
         || item instanceof AxeItem
         || item instanceof MaceItem
         || item instanceof TridentItem
         || item instanceof PickaxeItem && InventoryUtil.findClass(SwordItem.class) == -1 && InventoryUtil.findClass(AxeItem.class) == -1;
   }

   private boolean isAttackInRange(Entity entity) {
      if (entity == null) {
         return false;
      } else {
         Vec3d var2 = this.getAttackVec(entity);
         float var3 = this.getAttackRange();
         return mc.player.getEyePos().distanceTo(var2) <= (double)var3
            || !mc.player.canSee(entity) && mc.player.getEyePos().distanceTo(var2) <= this.wallRange.getValue();
      }
   }

   private void restoreSwapBack() {
      if (this.swapBackSlot != -1 && mc.player != null) {
         InventoryUtil.switchToSlot(this.swapBackSlot);
         this.swapBackSlot = -1;
      } else {
         this.swapBackSlot = -1;
      }
   }

   private Vec3d getAttackVec(Entity entity) {
      return MathUtil.getClosestPointToBox(mc.player.getEyePos(), entity.getBoundingBox());
   }

   private boolean shouldYawStep() {
      return this.whenElytra.getValue() || !mc.player.isFallFlying() && (!ElytraFly.INSTANCE.isOn() || !ElytraFly.INSTANCE.isFallFlying())
         ? this.yawStep.getValue()
         : false;
   }

   public boolean faceVector(Vec3d directionVec) {
      if (!this.shouldYawStep()) {
         CrucifixBeta.ROTATION.lookAt(directionVec);
         return true;
      } else {
         this.directionVec = directionVec;
         return CrucifixBeta.ROTATION.inFov(directionVec, this.fov.getValueFloat()) ? true : !this.checkFov.getValue();
      }
   }

   public Entity getTarget(double range) {
      Entity var3 = null;
      double var4 = range;
      double var6 = 36.0;

      for (Entity var9 : CrucifixBeta.THREAD.getEntities()) {
         if (this.isEnemy(var9)) {
            Vec3d var10 = this.getAttackVec(var9);
            if (!(mc.player.getEyePos().distanceTo(var10) > range)
               && (mc.player.canSee(var9) || !(mc.player.getEyePos().distanceTo(var10) > this.wallRange.getValue()))
               && CombatUtil.isValid(var9)
               && (!this.fovCheck.getValue() || CrucifixBeta.ROTATION.inFov(var10, this.fov.getValueFloat()))) {
               if (var3 == null) {
                  var3 = var9;
                  var4 = mc.player.getEyePos().distanceTo(var10);
                  var6 = (double)EntityUtil.getHealth(var9);
               } else {
                  if (this.armorLow.getValue() && var9 instanceof PlayerEntity && EntityUtil.isArmorLow((PlayerEntity)var9, 10)) {
                     var3 = var9;
                     break;
                  }

                  if (this.targetMode.getValue() == Aura.TargetMode.HEALTH && (double)EntityUtil.getHealth(var9) < var6) {
                     var3 = var9;
                     var6 = (double)EntityUtil.getHealth(var9);
                  } else if (this.targetMode.getValue() == Aura.TargetMode.DISTANCE && mc.player.getEyePos().distanceTo(var10) < var4) {
                     var3 = var9;
                     var4 = mc.player.getEyePos().distanceTo(var10);
                  }
               }
            }
         }
      }

      return var3;
   }

   private boolean isEnemy(Entity entity) {
      if (entity == null || entity == mc.player) {
         return false;
      } else if (this.ignoreNamed.getValue() && !(entity instanceof PlayerEntity) && entity.hasCustomName()) {
         return false;
      } else if (entity instanceof EndCrystalEntity) {
         return this.Crystals.getValue();
      } else if (entity instanceof ProjectileEntity) {
         return this.Projectiles.getValue();
      } else if (entity instanceof SlimeEntity) {
         return this.Slimes.getValue();
      } else if (entity instanceof PlayerEntity) {
         return this.ignoreNakeds.getValue() && isNaked((PlayerEntity)entity) ? false : this.Players.getValue();
      } else if (entity instanceof VillagerEntity || entity instanceof WanderingTraderEntity) {
         return this.Villagers.getValue();
      } else if (entity instanceof AnimalEntity) {
         return this.Animals.getValue();
      } else {
         return entity instanceof MobEntity ? this.Mobs.getValue() : false;
      }
   }

   private static boolean isNaked(PlayerEntity player) {
      for (ItemStack var2 : player.getArmorItems()) {
         if (!var2.isEmpty()) {
            return false;
         }
      }

      return true;
   }
@Environment(EnvType.CLIENT)
   public static enum Cooldown {
      Vanilla,
      Delay;

}

   @Environment(EnvType.CLIENT)
   public static enum MaceMode {
      Off,
      Always,
      Smart;

}

   @Environment(EnvType.CLIENT)
   public static enum Mode {
      Mace,
      Axe,
      Sword;

}

   @Environment(EnvType.CLIENT)
   public static enum Page {
      General,
      Rotate,
      Target,
      Render;

}

   @Environment(EnvType.CLIENT)
   public static enum SwapMode {
      Normal,
      Silent,
      SwapBack;

}

   @Environment(EnvType.CLIENT)
   public static enum TargetESP {
      Fill,
      Box,
      Jello,
      ThunderHack,
      None;

}

   @Environment(EnvType.CLIENT)
   private static enum TargetMode {
      DISTANCE,
      HEALTH;

}

   @Environment(EnvType.CLIENT)
   public static enum WeaponMode {
      Require,
      Swap,
      Any;

}
}
