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

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.ClientTickEvent;
import dev.crucifixbeta.api.events.impl.Render3DEvent;
import dev.crucifixbeta.api.events.impl.RotationEvent;
import dev.crucifixbeta.api.utils.combat.CombatUtil;
import dev.crucifixbeta.api.utils.entity.PlayerEntityPredict;
import dev.crucifixbeta.api.utils.math.AnimateUtil;
import dev.crucifixbeta.api.utils.math.ExplosionUtil;
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.world.BlockUtil;
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.impl.player.AirPlace;
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 java.util.ArrayList;
import java.util.List;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.Blocks;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult.Type;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RaycastContext;
import net.minecraft.world.World;
import net.minecraft.world.RaycastContext.FluidHandling;
import net.minecraft.world.RaycastContext.ShapeType;

@Environment(EnvType.CLIENT)
public class AutoAnchor extends Module {
   public static AutoAnchor INSTANCE;
   static Vec3d placeVec3d;
   static Vec3d curVec3d;
   public final EnumSetting<AutoAnchor.Page> page = this.add(new EnumSetting<>("Page", AutoAnchor.Page.General));
   public final SliderSetting range = this.add(
      new SliderSetting("Range", 5.0, 0.0, 6.0, 0.1, () -> this.page.getValue() == AutoAnchor.Page.General)
         .setSuffix("m")
   );
   public final SliderSetting targetRange = this.add(
      new SliderSetting("TargetRange", 8.0, 0.1, 12.0, 0.1, () -> this.page.getValue() == AutoAnchor.Page.General)
         .setSuffix("m")
   );
   public final SliderSetting minDamage = this.add(
      new SliderSetting("Min", 4.0, 0.0, 36.0, 0.1, () -> this.page.getValue() == AutoAnchor.Page.Interact)
         .setSuffix("dmg")
   );
   public final SliderSetting breakMin = this.add(
      new SliderSetting("ExplosionMin", 4.0, 0.0, 36.0, 0.1, () -> this.page.getValue() == AutoAnchor.Page.Interact)
         .setSuffix("dmg")
   );
   public final SliderSetting headDamage = this.add(
      new SliderSetting("ForceHead", 7.0, 0.0, 36.0, 0.1, () -> this.page.getValue() == AutoAnchor.Page.Interact)
         .setSuffix("dmg")
   );
   private final SliderSetting selfPredict = this.add(
      new SliderSetting("SelfPredict", 4, 0, 10, () -> this.page.getValue() == AutoAnchor.Page.Predict).setSuffix("ticks")
   );
   private final SliderSetting predictTicks = this.add(
      new SliderSetting("Predict", 4, 0, 10, () -> this.page.getValue() == AutoAnchor.Page.Predict).setSuffix("ticks")
   );
   private final SliderSetting simulation = this.add(
      new SliderSetting("Simulation", 5.0, 0.0, 20.0, 1.0, () -> this.page.getValue() == AutoAnchor.Page.Predict)
   );
   private final SliderSetting maxMotionY = this.add(
      new SliderSetting("MaxMotionY", 0.34, 0.0, 2.0, 0.01, () -> this.page.getValue() == AutoAnchor.Page.Predict)
   );
   private final BooleanSetting step = this.add(new BooleanSetting("Step", false, () -> this.page.getValue() == AutoAnchor.Page.Predict));
   private final BooleanSetting doubleStep = this.add(
      new BooleanSetting("DoubleStep", false, () -> this.page.getValue() == AutoAnchor.Page.Predict)
   );
   private final BooleanSetting jump = this.add(new BooleanSetting("Jump", false, () -> this.page.getValue() == AutoAnchor.Page.Predict));
   private final BooleanSetting inBlockPause = this.add(
      new BooleanSetting("InBlockPause", true, () -> this.page.getValue() == AutoAnchor.Page.Predict)
   );
   final ArrayList<BlockPos> chargeList = new ArrayList<>();
   private final BooleanSetting assist = this.add(new BooleanSetting("Assist", true, () -> this.page.getValue() == AutoAnchor.Page.Assist));
   private final BooleanSetting obsidian = this.add(new BooleanSetting("Obsidian", true, () -> this.page.getValue() == AutoAnchor.Page.Assist));
   private final BooleanSetting checkMine = this.add(
      new BooleanSetting("DetectMining", false, () -> this.page.getValue() == AutoAnchor.Page.Assist)
   );
   private final SliderSetting assistRange = this.add(
      new SliderSetting("AssistRange", 5.0, 0.0, 6.0, 0.1, () -> this.page.getValue() == AutoAnchor.Page.Assist)
         .setSuffix("m")
   );
   private final SliderSetting assistDamage = this.add(
      new SliderSetting("AssistDamage", 6.0, 0.0, 36.0, 0.1, () -> this.page.getValue() == AutoAnchor.Page.Assist)
         .setSuffix("h")
   );
   private final SliderSetting delay = this.add(
      new SliderSetting("AssistDelay", 0.1, 0.0, 1.0, 0.01, () -> this.page.getValue() == AutoAnchor.Page.Assist)
         .setSuffix("s")
   );
   private final BooleanSetting preferCrystal = this.add(
      new BooleanSetting("PreferCrystal", false, () -> this.page.getValue() == AutoAnchor.Page.General)
   );
   private final BooleanSetting thread = this.add(new BooleanSetting("Thread", false, () -> this.page.getValue() == AutoAnchor.Page.General));
   private final BooleanSetting light = this.add(new BooleanSetting("LessCPU", true, () -> this.page.getValue() == AutoAnchor.Page.General));
   private final BooleanSetting inventorySwap = this.add(
      new BooleanSetting("InventorySwap", true, () -> this.page.getValue() == AutoAnchor.Page.General)
   );
   private final BooleanSetting breakCrystal = this.add(
      new BooleanSetting("BreakCrystal", true, () -> this.page.getValue() == AutoAnchor.Page.General)
   );
   private final BooleanSetting spam = this.add(
      new BooleanSetting("Spam", true, () -> this.page.getValue() == AutoAnchor.Page.General).setParent()
   );
   private final BooleanSetting mineSpam = this.add(
      new BooleanSetting("OnlyMining", true, () -> this.page.getValue() == AutoAnchor.Page.General && this.spam.isOpen())
   );
   private final BooleanSetting spamPlace = this.add(
      new BooleanSetting("Fast", true, () -> this.page.getValue() == AutoAnchor.Page.General).setParent()
   );
   private final BooleanSetting inSpam = this.add(
      new BooleanSetting("WhenSpamming", true, () -> this.page.getValue() == AutoAnchor.Page.General && this.spamPlace.isOpen())
   );
   private final BooleanSetting usingPause = this.add(
      new BooleanSetting("UsingPause", true, () -> this.page.getValue() == AutoAnchor.Page.General)
   );
   private final EnumSetting<SwingSide> swingMode = this.add(
      new EnumSetting<>("Swing", SwingSide.All, () -> this.page.getValue() == AutoAnchor.Page.General)
   );
   private final EnumSetting<Timing> timing = this.add(
      new EnumSetting<>("Timing", Timing.All, () -> this.page.getValue() == AutoAnchor.Page.General)
   );
   private final SliderSetting placeDelay = this.add(
      new SliderSetting("PlaceDelay", 100.0, 0.0, 500.0, 1.0, () -> this.page.getValue() == AutoAnchor.Page.General)
         .setSuffix("ms")
   );
   private final SliderSetting fillDelay = this.add(
      new SliderSetting("FillDelay", 100.0, 0.0, 500.0, 1.0, () -> this.page.getValue() == AutoAnchor.Page.General)
         .setSuffix("ms")
   );
   private final SliderSetting breakDelay = this.add(
      new SliderSetting("BreakDelay", 100.0, 0.0, 500.0, 1.0, () -> this.page.getValue() == AutoAnchor.Page.General)
         .setSuffix("ms")
   );
   private final SliderSetting spamDelay = this.add(
      new SliderSetting("SpamDelay", 200.0, 0.0, 1000.0, 1.0, () -> this.page.getValue() == AutoAnchor.Page.General)
         .setSuffix("ms")
   );
   private final SliderSetting updateDelay = this.add(
      new SliderSetting("UpdateDelay", 200.0, 0.0, 1000.0, 1.0, () -> this.page.getValue() == AutoAnchor.Page.General)
         .setSuffix("ms")
   );
   private final BooleanSetting rotate = this.add(
      new BooleanSetting("Rotate", true, () -> this.page.getValue() == AutoAnchor.Page.Rotate).setParent()
   );
   private final BooleanSetting yawStep = this.add(
      new BooleanSetting("YawStep", true, () -> this.rotate.isOpen() && this.page.getValue() == AutoAnchor.Page.Rotate).setParent()
   );
   private final BooleanSetting whenElytra = this.add(
      new BooleanSetting(
         "FallFlying", true, () -> this.rotate.isOpen() && this.yawStep.isOpen() && this.page.getValue() == AutoAnchor.Page.Rotate
      )
   );
   private final SliderSetting steps = this.add(
      new SliderSetting(
         "Steps",
         0.05,
         0.0,
         1.0,
         0.01,
         () -> this.rotate.isOpen() && this.yawStep.isOpen() && this.page.getValue() == AutoAnchor.Page.Rotate
      )
   );
   private final BooleanSetting checkFov = this.add(
      new BooleanSetting(
         "OnlyLooking", true, () -> this.rotate.isOpen() && this.yawStep.isOpen() && this.page.getValue() == AutoAnchor.Page.Rotate
      )
   );
   private final SliderSetting fov = this.add(
      new SliderSetting(
         "Fov",
         20.0,
         0.0,
         360.0,
         0.1,
         () -> this.rotate.isOpen() && this.yawStep.isOpen() && this.checkFov.getValue() && this.page.getValue() == AutoAnchor.Page.Rotate
      )
   );
   private final SliderSetting priority = this.add(
      new SliderSetting(
         "Priority", 10, 0, 100, () -> this.rotate.isOpen() && this.yawStep.isOpen() && this.page.getValue() == AutoAnchor.Page.Rotate
      )
   );
   private final BooleanSetting noSuicide = this.add(
      new BooleanSetting("NoSuicide", true, () -> this.page.getValue() == AutoAnchor.Page.Interact)
   );
   private final BooleanSetting smart = this.add(new BooleanSetting("Smart", true, () -> this.page.getValue() == AutoAnchor.Page.Interact));
   private final BooleanSetting terrainIgnore = this.add(
      new BooleanSetting("TerrainIgnore", true, () -> this.page.getValue() == AutoAnchor.Page.Interact)
   );
   private final SliderSetting minPrefer = this.add(
      new SliderSetting("Prefer", 7.0, 0.0, 36.0, 0.1, () -> this.page.getValue() == AutoAnchor.Page.Interact)
         .setSuffix("dmg")
   );
   private final SliderSetting maxSelfDamage = this.add(
      new SliderSetting("MaxSelf", 8.0, 0.0, 36.0, 0.1, () -> this.page.getValue() == AutoAnchor.Page.Interact)
         .setSuffix("dmg")
   );
   private final EnumSetting<Aura.TargetESP> mode = this.add(
      new EnumSetting<>("TargetESP", Aura.TargetESP.Jello, () -> this.page.getValue() == AutoAnchor.Page.Render)
   );
   private final ColorSetting color = this.add(
      new ColorSetting("TargetColor", new Color(255, 255, 255, 250), () -> this.page.getValue() == AutoAnchor.Page.Render)
   );
   private final ColorSetting outlineColor = this.add(
      new ColorSetting("TargetOutlineColor", new Color(255, 255, 255, 250), () -> this.page.getValue() == AutoAnchor.Page.Render)
   );
   private final BooleanSetting render = this.add(new BooleanSetting("Render", true, () -> this.page.getValue() == AutoAnchor.Page.Render));
   private final BooleanSetting shrink = this.add(
      new BooleanSetting("Shrink", true, () -> this.page.getValue() == AutoAnchor.Page.Render && this.render.getValue())
   );
   private final ColorSetting box = this.add(
      new ColorSetting(
            "Box", new Color(255, 255, 255, 255), () -> this.page.getValue() == AutoAnchor.Page.Render && this.render.getValue()
         )
         .injectBoolean(true)
   );
   private final ColorSetting fill = this.add(
      new ColorSetting(
            "Fill", new Color(255, 255, 255, 100), () -> this.page.getValue() == AutoAnchor.Page.Render && this.render.getValue()
         )
         .injectBoolean(true)
   );
   private final SliderSetting sliderSpeed = this.add(
      new SliderSetting("SliderSpeed", 0.2, 0.0, 1.0, 0.01, () -> this.page.getValue() == AutoAnchor.Page.Render && this.render.getValue())
   );
   private final SliderSetting startFadeTime = this.add(
      new SliderSetting("StartFade", 0.3, 0.0, 2.0, 0.01, () -> this.page.getValue() == AutoAnchor.Page.Render && this.render.getValue())
         .setSuffix("s")
   );
   private final SliderSetting fadeSpeed = this.add(
      new SliderSetting("FadeSpeed", 0.2, 0.01, 1.0, 0.01, () -> this.page.getValue() == AutoAnchor.Page.Render && this.render.getValue())
   );
   private final Timer delayTimer = new Timer();
   private final Timer calcTimer = new Timer();
   private final Timer noPosTimer = new Timer();
   private final Timer assistTimer = new Timer();
   public Vec3d directionVec = null;
   public PlayerEntity displayTarget;
   public BlockPos currentPos;
   public BlockPos tempPos;
   public double lastDamage;
   double fade = 0.0;
   BlockPos assistPos;

