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

import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.ClickBlockEvent;
import dev.crucifixbeta.api.events.impl.Render3DEvent;
import dev.crucifixbeta.api.utils.Wrapper;
import dev.crucifixbeta.api.utils.math.DamageUtils;
import dev.crucifixbeta.api.utils.math.Easing;
import dev.crucifixbeta.api.utils.math.Timer;
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.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.TimerTask;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.BlockState;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.enchantment.Enchantments;
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.UpdateSelectedSlotC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action;
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.PositionAndOnGround;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;

@Environment(EnvType.CLIENT)
public class SpeedMineTest extends Module {
   public static SpeedMineTest INSTANCE;
   private final EnumSetting<SpeedMineTest.SwitchMode> switchMode = this.add(new EnumSetting<>("SwitchMode", SpeedMineTest.SwitchMode.Silent));
   private final SliderSetting range = this.add(new SliderSetting("Range", 6.0, 0.0, 12.0, 1.0));
   private final SliderSetting maxBreaks = this.add(new SliderSetting("MaxBreaks", 6.0, 0.0, 10.0, 1.0));
   private final BooleanSetting farCancel = this.add(new BooleanSetting("FarCancel", true));
   private final BooleanSetting swing = this.add(new BooleanSetting("Swing", true));
   private final BooleanSetting instantMine = this.add(new BooleanSetting("InstantMine", true));
   private final SliderSetting instantDelay = this.add(new SliderSetting("InstantDelay", 10.0, 0.0, 1000.0, 10.0, this.instantMine::getValue));
   private final BooleanSetting fastBypass = this.add(new BooleanSetting("FastBypass", true));
   private final BooleanSetting doubleBreak = this.add(new BooleanSetting("DoubleBreak", true));
   private final BooleanSetting checkGround = this.add(new BooleanSetting("CheckGround", true));
   private final BooleanSetting bypassGround = this.add(new BooleanSetting("BypassGround", true));
   private final BooleanSetting clientRemove = this.add(new BooleanSetting("ClientRemove", true));
   private final BooleanSetting safeDownMine = this.add(new BooleanSetting("SafeDownMine", true));
   private final SliderSetting switchDamage = this.add(new SliderSetting("SwitchDamage", 95.0, 0.0, 100.0, 1.0));
   private final SliderSetting switchTime = this.add(new SliderSetting("SwitchTime", 100.0, 0.0, 1000.0, 10.0));
   private final SliderSetting mineDelay = this.add(new SliderSetting("MineDelay", 300.0, 0.0, 1000.0, 10.0));
   private final SliderSetting packetDelay = this.add(new SliderSetting("PacketDelay", 200.0, 0.0, 1000.0, 10.0, this.doubleBreak::getValue));
   private final SliderSetting mineDamage = this.add(new SliderSetting("Damage", 0.8, 0.0, 2.0, 0.05));
   private final BooleanSetting usingPause = this.add(new BooleanSetting("PauseOnUse", true));
   private final BooleanSetting onlyMain = this.add(new BooleanSetting("OnlyMain", true, this.usingPause::getValue));
   private final EnumSetting<SpeedMineTest.Animation> animation = this.add(new EnumSetting<>("Animation", SpeedMineTest.Animation.Up));
   private final EnumSetting<Easing> ease = this.add(new EnumSetting<>("Ease", Easing.CubicInOut));
   private final EnumSetting<Easing> fadeEase = this.add(new EnumSetting<>("FadeEase", Easing.CubicInOut));
   private final SliderSetting expandLine = this.add(new SliderSetting("ExpandLine", 0.0, 0.0, 1.0, 0.01));
   private final BooleanSetting text = this.add(new BooleanSetting("Text", true));
   private final BooleanSetting box = this.add(new BooleanSetting("Box", true));
   private final BooleanSetting outline = this.add(new BooleanSetting("Outline", true));
   private final BooleanSetting fading = this.add(new BooleanSetting("Fading", true));
   private final SliderSetting renderTime = this.add(new SliderSetting("RenderTime", 0.1, 0.0, 5.0, 0.1));
   private final SliderSetting fadeTime = this.add(new SliderSetting("FadeTime", 0.2, 0.0, 5.0, 0.1));
   private final ColorSetting fadeSideColor = this.add(new ColorSetting("FadeSide", new Color(70, 200, 155, 31)));
   private final ColorSetting fadeLineColor = this.add(new ColorSetting("FadeLine", new Color(70, 200, 155, 233)));
   private final ColorSetting sideStart = this.add(new ColorSetting("StartFill", new Color(255, 255, 255, 100)));
   private final ColorSetting sideEnd = this.add(new ColorSetting("EndFill", new Color(255, 255, 255, 100)));
   private final ColorSetting lineStart = this.add(new ColorSetting("StartOutline", new Color(255, 255, 255, 100)));
   private final ColorSetting lineEnd = this.add(new ColorSetting("EndOutline", new Color(255, 255, 255, 100)));
   private final ColorSetting doubleSideColor = this.add(new ColorSetting("DoubleFill", new Color(88, 94, 255, 100), this.doubleBreak::getValue));
   private final ColorSetting doubleLineColor = this.add(
      new ColorSetting("DoubleOutline", new Color(88, 94, 255, 100), this.doubleBreak::getValue)
   );
   private BlockPos targetPos;
   private BlockPos secondPos;
   private BlockPos renderPos;
   private BlockPos secondRenderPos;
   private float progress;
   private float secondProgress;
   private int mainProgressPercent;
   private int secondProgressPercent;
   private boolean started;
   private boolean secondStarted;
   private boolean completed;
   private boolean hasSwitch;
   private boolean secondHasSwitch;
   private int maxBreaksCount;
   private int oldSlot = -1;
   private long lastTime;
   private long secondLastTime;
   private long fadeLastTime;
   private double renderProgress;
   private double secondRenderProgress;
   private final Timer switchTimer = new Timer();
   private final Timer mineTimer = new Timer();
   private final Timer instantTimer = new Timer();
   private final DecimalFormat df = new DecimalFormat("0.0");

