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

import asm.accessors.IPlayerMoveC2SPacket;
import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.ClickBlockEvent;
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.math.Easing;
import dev.crucifixbeta.api.utils.math.FadeUtils;
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.Render3DUtil;
import dev.crucifixbeta.api.utils.world.BlockUtil;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.combat.AutoAnchor;
import dev.crucifixbeta.mod.modules.impl.combat.Criticals;
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.impl.BindSetting;
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.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.AirBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.RespawnAnchorBlock;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.enchantment.Enchantments;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.item.ItemStack;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;
import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action;
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.Full;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;

@Environment(EnvType.CLIENT)
public class PacketMine extends Module {
   public static PacketMine INSTANCE;
   public static double progress;
   public static BlockPos secondPos;
   private final FadeUtils animationTime = new FadeUtils(1000L);
   private final DecimalFormat df = new DecimalFormat("0.0");
   private final EnumSetting<PacketMine.Page> page = this.add(new EnumSetting<>("Page", PacketMine.Page.General));
   private final SliderSetting stopDelay = this.add(
      new SliderSetting("StopDelay", 50.0, 0.0, 500.0, 1.0, () -> this.page.is(PacketMine.Page.General))
   );
   private final SliderSetting startDelay = this.add(
      new SliderSetting("StartDelay", 200.0, 0.0, 500.0, 1.0, () -> this.page.is(PacketMine.Page.General))
   );
   private final SliderSetting damage = this.add(
      new SliderSetting("Damage", 0.7F, 0.0, 2.0, 0.01, () -> this.page.is(PacketMine.Page.General))
   );
   private final SliderSetting maxBreak = this.add(
      new SliderSetting("MaxBreak", 3.0, 0.0, 20.0, 1.0, () -> this.page.is(PacketMine.Page.General))
   );
   public final BooleanSetting noCollide = this.add(new BooleanSetting("NoCollide", true, () -> this.page.is(PacketMine.Page.General)));
   private final BooleanSetting fastBypass = this.add(new BooleanSetting("FastBypass", false, () -> this.page.is(PacketMine.Page.General)));
   private final SliderSetting packetDelay = this.add(
      new SliderSetting("PacketDelay", 10.0, 0.0, 100.0, 1.0, () -> this.page.is(PacketMine.Page.General) && this.fastBypass.getValue())
   );
   private final BooleanSetting instant = this.add(new BooleanSetting("Instant", false, () -> this.page.is(PacketMine.Page.General)));
   private final BooleanSetting wait = this.add(
      new BooleanSetting("Wait", true, () -> !this.instant.getValue() && this.page.is(PacketMine.Page.General))
   );
   private final BooleanSetting mineAir = this.add(
      new BooleanSetting("MineAir", true, () -> this.wait.getValue() && !this.instant.getValue() && this.page.is(PacketMine.Page.General))
   );
   public final EnumSetting<PacketMine.ToolSwitchMode> toolSwitchMode = this.add(
      new EnumSetting<>("ToolSwitch", PacketMine.ToolSwitchMode.None, () -> this.page.is(PacketMine.Page.General))
   );
   private final SliderSetting switchDamage = this.add(
      new SliderSetting(
         "SwitchDamage",
         0.5,
         0.0,
         1.0,
         0.01,
         () -> this.page.is(PacketMine.Page.General) && !this.toolSwitchMode.is(PacketMine.ToolSwitchMode.None)
      )
   );
   private final BooleanSetting setAir = this.add(new BooleanSetting("SetAir", false, () -> this.page.is(PacketMine.Page.General)));
   private final BooleanSetting swing = this.add(new BooleanSetting("Swing", true, () -> this.page.is(PacketMine.Page.General)));
   private final BooleanSetting endSwing = this.add(new BooleanSetting("EndSwing", false, () -> this.page.is(PacketMine.Page.General)));
   public final SliderSetting range = this.add(
      new SliderSetting("Range", 6.0, 3.0, 10.0, 0.1, () -> this.page.is(PacketMine.Page.General))
   );
   private final EnumSetting<SwingSide> swingMode = this.add(
      new EnumSetting<>("SwingSide", SwingSide.All, () -> this.page.is(PacketMine.Page.General))
   );
   private final BooleanSetting unbreakableCancel = this.add(new BooleanSetting("UnbreakableCancel", true, () -> this.page.is(PacketMine.Page.Check)));
   private final BooleanSetting switchReset = this.add(new BooleanSetting("SwitchReset", false, () -> this.page.is(PacketMine.Page.Check)));
   private final BooleanSetting preferWeb = this.add(new BooleanSetting("PreferWeb", true, () -> this.page.is(PacketMine.Page.Check)));
   private final BooleanSetting preferHead = this.add(new BooleanSetting("PreferHead", true, () -> this.page.is(PacketMine.Page.Check)));
   private final BooleanSetting farCancel = this.add(new BooleanSetting("FarCancel", false, () -> this.page.is(PacketMine.Page.Check)));
   private final BooleanSetting onlyGround = this.add(new BooleanSetting("OnlyGround", true, () -> this.page.is(PacketMine.Page.Check)));
   private final BooleanSetting checkWeb = this.add(new BooleanSetting("CheckWeb", true, () -> this.page.is(PacketMine.Page.Check)));
   private final BooleanSetting checkGround = this.add(new BooleanSetting("CheckGround", true, () -> this.page.is(PacketMine.Page.Check)));
   private final BooleanSetting smart = this.add(
      new BooleanSetting("Smart", true, () -> this.page.is(PacketMine.Page.Check) && this.checkGround.getValue())
   );
   private final BooleanSetting usingPause = this.add(
      new BooleanSetting("UsingPause", false, () -> this.page.is(PacketMine.Page.Check)).setParent()
   );
   private final BooleanSetting allowOffhand = this.add(
      new BooleanSetting("AllowOffhand", true, () -> this.page.is(PacketMine.Page.Check) && this.usingPause.isOpen())
   );
   private final BooleanSetting bypassGround = this.add(new BooleanSetting("BypassGround", true, () -> this.page.is(PacketMine.Page.Check)));
   private final SliderSetting bypassTime = this.add(
      new SliderSetting("BypassTime", 400, 0, 2000, () -> this.bypassGround.getValue() && this.page.is(PacketMine.Page.Check))
   );
   private final BindSetting pause = this.add(new BindSetting("Pause", -1, () -> this.page.is(PacketMine.Page.Check)));
   private final BooleanSetting rotate = this.add(new BooleanSetting("StartRotate", true, () -> this.page.is(PacketMine.Page.Rotation)));
   private final BooleanSetting endRotate = this.add(new BooleanSetting("EndRotate", false, () -> this.page.is(PacketMine.Page.Rotation)));
   private final SliderSetting syncTime = this.add(new SliderSetting("Sync", 300, 0, 1000, () -> this.page.is(PacketMine.Page.Rotation)));
   private final BooleanSetting yawStep = this.add(
      new BooleanSetting("YawStep", false, () -> this.page.is(PacketMine.Page.Rotation)).setParent()
   );
   private final BooleanSetting whenElytra = this.add(
      new BooleanSetting("FallFlying", true, () -> this.page.is(PacketMine.Page.Rotation) && this.yawStep.isOpen())
   );
   private final SliderSetting steps = this.add(
      new SliderSetting("Steps", 0.05, 0.0, 1.0, 0.01, () -> this.page.is(PacketMine.Page.Rotation) && this.yawStep.isOpen())
   );
   private final BooleanSetting checkFov = this.add(
      new BooleanSetting("OnlyLooking", true, () -> this.page.is(PacketMine.Page.Rotation) && this.yawStep.isOpen())
   );
   private final SliderSetting fov = this.add(
      new SliderSetting("Fov", 20.0, 0.0, 360.0, 0.1, () -> this.page.is(PacketMine.Page.Rotation) && this.yawStep.isOpen())
   );
   private final SliderSetting priority = this.add(
      new SliderSetting("Priority", 10, 0, 100, () -> this.page.is(PacketMine.Page.Rotation) && this.yawStep.isOpen())
   );
   private final EnumSetting<PacketMine.Animation> animation = this.add(
      new EnumSetting<>("Animation", PacketMine.Animation.Up, () -> this.page.is(PacketMine.Page.Render))
   );
   private final EnumSetting<Easing> ease = this.add(
      new EnumSetting<>("Ease", Easing.CubicInOut, () -> this.page.is(PacketMine.Page.Render))
   );
   private final SliderSetting expandLine = this.add(new SliderSetting("ExpandLine", 0.0, 0.0, 1.0, () -> this.page.is(PacketMine.Page.Render)));
   private final ColorSetting startColor = this.add(
      new ColorSetting("StartFill", new Color(255, 255, 255, 100), () -> this.page.is(PacketMine.Page.Render))
   );
   private final ColorSetting startOutlineColor = this.add(
      new ColorSetting("StartOutline", new Color(255, 255, 255, 100), () -> this.page.is(PacketMine.Page.Render))
   );
   private final ColorSetting endColor = this.add(
      new ColorSetting("EndFill", new Color(255, 255, 255, 100), () -> this.page.is(PacketMine.Page.Render))
   );
   private final ColorSetting endOutlineColor = this.add(
      new ColorSetting("EndOutline", new Color(255, 255, 255, 100), () -> this.page.is(PacketMine.Page.Render))
   );
   private final BooleanSetting text = this.add(new BooleanSetting("Text", true, () -> this.page.is(PacketMine.Page.Render)));
   private final BooleanSetting box = this.add(new BooleanSetting("Box", true, () -> this.page.is(PacketMine.Page.Render)));
   private final BooleanSetting outline = this.add(new BooleanSetting("Outline", true, () -> this.page.is(PacketMine.Page.Render)));
   private final Timer mineTimer = new Timer();
   private final Timer sync = new Timer();
   private final Timer delayTimer = new Timer();
   private final Timer startTime = new Timer();
   public static boolean ghost;
   public static boolean complete;
   int lastSlot = -1;
   Vec3d directionVec = null;
   BlockPos breakPos;
   boolean startPacket = false;
   int breakNumber = 0;
   double breakFinalTime;
   boolean toolSwitched = false;
   int originalSlot = 0;
   int bestToolSlot = 0;
   List<BlockPos> fastBypassPositions = new ArrayList<>();
   private boolean isRotating = false;
   private Vec3d lastRotationTarget = null;
   private final Timer rotationResetTimer = new Timer();