   public AutoAnchor() {
      super("AutoAnchor", Module.Category.Combat);
      this.setChinese("\u91cd\u751f\u951a\u5149\u73af");
      INSTANCE = this;
      CrucifixBeta.EVENT_BUS.subscribe(new AutoAnchor.AnchorRender());
   }

   @Override
   public void onEnable() {
      placeVec3d = null;
      curVec3d = null;
   }

   public static boolean canSee(Vec3d from, Vec3d to) {
      BlockHitResult var2 = mc.world.raycast(new RaycastContext(from, to, ShapeType.COLLIDER, FluidHandling.NONE, mc.player));
      return var2 == null || var2.getType() == Type.MISS;
   }

   @Override
   public String getInfo() {
      return this.displayTarget != null && this.currentPos != null ? this.displayTarget.getName().getString() : null;
   }

   @Override
   public void onRender3D(MatrixStack matrixStack) {
      if (this.displayTarget != null && this.currentPos != null) {
         Aura.doRender(
            matrixStack,
            mc.getRenderTickCounter().getTickDelta(true),
            this.displayTarget,
            this.color.getValue(),
            this.outlineColor.getValue(),
            this.mode.getValue()
         );
      }
   }

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

   @Override
   public void onDisable() {
      this.tempPos = null;
      this.currentPos = null;
      placeVec3d = null;
      curVec3d = null;
   }