   public SpeedMineTest() {
      super("SpeedMine Test", Module.Category.Player);
      this.setChinese("SpeedMine Test");
      INSTANCE = this;
   }

   @Override
   public void onEnable() {
      this.resetState(false);
   }

   @Override
   public void onDisable() {
      this.restoreSlot();
      this.hasSwitch = false;
      this.secondHasSwitch = false;
   }

   @Override
   public String getInfo() {
      return this.mainProgressPercent >= 100 ? "Done" : this.mainProgressPercent + "%";
   }

   @EventListener
   public void onStartBreak(ClickBlockEvent event) {
      if (!nullCheck() && !mc.player.isCreative()) {
         BlockPos var2 = event.getPos();
         if (this.canBreak(var2)) {
            event.cancel();
            if (this.mineTimer.passedMs((double)((long)this.mineDelay.getValue()))) {
               this.mine(var2);
            }
         }
      }
   }

   public void mine(BlockPos pos) {
      this.mineTimer.reset();
      this.maxBreaksCount = 0;
      if (this.doubleBreak.getValue()) {
         if (this.targetPos != null && this.secondPos == null && !this.targetPos.equals(pos)) {
            if (this.completed) {
               this.setMain(pos);
            } else {
               this.secondPos = this.targetPos;
               this.secondStarted = false;
               this.secondProgress = 0.0F;
               this.secondProgressPercent = 0;
               this.setMain(pos);
            }
         } else if (this.targetPos == null || !this.targetPos.equals(pos)) {
            this.setMain(pos);
         }
      } else if (this.targetPos == null || !this.targetPos.equals(pos)) {
         this.setMain(pos);
      }
   }