   public void mine(BlockPos pos) {
      if (!nullCheck()) {
         if (mc.player.isCreative()) {
            mc.interactionManager.attackBlock(pos, BlockUtil.getClickSide(pos));
         } else if (this.isOff()) {
            mc.interactionManager.attackBlock(pos, BlockUtil.getClickSide(pos));
         } else if (!pos.equals(this.breakPos)) {
            if (!unbreakable(pos)) {
               if (this.breakPos == null || !this.preferWeb.getValue() || BlockUtil.getBlock(this.breakPos) != Blocks.COBWEB) {
                  if (this.breakPos == null
                     || !this.preferHead.getValue()
                     || !mc.player.isCrawling()
                     || !EntityUtil.getPlayerPos(true).up().equals(this.breakPos)) {
                     if (BlockUtil.getClickSideStrict(pos) != null) {
                        if (!((double)MathHelper.sqrt((float)mc.player.getEyePos().squaredDistanceTo(pos.toCenterPos())) > this.range.getValue())) {
                           this.switchBackToOriginal();
                           this.breakPos = pos;
                           this.breakNumber = 0;
                           this.startPacket = false;
                           ghost = false;
                           complete = false;
                           this.mineTimer.reset();
                           this.animationTime.reset();
                           this.toolSwitched = false;
                           this.originalSlot = mc.player.getInventory().selectedSlot;
                        }
                     }
                  }
               }
            }
         }
      }
   }