   public void onThread() {
      if (!this.isOff() && !nullCheck()) {
         if (mc.world != null && mc.world.getRegistryKey() == World.NETHER) {
            this.currentPos = null;
         } else {
            if (this.thread.getValue()) {
               if (Blink.INSTANCE.isOn() && Blink.INSTANCE.pauseModule.getValue()) {
                  this.currentPos = null;
                  return;
               }

               if (AutoCrystal.INSTANCE.isOn() && AutoCrystal.INSTANCE.crystalPos != null && this.preferCrystal.getValue()) {
                  this.currentPos = null;
                  return;
               }

               int var1 = this.inventorySwap.getValue()
                  ? InventoryUtil.findBlockInventorySlot(Blocks.RESPAWN_ANCHOR)
                  : InventoryUtil.findBlock(Blocks.RESPAWN_ANCHOR);
               int var2 = this.inventorySwap.getValue() ? InventoryUtil.findBlockInventorySlot(Blocks.GLOWSTONE) : InventoryUtil.findBlock(Blocks.GLOWSTONE);
               int var3 = this.inventorySwap.getValue() ? var1 : InventoryUtil.findUnBlock();
               if (var1 == -1) {
                  this.currentPos = null;
                  return;
               }

               if (var2 == -1) {
                  this.currentPos = null;
                  return;
               }

               if (var3 == -1) {
                  this.currentPos = null;
                  return;
               }

               if (mc.player.isSneaking()) {
                  this.currentPos = null;
                  return;
               }

               if (this.usingPause.getValue() && this.shouldPauseForUsingItem()) {
                  this.currentPos = null;
                  return;
               }

               this.calc();
            }
         }
      }
   }

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