   private void setMain(BlockPos pos) {
      this.targetPos = pos;
      this.started = false;
      this.completed = false;
      this.progress = 0.0F;
      this.mainProgressPercent = 0;
      this.lastTime = System.currentTimeMillis();
      this.instantTimer.setMs(917813L);
   }

   @EventListener
   public void onRender(Render3DEvent event) {
      if (!nullCheck()) {
         MatrixStack var2 = event.matrixStack;
         long var3 = System.currentTimeMillis();
         double var5 = (double)(var3 - this.fadeLastTime) / 1000.0;
         this.fadeLastTime = var3;
         this.updateSlotRestore();
         if (this.targetPos != null) {
            this.renderPos = this.targetPos;
         }

         if (this.secondPos != null) {
            this.secondRenderPos = this.secondPos;
         }

         this.updateFadeProgress(var5);
         this.renderFadeBoxes(var2);
         this.processSecond(var2);
         this.processMain(var2);
      }
   }

   private void processMain(MatrixStack stack) {
      if (this.targetPos != null) {
         if (this.farCancel.getValue() && this.isOutOfRange(this.targetPos)) {
            this.targetPos = null;
         } else if (this.isAir(this.targetPos)) {
            this.completed = true;
            this.maxBreaksCount = 0;
            if (!this.instantMine.getValue()) {
               this.targetPos = null;
            }
         } else {
            double var2 = (double)this.getMineTicks(this.targetPos, this.getTool(this.targetPos));
            this.mainProgressPercent = (int)((double)this.progress / (var2 * this.mineDamage.getValue()) * 100.0);
            if ((double)this.progress >= var2 * this.mineDamage.getValue() && this.completed) {
               if (!this.isPaused()) {
                  this.maxBreaksCount++;
               }

               if (this.maxBreaksCount >= this.maxBreaks.getValueInt() * 10) {
                  this.maxBreaksCount = 0;
                  this.targetPos = null;
                  return;
               }
            }

            if (this.instantMine.getValue() && this.completed) {
               this.renderMineBox(stack, this.targetPos, 1.0, this.getColor(1.0), this.getOutlineColor(1.0), true, (double)this.progress);
               if (this.instantTimer.passedMs((double)this.instantDelay.getValueInt())) {
                  this.finishBreak(this.targetPos, false, true);
                  this.instantTimer.reset();
               }
            } else {
               long var4 = System.currentTimeMillis() - this.lastTime;
               this.lastTime = System.currentTimeMillis();
               if (!this.started) {
                  this.sendStart(this.targetPos, true);
               } else {
                  this.progress = this.progress + this.progressDelta(var4);
                  this.renderMineBox(
                     stack,
                     this.targetPos,
                     this.ease.getValue().ease(clamp((double)this.progress / (var2 * this.mineDamage.getValue()), 0.0, 1.0)),
                     this.getColor(this.fadeEase.getValue().ease(clamp((double)this.progress / (var2 * this.mineDamage.getValue()), 0.0, 1.0))),
                     this.getOutlineColor(this.fadeEase.getValue().ease(clamp((double)this.progress / (var2 * this.mineDamage.getValue()), 0.0, 1.0))),
                     true,
                     (double)this.progress
                  );
                  if ((double)this.progress >= var2 * this.mineDamage.getValue()) {
                     this.finishBreak(this.targetPos, false, true);
                     this.completed = true;
                     if (!this.instantMine.getValue() && this.secondPos == null && !this.isSafeDownMine(this.targetPos)) {
                        this.targetPos = null;
                     }
                  }
               }
            }
         }
      }
   }