   public PacketMine() {
      super("PacketMine", Module.Category.Player);
      this.setChinese("\u53d1\u5305\u6316\u6398");
      INSTANCE = this;
   }

   public static BlockPos getBreakPos() {
      return INSTANCE.isOn() ? INSTANCE.breakPos : null;
   }

   @Override
   public String getInfo() {
      return progress >= 1.0 ? "Done" : this.df.format(progress * 100.0) + "%";
   }

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

   @Override
   public void onLogin() {
      this.resetMining();
   }

   @Override
   public void onDisable() {
      this.resetMining();
      this.switchBackToOriginal();
   }

   private void resetMining() {
      this.startPacket = false;
      ghost = false;
      complete = false;
      this.breakPos = null;
      this.toolSwitched = false;
      this.fastBypassPositions.clear();
      this.isRotating = false;
      this.lastRotationTarget = null;
      this.switchBackToOriginal();
      this.originalSlot = -1;
   }

   private void switchBackToOriginal() {
      if (!this.toolSwitchMode.is(PacketMine.ToolSwitchMode.None)) {
         if (this.toolSwitched) {
            if (this.toolSwitchMode.is(PacketMine.ToolSwitchMode.Silent)) {
               mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(mc.player.getInventory().selectedSlot));
            } else if (this.toolSwitchMode.is(PacketMine.ToolSwitchMode.Delay)) {
               int var1 = mc.player.getInventory().selectedSlot;
               if (var1 == this.bestToolSlot) {
                  InventoryUtil.switchToSlot(this.originalSlot);
               }
            }

            this.toolSwitched = false;
         }
      }
   }

   private void handleToolSwitching() {
      if (!this.toolSwitchMode.is(PacketMine.ToolSwitchMode.None)) {
         if (this.breakPos == null) {
            this.switchBackToOriginal();
         } else if (!this.isAir(this.breakPos) && !unbreakable(this.breakPos)) {
            if ((double)MathHelper.sqrt((float)mc.player.getEyePos().squaredDistanceTo(this.breakPos.toCenterPos())) > this.range.getValue() + 1.0) {
               this.switchBackToOriginal();
            } else if (this.startPacket) {
               if (!this.toolSwitched) {
                  if (!this.usingPause.getValue() || !mc.player.isUsingItem() || this.allowOffhand.getValue() && mc.player.getActiveHand() != Hand.MAIN_HAND) {
                     this.performToolSwitch();
                  }
               }
            }
         } else {
            this.switchBackToOriginal();
         }
      }
   }

   private void performToolSwitch() {
      int var1 = this.getTool(this.breakPos);
      if (var1 != -1 && var1 != mc.player.getInventory().selectedSlot) {
         if (!(this.breakFinalTime <= 0.0)) {
            double var2 = Math.min((double)this.mineTimer.getMs() / this.breakFinalTime, 1.0);
            double var4 = Math.min(this.switchDamage.getValue(), 1.0);
            if (var2 >= var4) {
               this.originalSlot = mc.player.getInventory().selectedSlot;
               this.bestToolSlot = var1;
               switch ((PacketMine.ToolSwitchMode)this.toolSwitchMode.getValue()) {
                  case Delay:
                     InventoryUtil.switchToSlot(var1);
                     break;
                  case Silent:
                     mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(var1));
               }

               this.toolSwitched = true;
            }
         }
      }
   }

   private void performFinishBreak() {
      Direction var1 = this.getMineSide(this.breakPos);
      if (var1 != null) {
         if (!this.endRotate.getValue() || this.faceVector(this.breakPos.toCenterPos().offset(var1, 0.5))) {
            if (this.bypassGround.getValue()
               && !mc.player.isFallFlying()
               && this.breakPos != null
               && !this.isAir(this.breakPos)
               && this.bypassTime.getValue() > 0.0
               && MathHelper.sqrt((float)this.breakPos.toCenterPos().squaredDistanceTo(mc.player.getEyePos())) <= this.range.getValueFloat() + 2.0F) {
               double var2 = this.breakFinalTime - this.bypassTime.getValue();
               if (var2 <= 0.0 || this.mineTimer.passed((long)var2)) {
                  mc.getNetworkHandler()
                     .sendPacket(new Full(mc.player.getX(), mc.player.getY(), mc.player.getZ(), mc.player.getYaw(), mc.player.getPitch(), true));
               }
            }

            sendSequencedPacket(id -> new PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, this.breakPos, var1, id));
            if (this.endSwing.getValue()) {
               EntityUtil.swingHand(Hand.MAIN_HAND, this.swingMode.getValue());
            }

            this.breakNumber++;
            this.delayTimer.reset();
            this.startTime.reset();
            if (this.setAir.getValue()) {
               mc.world.setBlockState(this.breakPos, Blocks.AIR.getDefaultState());
            }

            if (this.endRotate.getValue() && !this.shouldYawStep()) {
               this.scheduleRotationReset();
            }

            ghost = true;
            this.switchBackToOriginal();
         }
      }
   }

   @EventListener
   public void onTick(ClientTickEvent event) {
      if (!nullCheck()) {
         this.handleRotationReset();
         if (this.breakPos != null) {
            int var2 = this.getTool(this.breakPos);
            if (var2 == -1) {
               var2 = mc.player.getInventory().selectedSlot;
            }

            this.breakFinalTime = Math.max(1.0, this.getBreakTime(this.breakPos, var2));
         }

         this.handleToolSwitching();
         if (this.breakPos != null && mc.world.isAir(this.breakPos)) {
            complete = true;
            this.switchBackToOriginal();
         }

         if (mc.player.isDead()) {
            this.switchBackToOriginal();
         }

         if (mc.player.isCreative()) {
            this.resetMining();
         } else if (this.breakPos == null) {
            this.breakNumber = 0;
            this.resetMining();
         } else {
            progress = Math.min((double)this.mineTimer.getMs() / Math.max(this.breakFinalTime, 1.0), 1.0);
            if (this.isAir(this.breakPos)) {
               this.breakNumber = 0;
            }

            if ((!((double)this.breakNumber > this.maxBreak.getValue() - 1.0) || !(this.maxBreak.getValue() > 0.0) || complete)
               && (this.wait.getValue() || !this.isAir(this.breakPos) || this.instant.getValue())) {
               if (unbreakable(this.breakPos)) {
                  if (this.unbreakableCancel.getValue()) {
                     this.switchBackToOriginal();
                     this.resetMining();
                  }

                  this.breakNumber = 0;
               } else if ((double)MathHelper.sqrt((float)mc.player.getEyePos().squaredDistanceTo(this.breakPos.toCenterPos())) > this.range.getValue()) {
                  if (this.farCancel.getValue()) {
                     this.switchBackToOriginal();
                     this.resetMining();
                  }
               } else if (!this.usingPause.getValue()
                  || !mc.player.isUsingItem()
                  || this.allowOffhand.getValue() && mc.player.getActiveHand() != Hand.MAIN_HAND) {
                  if (this.pause.isPressed()) {
                     this.switchBackToOriginal();
                  } else if (!Blink.INSTANCE.isOn() || !Blink.INSTANCE.pauseModule.getValue()) {
                     if (!this.breakPos.equals(AutoAnchor.INSTANCE.currentPos) || !(BlockUtil.getBlock(getBreakPos()) instanceof RespawnAnchorBlock)) {
                        if (this.isAir(this.breakPos)) {
                           this.breakNumber = 0;
                        }

                        if (this.delayTimer.passed((long)this.stopDelay.getValue())) {
                           if (this.startPacket) {
                              if (this.isAir(this.breakPos)) {
                                 return;
                              }

                              if (this.onlyGround.getValue() && !mc.player.isOnGround()) {
                                 return;
                              }

                              if (this.mineTimer.passed((long)this.breakFinalTime)) {
                                 this.performFinishBreak();
                              }
                           } else {
                              if (!this.startTime.passed((long)this.startDelay.getValueInt())) {
                                 return;
                              }

                              if (!this.mineAir.getValue() && this.isAir(this.breakPos)) {
                                 return;
                              }

                              Direction var4 = this.getMineSide(this.breakPos);
                              if (var4 == null) {
                                 return;
                              }

                              if (this.rotate.getValue()) {
                                 Vec3i var3 = var4.getVector();
                                 if (!this.faceVector(
                                    this.breakPos.toCenterPos().add(new Vec3d((double)var3.getX() * 0.5, (double)var3.getY() * 0.5, (double)var3.getZ() * 0.5))
                                 )) {
                                    return;
                                 }
                              }

                              this.mineTimer.reset();
                              this.animationTime.reset();
                              this.animationTime.setLength((long)Math.max(this.breakFinalTime, 1.0));
                              if (this.swing.getValue()) {
                                 EntityUtil.swingHand(Hand.MAIN_HAND, this.swingMode.getValue());
                              }

                              this.startMining();
                           }
                        }
                     }
                  }
               }
            } else {
               this.switchBackToOriginal();
               this.resetMining();
            }
         }
      }
   }

   private void scheduleRotationReset() {
      this.isRotating = true;
      this.rotationResetTimer.reset();
   }

   private Direction getMineSide(BlockPos pos) {
      Direction var2 = BlockUtil.getClickSideStrict(pos);
      return var2 != null ? var2 : BlockUtil.getClickSide(pos);
   }

   private void handleRotationReset() {
      if (this.isRotating && this.rotationResetTimer.passedMs(50.0)) {
         CrucifixBeta.ROTATION.snapBack();
         this.isRotating = false;
         this.lastRotationTarget = null;
      }
   }

   private void startMining() {
      this.toolSwitched = false;
      Direction var1 = this.getMineSide(this.breakPos);
      if (var1 != null) {
         sendSequencedPacket(id -> new PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, this.breakPos, var1, id));
         if (this.fastBypass.getValue()) {
            this.sendFastBypassPackets();
         }

         this.startPacket = true;
         if (this.rotate.getValue() && !this.shouldYawStep()) {
            this.scheduleRotationReset();
         }

         this.startTime.reset();
      }
   }

   private void sendFastBypassPackets() {
      BlockPos var1 = new BlockPos(this.breakPos.getX(), 400, this.breakPos.getZ());

      for (int var2 = 0; var2 < 3; var2++) {
         int var3 = var2 * 10;
         Direction[] var4 = Direction.values();
         Direction var5 = var4[(int)(Math.random() * (double)var4.length)];
         sendSequencedPacket(id -> new PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, var1.up(var3), var5, id));
      }
   }

   @EventListener
   public void onAttackBlock(ClickBlockEvent event) {
      if (!nullCheck()) {
         if (!mc.player.isCreative()) {
            event.cancel();
            BlockPos var2 = event.getPos();
            if (!unbreakable(var2)) {
               if (!this.isAir(var2)) {
                  if (!var2.equals(this.breakPos)) {
                     if (this.breakPos == null || !this.preferWeb.getValue() || BlockUtil.getBlock(this.breakPos) != Blocks.COBWEB) {
                        if (this.breakPos == null
                           || !this.preferHead.getValue()
                           || !mc.player.isCrawling()
                           || !EntityUtil.getPlayerPos(true).up().equals(this.breakPos)) {
                           if (BlockUtil.getClickSideStrict(var2) != null) {
                              if (!((double)MathHelper.sqrt((float)mc.player.getEyePos().squaredDistanceTo(var2.toCenterPos())) > this.range.getValue())) {
                                 this.switchBackToOriginal();
                                 this.breakPos = var2;
                                 this.breakNumber = 0;
                                 this.startPacket = false;
                                 ghost = false;
                                 complete = false;
                                 this.mineTimer.reset();
                                 this.animationTime.reset();
                                 this.toolSwitched = false;
                                 this.originalSlot = mc.player.getInventory().selectedSlot;
                                 if (!Blink.INSTANCE.isOn() || !Blink.INSTANCE.pauseModule.getValue()) {
                                    if (this.startTime.passed((long)this.startDelay.getValueInt())) {
                                       Direction var3 = this.getMineSide(this.breakPos);
                                       if (var3 != null) {
                                          if (this.rotate.getValue()) {
                                             Vec3i var4 = var3.getVector();
                                             if (!this.faceVector(
                                                this.breakPos
                                                   .toCenterPos()
                                                   .add(new Vec3d((double)var4.getX() * 0.5, (double)var4.getY() * 0.5, (double)var4.getZ() * 0.5))
                                             )) {
                                                return;
                                             }
                                          }

                                          if (this.swing.getValue()) {
                                             EntityUtil.swingHand(Hand.MAIN_HAND, this.swingMode.getValue());
                                          }

                                          this.startMining();
                                       }
                                    }
                                 }
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }

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

   @Override
   public void onRender3D(MatrixStack matrixStack) {
      if (this.breakPos != null && mc.world.isAir(this.breakPos)) {
         complete = true;
      }

      if (!mc.player.isCreative()) {
         if (this.breakPos != null) {
            double var2 = Math.max(this.breakFinalTime, 1.0);
            progress = Math.min((double)this.mineTimer.getMs() / var2, 1.0);
            double var4;
            if (this.startPacket) {
               this.animationTime.setLength((long)var2);
               var4 = this.animationTime.ease(this.ease.getValue());
            } else {
               var4 = 0.0;
            }

            if (unbreakable(this.breakPos)) {
               if (this.box.getValue()) {
                  Render3DUtil.drawFill(matrixStack, new Box(this.breakPos), this.startColor.getValue());
               }

               if (this.outline.getValue()) {
                  Render3DUtil.drawBox(matrixStack, new Box(this.breakPos), this.startOutlineColor.getValue());
               }

               return;
            }

            if (this.box.getValue()) {
               double var6 = this.startPacket ? (double)this.mineTimer.getMs() / var2 : 0.0;
               var6 = Math.min(var6, 1.0);
               Render3DUtil.drawFill(matrixStack, this.getFillBox(this.breakPos, var4), this.getColor(var6));
            }

            if (this.outline.getValue()) {
               double var9 = this.startPacket ? (double)this.mineTimer.getMs() / var2 : 0.0;
               var9 = Math.min(var9, 1.0);
               Render3DUtil.drawBox(matrixStack, this.getOutlineBox(this.breakPos, var4), this.getOutlineColor(var9));
            }

            if (this.text.getValue()) {
               if (this.isAir(this.breakPos)) {
                  Render3DUtil.drawText3D("Waiting", this.breakPos.toCenterPos(), -1);
               } else if ((double)((int)this.mineTimer.getMs()) < var2) {
                  Render3DUtil.drawText3D(this.df.format(progress * 100.0) + "%", this.breakPos.toCenterPos(), -1);
               } else {
                  Render3DUtil.drawText3D("100.0%", this.breakPos.toCenterPos(), -1);
               }
            }
         } else {
            progress = 0.0;
         }
      } else {
         progress = 0.0;
      }
   }

   private Box getFillBox(BlockPos pos, double ease) {
      switch ((PacketMine.Animation)this.animation.getValue()) {
         case Center:
            ease = (1.0 - ease) / 2.0;
            return new Box(pos).shrink(ease, ease, ease).shrink(-ease, -ease, -ease);
         case Grow:
            ease = (1.0 - ease) / 2.0;
            return new Box(pos).shrink(ease, 0.0, ease).shrink(-ease, 0.0, -ease);
         case Up:
            return new Box(
               (double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), (double)(pos.getX() + 1), (double)pos.getY() + ease, (double)(pos.getZ() + 1)
            );
         case Down:
            return new Box(
               (double)pos.getX(),
               (double)(pos.getY() + 1) - ease,
               (double)pos.getZ(),
               (double)(pos.getX() + 1),
               (double)(pos.getY() + 1),
               (double)(pos.getZ() + 1)
            );
         case Oscillation:
            return new Box(pos).shrink(ease, ease, ease).shrink(-ease, -ease, -ease);
         default:
            return new Box(pos);
      }
   }

   private Box getOutlineBox(BlockPos pos, double ease) {
      ease = Math.min(ease + this.expandLine.getValue(), 1.0);
      switch ((PacketMine.Animation)this.animation.getValue()) {
         case Center:
            ease = (1.0 - ease) / 2.0;
            return new Box(pos).shrink(ease, ease, ease).shrink(-ease, -ease, -ease);
         case Grow:
            ease = (1.0 - ease) / 2.0;
            return new Box(pos).shrink(ease, 0.0, ease).shrink(-ease, 0.0, -ease);
         case Up:
            return new Box(
               (double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), (double)(pos.getX() + 1), (double)pos.getY() + ease, (double)(pos.getZ() + 1)
            );
         case Down:
            return new Box(
               (double)pos.getX(),
               (double)(pos.getY() + 1) - ease,
               (double)pos.getZ(),
               (double)(pos.getX() + 1),
               (double)(pos.getY() + 1),
               (double)(pos.getZ() + 1)
            );
         case Oscillation:
            return new Box(pos).shrink(ease, ease, ease).shrink(-ease, -ease, -ease);
         default:
            return new Box(pos);
      }
   }

   @EventListener(
      priority = -200
   )
   public void onPacketSend(PacketEvent.Send event) {
      if (!nullCheck() && !mc.player.isCreative()) {
         if (event.getPacket() instanceof PlayerMoveC2SPacket) {
            if (this.bypassGround.getValue()
               && !mc.player.isFallFlying()
               && this.breakPos != null
               && !this.isAir(this.breakPos)
               && this.bypassTime.getValue() > 0.0
               && MathHelper.sqrt((float)this.breakPos.toCenterPos().squaredDistanceTo(mc.player.getEyePos())) <= this.range.getValueFloat() + 2.0F) {
               double var2 = this.breakFinalTime - this.bypassTime.getValue();
               if (var2 <= 0.0 || this.mineTimer.passed((long)var2)) {
                  ((IPlayerMoveC2SPacket)event.getPacket()).setOnGround(true);
               }
            }
         } else if (event.getPacket() instanceof UpdateSelectedSlotC2SPacket var5) {
            if (var5.getSelectedSlot() != this.lastSlot) {
               this.lastSlot = var5.getSelectedSlot();
               if (this.switchReset.getValue()) {
                  this.startPacket = false;
                  ghost = false;
                  complete = false;
                  this.mineTimer.reset();
                  this.animationTime.reset();
               }
            }
         } else if (event.getPacket() instanceof PlayerActionC2SPacket var7) {
            if (var7.getAction() == Action.START_DESTROY_BLOCK) {
               if (this.breakPos == null || !var7.getPos().equals(this.breakPos)) {
                  return;
               }

               this.startPacket = true;
            } else if (var7.getAction() == Action.STOP_DESTROY_BLOCK) {
               if (this.breakPos == null || !var7.getPos().equals(this.breakPos)) {
                  return;
               }

               if (!this.instant.getValue()) {
                  this.startPacket = false;
                  ghost = false;
                  complete = false;
                  this.switchBackToOriginal();
               }
            }
         }
      }
   }

   public static double getBreakTime(BlockPos pos) {
      int var1 = INSTANCE.getTool(pos);
      if (var1 == -1) {
         var1 = mc.player.getInventory().selectedSlot;
      }

      return INSTANCE.getBreakTime(pos, var1);
   }

   double getBreakTime(BlockPos pos, int slot) {
      return this.getBreakTime(pos, slot, this.damage.getValue());
   }

   double getBreakTime(BlockPos pos, int slot, double damage) {
      float var5 = this.getBlockStrength(pos, mc.player.getInventory().getStack(slot));
      return var5 <= 0.0F ? 0.0 : (double)(1.0F / var5 / 20.0F * 1000.0F) * damage;
   }

   float getBlockStrength(BlockPos position, ItemStack itemStack) {
      BlockState var3 = mc.world.getBlockState(position);
      float var4 = var3.getHardness(mc.world, position);
      if (var4 <= 0.0F) {
         return Float.MAX_VALUE;
      } else {
         float var5 = var3.isToolRequired() && !itemStack.isSuitableFor(var3) ? 100.0F : 30.0F;
         return this.getDigSpeed(position, var3, itemStack) / var4 / var5;
      }
   }

   float getDigSpeed(BlockPos position, BlockState state, ItemStack itemStack) {
      float var4 = this.getDestroySpeed(state, itemStack);
      int var5;
      if (var4 > 1.0F
         && (
               var5 = EnchantmentHelper.getLevel(
                  mc.world.getRegistryManager().get(Enchantments.EFFICIENCY.getRegistryRef()).getEntry(Enchantments.EFFICIENCY).get(), itemStack
               )
            )
            > 0
         && !itemStack.isEmpty()) {
         var4 += (float)(StrictMath.pow((double)var5, 2.0) + 1.0);
      }

      if (mc.player.hasStatusEffect(StatusEffects.HASTE)) {
         var4 *= 1.0F + (float)(mc.player.getStatusEffect(StatusEffects.HASTE).getAmplifier() + 1) * 0.2F;
      }

      if (mc.player.hasStatusEffect(StatusEffects.MINING_FATIGUE)) {
         var4 *= switch (mc.player.getStatusEffect(StatusEffects.MINING_FATIGUE).getAmplifier()) {
            case 0 -> 0.3F;
            case 1 -> 0.09F;
            case 2 -> 0.0027F;
            default -> 8.1E-4F;
         };
      }

      if (mc.player.isSubmergedInWater()) {
         var4 *= (float)mc.player.getAttributeInstance(EntityAttributes.PLAYER_SUBMERGED_MINING_SPEED).getValue();
      }

      boolean var6 = this.checkWeb.getValue() && CrucifixBeta.PLAYER.isInWeb(mc.player) && mc.world.getBlockState(position).getBlock() == Blocks.COBWEB;
      if ((!mc.player.isOnGround() || var6)
         && INSTANCE.checkGround.getValue()
         && (!this.smart.getValue() || Criticals.INSTANCE.mode.is(Criticals.Mode.Ground) && Criticals.INSTANCE.isOn() || mc.player.isFallFlying() || var6)) {
         var4 /= 5.0F;
      }

      return var4 < 0.0F ? 0.0F : var4;
   }

   float getDestroySpeed(BlockState state, ItemStack itemStack) {
      float var3 = 1.0F;
      if (itemStack != null && !itemStack.isEmpty()) {
         var3 *= itemStack.getMiningSpeedMultiplier(state);
      }

      return var3;
   }

   Color getColor(double quad) {
      int var3 = this.startColor.getValue().getRed();
      int var4 = this.startColor.getValue().getGreen();
      int var5 = this.startColor.getValue().getBlue();
      int var6 = this.startColor.getValue().getAlpha();
      int var7 = this.endColor.getValue().getRed();
      int var8 = this.endColor.getValue().getGreen();
      int var9 = this.endColor.getValue().getBlue();
      int var10 = this.endColor.getValue().getAlpha();
      return new Color(
         (int)((double)var3 + (double)(var7 - var3) * quad),
         (int)((double)var4 + (double)(var8 - var4) * quad),
         (int)((double)var5 + (double)(var9 - var5) * quad),
         (int)((double)var6 + (double)(var10 - var6) * quad)
      );
   }

   Color getOutlineColor(double quad) {
      int var3 = this.startOutlineColor.getValue().getRed();
      int var4 = this.startOutlineColor.getValue().getGreen();
      int var5 = this.startOutlineColor.getValue().getBlue();
      int var6 = this.startOutlineColor.getValue().getAlpha();
      int var7 = this.endOutlineColor.getValue().getRed();
      int var8 = this.endOutlineColor.getValue().getGreen();
      int var9 = this.endOutlineColor.getValue().getBlue();
      int var10 = this.endOutlineColor.getValue().getAlpha();
      return new Color(
         (int)((double)var3 + (double)(var7 - var3) * quad),
         (int)((double)var4 + (double)(var8 - var4) * quad),
         (int)((double)var5 + (double)(var9 - var5) * quad),
         (int)((double)var6 + (double)(var10 - var6) * quad)
      );
   }

   int getTool(BlockPos pos) {
      if (this.toolSwitchMode.is(PacketMine.ToolSwitchMode.None)) {
         return -1;
      } else {
         int var2 = -1;
         float var3 = 1.0F;

         for (int var4 = 0; var4 < 9; var4++) {
            ItemStack var5 = mc.player.getInventory().getStack(var4);
            float var6;
            float var7;
            if (var5 != ItemStack.EMPTY
               && (
                        var6 = (float)EnchantmentHelper.getLevel(
                           mc.world.getRegistryManager().get(Enchantments.EFFICIENCY.getRegistryRef()).getEntry(Enchantments.EFFICIENCY).get(), var5
                        )
                     )
                     + (var7 = var5.getMiningSpeedMultiplier(mc.world.getBlockState(pos)))
                  > var3) {
               var3 = var6 + var7;
               var2 = var4;
            }
         }

         return var2;
      }
   }

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

   boolean isAir(BlockPos breakPos) {
      return mc.world.isAir(breakPos) || BlockUtil.getBlock(breakPos) == Blocks.FIRE && BlockUtil.hasCrystal(breakPos);
   }

   public static boolean unbreakable(BlockPos blockPos) {
      Block var1 = mc.world.getBlockState(blockPos).getBlock();
      return !(var1 instanceof AirBlock) && (var1.getHardness() == -1.0F || var1.getHardness() == 100.0F);
   }

   static {

      progress = 0.0;
      ghost = false;
      complete = false;
   }
@Environment(EnvType.CLIENT)
   private static enum Animation {
      Center,
      Grow,
      Up,
      Down,
      Oscillation,
      None;

}

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

}

   @Environment(EnvType.CLIENT)
   public static enum ToolSwitchMode {
      None,
      Delay,
      Silent;

}
}