   @EventListener
   public void onTick(ClientTickEvent event) {
      if (!nullCheck()) {
         if (mc.world != null && mc.world.getRegistryKey() == World.NETHER) {
            this.currentPos = null;
         } else if ((!this.timing.is(Timing.Pre) || !event.isPost()) && (!this.timing.is(Timing.Post) || !event.isPre())) {
            int var2 = this.inventorySwap.getValue()
               ? InventoryUtil.findBlockInventorySlot(Blocks.RESPAWN_ANCHOR)
               : InventoryUtil.findBlock(Blocks.RESPAWN_ANCHOR);
            int var3 = this.inventorySwap.getValue() ? InventoryUtil.findBlockInventorySlot(Blocks.GLOWSTONE) : InventoryUtil.findBlock(Blocks.GLOWSTONE);
            int var4 = this.inventorySwap.getValue() ? var2 : InventoryUtil.findUnBlock();
            int var5 = mc.player.getInventory().selectedSlot;
            if (Blink.INSTANCE.isOn() && Blink.INSTANCE.pauseModule.getValue()) {
               this.currentPos = null;
            } else if (AutoCrystal.INSTANCE.isOn() && AutoCrystal.INSTANCE.crystalPos != null) {
               this.currentPos = null;
            } else if (var2 == -1) {
               this.currentPos = null;
            } else if (var3 == -1) {
               this.currentPos = null;
            } else if (var4 == -1) {
               this.currentPos = null;
            } else if (mc.player.isSneaking()) {
               this.currentPos = null;
            } else if (this.usingPause.getValue() && this.shouldPauseForUsingItem()) {
               this.currentPos = null;
            } else if (!this.inventorySwap.getValue() || EntityUtil.inInventory()) {
               if (this.assist.getValue()) {
                  this.onAssist();
               }

               if (!this.thread.getValue()) {
                  this.calc();
               }

               BlockPos var6 = this.currentPos;
               if (this.currentPos != null) {
                  if (!this.tryUseCombatTask()) {
                     return;
                  }

                  if (this.breakCrystal.getValue()) {
                     CombatUtil.attackCrystal(new BlockPos(var6), this.rotate.getValue(), false);
                  }

                  boolean var7 = this.spam.getValue() && (!this.mineSpam.getValue() || CrucifixBeta.BREAK.isMining(var6));
                  if (var7) {
                     if (!this.delayTimer.passed((long)this.spamDelay.getValueFloat())) {
                        return;
                     }

                     this.delayTimer.reset();
                     if (BlockUtil.canPlace(var6, this.range.getValue(), this.breakCrystal.getValue())) {
                        this.placeBlock(var6, this.rotate.getValue(), var2);
                     }

                     if (!this.chargeList.contains(var6)) {
                        this.delayTimer.reset();
                        this.clickBlock(var6, BlockUtil.getClickSide(var6), this.rotate.getValue(), var3);
                        this.chargeList.add(var6);
                     }

                     this.chargeList.remove(var6);
                     this.clickBlock(var6, BlockUtil.getClickSide(var6), this.rotate.getValue(), var4);
                     if (this.spamPlace.getValue() && this.inSpam.getValue()) {
                        if (this.shouldYawStep() && this.checkFov.getValue()) {
                           Direction var11 = BlockUtil.getClickSide(var6);
                           Vec3d var12 = new Vec3d(
                              (double)var6.getX() + 0.5 + (double)var11.getVector().getX() * 0.5,
                              (double)var6.getY() + 0.5 + (double)var11.getVector().getY() * 0.5,
                              (double)var6.getZ() + 0.5 + (double)var11.getVector().getZ() * 0.5
                           );
                           if (CrucifixBeta.ROTATION.inFov(var12, this.fov.getValueFloat())) {
                              CombatUtil.modifyPos = var6;
                              CombatUtil.modifyBlockState = Blocks.AIR.getDefaultState();
                              this.placeBlock(var6, this.rotate.getValue(), var2);
                              CombatUtil.modifyPos = null;
                           }
                        } else {
                           CombatUtil.modifyPos = var6;
                           CombatUtil.modifyBlockState = Blocks.AIR.getDefaultState();
                           this.placeBlock(var6, this.rotate.getValue(), var2);
                           CombatUtil.modifyPos = null;
                        }
                     }
                  } else if (BlockUtil.canPlace(var6, this.range.getValue(), this.breakCrystal.getValue())) {
                     if (!this.delayTimer.passed((long)this.placeDelay.getValueFloat())) {
                        return;
                     }

                     this.delayTimer.reset();
                     this.placeBlock(var6, this.rotate.getValue(), var2);
                  } else if (BlockUtil.getBlock(var6) == Blocks.RESPAWN_ANCHOR) {
                     if (!this.chargeList.contains(var6)) {
                        if (!this.delayTimer.passed((long)this.fillDelay.getValueFloat())) {
                           return;
                        }

                        this.delayTimer.reset();
                        this.clickBlock(var6, BlockUtil.getClickSide(var6), this.rotate.getValue(), var3);
                        this.chargeList.add(var6);
                     } else {
                        if (!this.delayTimer.passed((long)this.breakDelay.getValueFloat())) {
                           return;
                        }

                        this.delayTimer.reset();
                        this.chargeList.remove(var6);
                        this.clickBlock(var6, BlockUtil.getClickSide(var6), this.rotate.getValue(), var4);
                        if (this.spamPlace.getValue()) {
                           if (this.shouldYawStep() && this.checkFov.getValue()) {
                              Direction var9 = BlockUtil.getClickSide(var6);
                              Vec3d var10 = new Vec3d(
                                 (double)var6.getX() + 0.5 + (double)var9.getVector().getX() * 0.5,
                                 (double)var6.getY() + 0.5 + (double)var9.getVector().getY() * 0.5,
                                 (double)var6.getZ() + 0.5 + (double)var9.getVector().getZ() * 0.5
                              );
                              if (CrucifixBeta.ROTATION.inFov(var10, this.fov.getValueFloat())) {
                                 CombatUtil.modifyPos = var6;
                                 CombatUtil.modifyBlockState = Blocks.AIR.getDefaultState();
                                 this.placeBlock(var6, this.rotate.getValue(), var2);
                                 CombatUtil.modifyPos = null;
                              }
                           } else {
                              CombatUtil.modifyPos = var6;
                              CombatUtil.modifyBlockState = Blocks.AIR.getDefaultState();
                              this.placeBlock(var6, this.rotate.getValue(), var2);
                              CombatUtil.modifyPos = null;
                           }
                        }
                     }
                  }

                  if (!this.inventorySwap.getValue()) {
                     this.doSwap(var5);
                  }
               }
            }
         }
      }
   }