   private void processSecond(MatrixStack stack) {
      if (this.secondPos != null && this.doubleBreak.getValue()) {
         if (this.farCancel.getValue() && this.isOutOfRange(this.secondPos)) {
            this.secondPos = null;
         } else if (this.isAir(this.secondPos)) {
            this.secondPos = null;
         } else {
            double var2 = (double)this.getMineTicks(this.secondPos, this.getTool(this.secondPos));
            this.secondProgressPercent = (int)((double)this.secondProgress / (var2 * this.mineDamage.getValue()) * 100.0);
            if (!this.secondStarted) {
               this.sendStart(this.secondPos, false);
            } else {
               long var4 = System.currentTimeMillis() - this.secondLastTime;
               this.secondLastTime = System.currentTimeMillis();
               this.secondProgress = this.secondProgress + this.progressDelta(var4);
               this.renderMineBox(
                  stack,
                  this.secondPos,
                  this.ease.getValue().ease(clamp((double)this.secondProgress / (var2 * this.mineDamage.getValue()), 0.0, 1.0)),
                  this.doubleSideColor.getValue(),
                  this.doubleLineColor.getValue(),
                  false,
                  (double)this.secondProgress
               );
               if ((double)this.secondProgress >= var2 * this.mineDamage.getValue()) {
                  this.finishBreak(this.secondPos, true, false);
                  if (!this.isSafeDownMine(this.secondPos)) {
                     this.secondPos = null;
                  }
               }
            }
         }
      }
   }