   private void calc() {
      if (!nullCheck()) {
         if (mc.world != null && mc.world.getRegistryKey() == World.NETHER) {
            this.currentPos = null;
         } else {
            if (this.calcTimer.passed((long)this.updateDelay.getValueFloat())) {
               this.calcTimer.reset();
               PlayerEntityPredict var1 = new PlayerEntityPredict(
                  mc.player,
                  this.maxMotionY.getValue(),
                  this.selfPredict.getValueInt(),
                  this.simulation.getValueInt(),
                  this.step.getValue(),
                  this.doubleStep.getValue(),
                  this.jump.getValue(),
                  this.inBlockPause.getValue()
               );
               this.tempPos = null;
               double var2 = this.minDamage.getValue();
               double var4 = this.breakMin.getValue();
               boolean var6 = false;
               List<PlayerEntity> var7 = CombatUtil.getEnemies(this.targetRange.getValue());
               ArrayList<PlayerEntityPredict> var8 = new ArrayList<>();

               for (PlayerEntity var10 : var7) {
                  var8.add(
                     new PlayerEntityPredict(
                        var10,
                        this.maxMotionY.getValue(),
                        this.predictTicks.getValueInt(),
                        this.simulation.getValueInt(),
                        this.step.getValue(),
                        this.doubleStep.getValue(),
                        this.jump.getValue(),
                        this.inBlockPause.getValue()
                     )
                  );
               }

               for (PlayerEntityPredict var25 : var8) {
                  BlockPos var11 = EntityUtil.getEntityPos(var25.player, true).up(2);
                  double var12;
                  if ((
                        BlockUtil.canPlace(var11, this.range.getValue(), this.breakCrystal.getValue())
                           || BlockUtil.getBlock(var11) == Blocks.RESPAWN_ANCHOR && BlockUtil.getClickSideStrict(var11) != null
                     )
                     && !((var12 = this.getAnchorDamage(var11, var1.player, var1.predict)) > this.maxSelfDamage.getValue())
                     && (!this.noSuicide.getValue() || !(var12 > (double)(mc.player.getHealth() + mc.player.getAbsorptionAmount())))) {
                     double var14 = this.getAnchorDamage(var11, var25.player, var25.predict);
                     if (var14 > (double)this.headDamage.getValueFloat() && (!this.smart.getValue() || !(var12 > var14))) {
                        this.lastDamage = var14;
                        this.displayTarget = var25.player;
                        this.tempPos = var11;
                        break;
                     }
                  }
               }

               if (this.tempPos == null) {
                  for (BlockPos var26 : BlockUtil.getSphere(this.range.getValueFloat() + 1.0F, mc.player.getEyePos())) {
                     for (PlayerEntityPredict var17 : var8) {
                        if (this.light.getValue()) {
                           CombatUtil.modifyPos = var26;
                           CombatUtil.modifyBlockState = Blocks.AIR.getDefaultState();
                           boolean var18 = !canSee(var26.toCenterPos(), var17.predict.getPos());
                           CombatUtil.modifyPos = null;
                           if (var18) {
                              continue;
                           }
                        }

                        if (BlockUtil.getBlock(var26) != Blocks.RESPAWN_ANCHOR) {
                           if (!var6 && BlockUtil.canPlace(var26, this.range.getValue(), this.breakCrystal.getValue())) {
                              CombatUtil.modifyPos = var26;
                              CombatUtil.modifyBlockState = Blocks.OBSIDIAN.getDefaultState();
                              boolean var28 = BlockUtil.getClickSideStrict(var26) == null;
                              CombatUtil.modifyPos = null;
                              double var27;
                              double var29;
                              if (!var28
                                 && (var27 = this.getAnchorDamage(var26, var17.player, var17.predict)) >= var2
                                 && (AutoCrystal.INSTANCE.crystalPos == null || AutoCrystal.INSTANCE.isOff() || (double)AutoCrystal.INSTANCE.lastDamage < var27)
                                 && !((var29 = this.getAnchorDamage(var26, var1.player, var1.predict)) > this.maxSelfDamage.getValue())
                                 && (!this.noSuicide.getValue() || !(var29 > (double)(mc.player.getHealth() + mc.player.getAbsorptionAmount())))
                                 && (!this.smart.getValue() || !(var29 > var27))) {
                                 this.lastDamage = var27;
                                 this.displayTarget = var17.player;
                                 var2 = var27;
                                 this.tempPos = var26;
                              }
                           }
                        } else {
                           double var19 = this.getAnchorDamage(var26, var17.player, var17.predict);
                           if (BlockUtil.getClickSideStrict(var26) != null && var19 >= var4) {
                              if (var19 >= this.minPrefer.getValue()) {
                                 var6 = true;
                              }

                              double var21;
                              if ((var6 || !(var19 < var2))
                                 && (AutoCrystal.INSTANCE.crystalPos == null || AutoCrystal.INSTANCE.isOff() || (double)AutoCrystal.INSTANCE.lastDamage < var19)
                                 && !((var21 = this.getAnchorDamage(var26, var1.player, var1.predict)) > this.maxSelfDamage.getValue())
                                 && (!this.noSuicide.getValue() || !(var21 > (double)(mc.player.getHealth() + mc.player.getAbsorptionAmount())))
                                 && (!this.smart.getValue() || !(var21 > var19))) {
                                 this.lastDamage = var19;
                                 this.displayTarget = var17.player;
                                 var4 = var19;
                                 this.tempPos = var26;
                              }
                           }
                        }
                     }
                  }
               }
            }

            this.currentPos = this.tempPos;
         }
      }
   }

   public double getAnchorDamage(BlockPos anchorPos, PlayerEntity target, PlayerEntity predict) {
      if (this.terrainIgnore.getValue()) {
         CombatUtil.terrainIgnore = true;
      }

      double var4 = (double)ExplosionUtil.anchorDamage(anchorPos, target, predict);
      CombatUtil.terrainIgnore = false;
      return var4;
   }

   public void placeBlock(BlockPos pos, boolean rotate, int slot) {
      if (BlockUtil.allowAirPlace()) {
         this.airPlace(pos, rotate, slot);
      } else {
         Direction var4 = BlockUtil.getPlaceSide(pos);
         if (var4 != null) {
            this.clickBlock(pos.offset(var4), var4.getOpposite(), rotate, slot);
         }
      }
   }

   public void clickBlock(BlockPos pos, Direction side, boolean rotate, int slot) {
      if (pos != null) {
         Vec3d var5 = new Vec3d(
            (double)pos.getX() + 0.5 + (double)side.getVector().getX() * 0.5,
            (double)pos.getY() + 0.5 + (double)side.getVector().getY() * 0.5,
            (double)pos.getZ() + 0.5 + (double)side.getVector().getZ() * 0.5
         );
         if (!rotate || this.faceVector(var5)) {
            this.doSwap(slot);
            EntityUtil.swingHand(Hand.MAIN_HAND, this.swingMode.getValue());
            BlockHitResult var6 = new BlockHitResult(var5, side, pos, false);
            Module.sendSequencedPacket(id -> new PlayerInteractBlockC2SPacket(Hand.MAIN_HAND, var6, id));
            if (this.inventorySwap.getValue()) {
               this.doSwap(slot);
            }

            if (rotate && !this.shouldYawStep()) {
               CrucifixBeta.ROTATION.snapBack();
            }
         }
      }
   }