   private void sendStart(BlockPos pos, boolean main) {
      Direction var3 = this.getSide(pos);
      mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, pos, var3));
      if (this.fastBypass.getValue()) {
         BlockPos var4 = BlockPos.ofFloored(mc.player.getX(), 325.0, mc.player.getZ());
         mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, var4, Direction.DOWN, 0));
      }

      if (this.doubleBreak.getValue() && !this.isSafeDownMine(pos)) {
         long var5 = (long)this.packetDelay.getValue();
         final java.util.Timer var7 = new java.util.Timer();
         var7.schedule(new TimerTask() {
            @Override
            public void run() {
               Wrapper.mc.execute(() -> {
                  if (Wrapper.mc.getNetworkHandler() != null) {
                     Wrapper.mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, pos, SpeedMineTest.this.getSide(pos)));
                  }
               });
               var7.cancel();
            }
         }, var5);
      }

      if (this.swing.getValue()) {
         mc.player.swingHand(Hand.MAIN_HAND);
      }

      if (main) {
         this.started = true;
         this.progress = 0.0F;
         this.lastTime = System.currentTimeMillis();
      } else {
         this.secondStarted = true;
         this.secondProgress = 0.0F;
         this.secondLastTime = System.currentTimeMillis();
      }
   }

   private void finishBreak(BlockPos pos, boolean second, boolean allowInstant) {
      if (pos != null && !this.isPaused()) {
         this.switchTool(pos, second);
         if (this.bypassGround.getValue() && !mc.player.isFallFlying() && !this.isAir(pos)) {
            mc.getNetworkHandler().sendPacket(new PositionAndOnGround(mc.player.getX(), mc.player.getY(), mc.player.getZ(), true));
         }

         if (this.swing.getValue()) {
            mc.player.swingHand(Hand.MAIN_HAND);
         }

         mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, pos, this.getSide(pos), 0));
         if (this.clientRemove.getValue() && !this.isSafeDownMine(pos) && !this.isAir(pos)) {
            mc.interactionManager.breakBlock(pos);
         }

         if (allowInstant) {
            this.instantTimer.reset();
         }
      }
   }

   private void switchTool(BlockPos pos, boolean second) {
      int var3 = this.getTool(pos);
      if (var3 != -1 && !this.switchMode.is(SpeedMineTest.SwitchMode.None)) {
         int var4 = mc.player.getInventory().selectedSlot;
         if (!this.hasSwitch && !this.secondHasSwitch) {
            this.oldSlot = var4;
         }

         if (var3 != var4) {
            if (this.switchMode.is(SpeedMineTest.SwitchMode.Delay)) {
               InventoryUtil.switchToSlot(var3);
            } else {
               mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(var3));
            }

            this.switchTimer.reset();
            if (second) {
               this.secondHasSwitch = true;
            } else {
               this.hasSwitch = true;
            }
         }
      }
   }

   private void updateSlotRestore() {
      if ((this.hasSwitch || this.secondHasSwitch) && this.switchTimer.passedMs((double)((long)this.switchTime.getValue())) && this.oldSlot >= 0) {
         this.restoreSlot();
         this.hasSwitch = false;
         this.secondHasSwitch = false;
      }
   }

   private void restoreSlot() {
      if (!nullCheck() && this.oldSlot >= 0 && mc.getNetworkHandler() != null) {
         if (this.switchMode.is(SpeedMineTest.SwitchMode.Delay)) {
            InventoryUtil.switchToSlot(this.oldSlot);
         } else if (this.switchMode.is(SpeedMineTest.SwitchMode.Silent)) {
            mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(this.oldSlot));
         }

         this.oldSlot = -1;
      }
   }

   private float progressDelta(long deltaMs) {
      return this.checkGround.getValue() && !mc.player.isOnGround() ? (float)((double)deltaMs / 1000.0 * 4.0) : (float)((double)deltaMs / 1000.0 * 20.0);
   }

   private boolean isSafeDownMine(BlockPos pos) {
      if (this.safeDownMine.getValue() && pos != null && mc.player != null) {
         BlockPos var2 = mc.player.getBlockPos();
         return pos.equals(var2.down())
            || pos.getY() < var2.getY()
               && Math.abs((double)pos.getX() + 0.5 - mc.player.getX()) < 0.9
               && Math.abs((double)pos.getZ() + 0.5 - mc.player.getZ()) < 0.9;
      } else {
         return false;
      }
   }

   private Direction getSide(BlockPos pos) {
      Direction var2 = BlockUtil.getClickSide(pos);
      if (var2 != null) {
         return var2;
      } else {
         return this.isSafeDownMine(pos) ? Direction.UP : Direction.DOWN;
      }
   }

   private boolean canBreak(BlockPos pos) {
      if (pos != null && mc.world != null && mc.player != null) {
         BlockState var2 = mc.world.getBlockState(pos);
         if (!var2.isAir() && !var2.isReplaceable()) {
            return !mc.player.isCreative() && var2.getHardness(mc.world, pos) < 0.0F ? false : !var2.getCollisionShape(mc.world, pos).isEmpty();
         } else {
            return false;
         }
      } else {
         return false;
      }
   }

   private boolean isOutOfRange(BlockPos pos) {
      return mc.player.getEyePos().squaredDistanceTo(pos.toCenterPos()) > this.range.getValue() * this.range.getValue();
   }

   private boolean isPaused() {
      return this.usingPause.getValue() && mc.options.useKey.isPressed() && (!this.onlyMain.getValue() || mc.player.getActiveHand() == Hand.MAIN_HAND);
   }

   private boolean isAir(BlockPos pos) {
      if (pos != null && mc.world != null) {
         BlockState var2 = mc.world.getBlockState(pos);
         return var2.isAir() || var2.isReplaceable();
      } else {
         return true;
      }
   }

   private float getMineTicks(BlockPos pos, int slot) {
      if (pos == null) {
         return 20.0F;
      } else {
         BlockState var3 = mc.world.getBlockState(pos);
         float var4 = var3.getHardness(mc.world, pos);
         if (var4 < 0.0F) {
            return Float.MAX_VALUE;
         } else if (var4 == 0.0F) {
            return 1.0F;
         } else {
            ItemStack var5 = slot == -1 ? ItemStack.EMPTY : mc.player.getInventory().getStack(slot);
            boolean var6 = !var3.isToolRequired() || var5.isSuitableFor(var3);
            float var7 = var5.getMiningSpeedMultiplier(var3);
            int var8 = DamageUtils.getEnchantmentLevel(var5, Enchantments.EFFICIENCY);
            if (var8 > 0 && var7 > 1.0F) {
               var7 += (float)(var8 * var8) + 1.0F;
            }

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

            if (mc.player.hasStatusEffect(StatusEffects.MINING_FATIGUE)) {
               int var10 = mc.player.getStatusEffect(StatusEffects.MINING_FATIGUE).getAmplifier();

               var7 *= switch (var10) {
                  case 0 -> 0.3F;
                  case 1 -> 0.09F;
                  case 2 -> 0.0027F;
                  default -> 8.1E-4F;
               };
            }

            float var11 = var7 / var4 / (var6 ? 30.0F : 100.0F);
            return var11 <= 0.0F ? Float.MAX_VALUE : 1.0F / var11;
         }
      }
   }

   private int getTool(BlockPos pos) {
      int var2 = -1;
      float var3 = 1.0F;

      for (int var4 = 0; var4 < 9; var4++) {
         ItemStack var5 = mc.player.getInventory().getStack(var4);
         if (!var5.isEmpty()) {
            float var6 = (float)DamageUtils.getEnchantmentLevel(var5, Enchantments.EFFICIENCY) + var5.getMiningSpeedMultiplier(mc.world.getBlockState(pos));
            if (var6 > var3) {
               var3 = var6;
               var2 = var4;
            }
         }
      }

      return var2;
   }

   private void resetState(boolean restoreSlot) {
      if (restoreSlot) {
         this.restoreSlot();
      }

      this.targetPos = null;
      this.secondPos = null;
      this.renderPos = null;
      this.secondRenderPos = null;
      this.started = false;
      this.secondStarted = false;
      this.completed = false;
      this.hasSwitch = false;
      this.secondHasSwitch = false;
      this.maxBreaksCount = 0;
      this.progress = 0.0F;
      this.secondProgress = 0.0F;
      this.mainProgressPercent = 0;
      this.secondProgressPercent = 0;
      this.renderProgress = 0.0;
      this.secondRenderProgress = 0.0;
      this.lastTime = System.currentTimeMillis();
      this.secondLastTime = System.currentTimeMillis();
      this.fadeLastTime = System.currentTimeMillis();
      this.mineTimer.setMs(917813L);
      this.instantTimer.setMs(917813L);
      this.switchTimer.setMs(917813L);
   }

   private void updateFadeProgress(double delta) {
      if (!this.fading.getValue()) {
         this.renderProgress = 0.0;
         this.secondRenderProgress = 0.0;
      } else {
         boolean var3 = this.isPaused();
         if (this.isSolidForFade(this.renderPos) && !var3) {
            this.renderProgress = this.fadeTime.getValue() + this.renderTime.getValue();
         } else {
            this.renderProgress = Math.max(0.0, this.renderProgress - delta);
         }

         if (this.isSolidForFade(this.secondRenderPos) && !var3) {
            this.secondRenderProgress = this.fadeTime.getValue() + this.renderTime.getValue();
         } else {
            this.secondRenderProgress = Math.max(0.0, this.secondRenderProgress - delta);
         }
      }
   }

   private boolean isSolidForFade(BlockPos pos) {
      return pos != null && !this.isAir(pos);
   }

   private void renderFadeBoxes(MatrixStack stack) {
      if (this.fading.getValue()) {
         this.renderFadeBox(stack, this.renderPos, this.renderProgress);
         this.renderFadeBox(stack, this.secondRenderPos, this.secondRenderProgress);
      }
   }

   private void renderFadeBox(MatrixStack stack, BlockPos pos, double progress) {
      if (pos != null && !(progress <= 0.0) && !this.isSolidForFade(pos)) {
         double var5 = Math.min(1.0, progress / Math.max(0.01, this.fadeTime.getValue()));
         Color var7 = new Color(
            this.fadeSideColor.getValue().getRed(),
            this.fadeSideColor.getValue().getGreen(),
            this.fadeSideColor.getValue().getBlue(),
            (int)((double)this.fadeSideColor.getValue().getAlpha() * var5)
         );
         Color var8 = new Color(
            this.fadeLineColor.getValue().getRed(),
            this.fadeLineColor.getValue().getGreen(),
            this.fadeLineColor.getValue().getBlue(),
            (int)((double)this.fadeLineColor.getValue().getAlpha() * var5)
         );
         Render3DUtil.drawFill(stack, new Box(pos), var7);
         Render3DUtil.drawBox(stack, new Box(pos), var8);
      }
   }

   private void renderMineBox(MatrixStack stack, BlockPos pos, double eased, Color fill, Color line, boolean drawText, double progressValue) {
      if (this.box.getValue()) {
         Render3DUtil.drawFill(stack, this.getFillBox(pos, eased), fill);
      }

      if (this.outline.getValue()) {
         Render3DUtil.drawBox(stack, this.getOutlineBox(pos, eased), line);
      }

      if (drawText && this.text.getValue()) {
         double var10 = clamp(progressValue / ((double)this.getMineTicks(pos, this.getTool(pos)) * this.mineDamage.getValue()), 0.0, 1.0) * 100.0;
         Render3DUtil.drawText3D(this.isAir(pos) ? "Waiting" : this.df.format(var10) + "%", pos.toCenterPos(), -1);
      }
   }

   private Box getFillBox(BlockPos pos, double eased) {
      return switch ((SpeedMineTest.Animation)this.animation.getValue()) {
         case Center -> {
            double var6 = (1.0 - eased) / 2.0;
            yield new Box(pos).shrink(var6, var6, var6).shrink(-var6, -var6, -var6);
         }
         case Grow -> {
            double var4 = (1.0 - eased) / 2.0;
            yield new Box(pos).shrink(var4, 0.0, var4).shrink(-var4, 0.0, -var4);
         }
         case Up -> new Box(
         (double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), (double)pos.getX() + 1.0, (double)pos.getY() + eased, (double)pos.getZ() + 1.0
      );
         case Down -> new Box(
         (double)pos.getX(), (double)pos.getY() + 1.0 - eased, (double)pos.getZ(), (double)pos.getX() + 1.0, (double)pos.getY() + 1.0, (double)pos.getZ() + 1.0
      );
         case Oscillation -> new Box(pos).shrink(eased, eased, eased).shrink(-eased, -eased, -eased);
         case None -> new Box(pos);
      };
   }

   private Box getOutlineBox(BlockPos pos, double eased) {
      return this.getFillBox(pos, Math.min(eased + this.expandLine.getValue(), 1.0));
   }

   private Color getColor(double progress) {
      return this.lerpColor(this.sideStart.getValue(), this.sideEnd.getValue(), progress);
   }

   private Color getOutlineColor(double progress) {
      return this.lerpColor(this.lineStart.getValue(), this.lineEnd.getValue(), progress);
   }

   private Color lerpColor(Color start, Color end, double progress) {
      double var5 = clamp(progress, 0.0, 1.0);
      int var7 = (int)((double)start.getRed() + (double)(end.getRed() - start.getRed()) * var5);
      int var8 = (int)((double)start.getGreen() + (double)(end.getGreen() - start.getGreen()) * var5);
      int var9 = (int)((double)start.getBlue() + (double)(end.getBlue() - start.getBlue()) * var5);
      int var10 = (int)((double)start.getAlpha() + (double)(end.getAlpha() - start.getAlpha()) * var5);
      return new Color(var7, var8, var9, var10);
   }

   private static double clamp(double value, double min, double max) {
      return Math.max(min, Math.min(max, value));
   }
@Environment(EnvType.CLIENT)
   private static enum Animation {
      Center,
      Grow,
      Up,
      Down,
      Oscillation,
      None;

}

   @Environment(EnvType.CLIENT)
   private static enum SwitchMode {
      None,
      Delay,
      Silent;

}
}