   public void airPlace(BlockPos pos, boolean rotate, int slot) {
      if (pos != null) {
         Direction var4 = BlockUtil.getClickSide(pos);
         Vec3d var5 = new Vec3d(
            (double)pos.getX() + 0.5 + (double)var4.getVector().getX() * 0.5,
            (double)pos.getY() + 0.5 + (double)var4.getVector().getY() * 0.5,
            (double)pos.getZ() + 0.5 + (double)var4.getVector().getZ() * 0.5
         );
         if (!rotate || this.faceVector(var5)) {
            this.doSwap(slot);
            boolean var6 = AirPlace.INSTANCE.grimBypass.getValue();
            if (var6) {
               mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(Action.SWAP_ITEM_WITH_OFFHAND, new BlockPos(0, 0, 0), Direction.DOWN));
            }

            EntityUtil.swingHand(Hand.MAIN_HAND, this.swingMode.getValue());
            BlockHitResult var7 = new BlockHitResult(var5, var4, pos, false);
            Module.sendSequencedPacket(id -> new PlayerInteractBlockC2SPacket(var6 ? Hand.OFF_HAND : Hand.MAIN_HAND, var7, id));
            if (var6) {
               mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(Action.SWAP_ITEM_WITH_OFFHAND, new BlockPos(0, 0, 0), Direction.DOWN));
            }

            if (this.inventorySwap.getValue()) {
               this.doSwap(slot);
            }

            if (rotate && !this.shouldYawStep()) {
               CrucifixBeta.ROTATION.snapBack();
            }
         }
      }
   }

   private void doSwap(int slot) {
      if (this.inventorySwap.getValue()) {
         InventoryUtil.inventorySwap(slot, mc.player.getInventory().selectedSlot);
      } else {
         InventoryUtil.switchToSlot(slot);
      }
   }

   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 void onAssist() {
      if (mc.world == null || mc.world.getRegistryKey() != World.NETHER) {
         this.assistPos = null;
         int var1 = this.inventorySwap.getValue()
            ? InventoryUtil.findBlockInventorySlot(Blocks.RESPAWN_ANCHOR)
            : InventoryUtil.findBlock(Blocks.RESPAWN_ANCHOR);
         int var2 = this.inventorySwap.getValue() ? InventoryUtil.findBlockInventorySlot(Blocks.GLOWSTONE) : InventoryUtil.findBlock(Blocks.GLOWSTONE);
         int var3 = mc.player.getInventory().selectedSlot;
         if (var1 != -1) {
            if (this.obsidian.getValue()) {
               var1 = this.inventorySwap.getValue() ? InventoryUtil.findBlockInventorySlot(Blocks.OBSIDIAN) : InventoryUtil.findBlock(Blocks.OBSIDIAN);
               if (var1 == -1) {
                  return;
               }
            }

            if (var2 != -1) {
               if (!mc.player.isSneaking()) {
                  if (!this.usingPause.getValue() || !this.shouldPauseForUsingItem()) {
                     if (!Blink.INSTANCE.isOn() || !Blink.INSTANCE.pauseModule.getValue()) {
                        if (this.assistTimer.passed((long)(this.delay.getValueFloat() * 1000.0F))) {
                           this.assistTimer.reset();
                            ArrayList<PlayerEntityPredict> var4 = new ArrayList<>();

                           for (PlayerEntity var6 : CombatUtil.getEnemies(this.assistRange.getValue())) {
                              var4.add(
                                 new PlayerEntityPredict(
                                    var6,
                                    this.maxMotionY.getValue(),
                                    this.predictTicks.getValueInt(),
                                    this.simulation.getValueInt(),
                                    this.step.getValue(),
                                    this.doubleStep.getValue(),
                                    this.jump.getValue(),
                                    this.inBlockPause.getValue()
                                 )
                              );
                           }

                           double var7 = this.assistDamage.getValue();

                           for (PlayerEntityPredict var10 : var4) {
                              BlockPos var11 = EntityUtil.getEntityPos(var10.player, true).up(2);
                              if (mc.world.getBlockState(var11).getBlock() == Blocks.RESPAWN_ANCHOR) {
                                 return;
                              }

                              double var12;
                              if (BlockUtil.clientCanPlace(var11, false) && (var12 = this.getAnchorDamage(var11, var10.player, var10.predict)) >= var7) {
                                 var7 = var12;
                                 this.assistPos = var11;
                              }

                              for (Direction var17 : Direction.values()) {
                                 double var18;
                                 if (var17 != Direction.UP
                                    && var17 != Direction.DOWN
                                    && BlockUtil.clientCanPlace(var11.offset(var17), false)
                                    && (var18 = this.getAnchorDamage(var11.offset(var17), var10.player, var10.predict)) >= var7) {
                                    var7 = var18;
                                    this.assistPos = var11.offset(var17);
                                 }
                              }
                           }

                           BlockPos var20;
                           if (this.assistPos != null
                              && BlockUtil.getPlaceSide(this.assistPos, this.range.getValue()) == null
                              && (var20 = this.getHelper(this.assistPos)) != null) {
                              this.doSwap(var1);
                              BlockUtil.placeBlock(var20, this.rotate.getValue());
                              if (this.inventorySwap.getValue()) {
                                 this.doSwap(var1);
                              } else {
                                 this.doSwap(var3);
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }

   public BlockPos getHelper(BlockPos pos) {
      for (Direction var5 : Direction.values()) {
         if ((!this.checkMine.getValue() || !CrucifixBeta.BREAK.isMining(pos.offset(var5)))
            && BlockUtil.isStrictDirection(pos.offset(var5), var5.getOpposite())
            && BlockUtil.canPlace(pos.offset(var5))) {
            return pos.offset(var5);
         }
      }

      return null;
   }
@Environment(EnvType.CLIENT)
   public class AnchorRender {
      @EventListener
      public void onRender3D(Render3DEvent event) {
         BlockPos var2 = AutoAnchor.INSTANCE.currentPos;
         if (var2 != null) {
            AutoAnchor.this.noPosTimer.reset();
            AutoAnchor.placeVec3d = var2.toCenterPos();
         }

         if (AutoAnchor.placeVec3d != null) {
            AutoAnchor.this.fade = AutoAnchor.this.fadeSpeed.getValue() >= 1.0
               ? (AutoAnchor.this.noPosTimer.passed((long)(AutoAnchor.this.startFadeTime.getValue() * 1000.0)) ? 0.0 : 0.5)
               : AnimateUtil.animate(
                  AutoAnchor.this.fade,
                  AutoAnchor.this.noPosTimer.passed((long)(AutoAnchor.this.startFadeTime.getValue() * 1000.0)) ? 0.0 : 0.5,
                  AutoAnchor.this.fadeSpeed.getValue() / 10.0
               );
            if (AutoAnchor.this.fade == 0.0) {
               AutoAnchor.curVec3d = null;
            } else {
               AutoAnchor.curVec3d = AutoAnchor.curVec3d != null && !(AutoAnchor.this.sliderSpeed.getValue() >= 1.0)
                  ? new Vec3d(
                     AnimateUtil.animate(AutoAnchor.curVec3d.x, AutoAnchor.placeVec3d.x, AutoAnchor.this.sliderSpeed.getValue() / 10.0),
                     AnimateUtil.animate(AutoAnchor.curVec3d.y, AutoAnchor.placeVec3d.y, AutoAnchor.this.sliderSpeed.getValue() / 10.0),
                     AnimateUtil.animate(AutoAnchor.curVec3d.z, AutoAnchor.placeVec3d.z, AutoAnchor.this.sliderSpeed.getValue() / 10.0)
                  )
                  : AutoAnchor.placeVec3d;
               if (AutoAnchor.this.render.getValue()) {
                  Box var3 = new Box(AutoAnchor.curVec3d, AutoAnchor.curVec3d);
                  var3 = AutoAnchor.this.shrink.getValue() ? var3.expand(AutoAnchor.this.fade) : var3.expand(0.5);
                  if (AutoAnchor.this.fill.booleanValue) {
                     event.drawFill(
                        var3,
                        ColorUtil.injectAlpha(
                           AutoAnchor.this.fill.getValue(), (int)((double)AutoAnchor.this.fill.getValue().getAlpha() * AutoAnchor.this.fade * 2.0)
                        )
                     );
                  }

                  if (AutoAnchor.this.box.booleanValue) {
                     event.drawBox(
                        var3,
                        ColorUtil.injectAlpha(
                           AutoAnchor.this.box.getValue(), (int)((double)AutoAnchor.this.box.getValue().getAlpha() * AutoAnchor.this.fade * 2.0)
                        )
                     );
                  }
               }
            }
         }
      }
   }

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

}
}
