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

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.UpdateEvent;
import dev.crucifixbeta.api.utils.combat.CombatUtil;
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.player.MovementUtil;
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.client.ClientSetting;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.ColorSetting;
import dev.crucifixbeta.mod.modules.settings.impl.EnumSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import java.awt.Color;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.FacingBlock;
import net.minecraft.block.PistonBlock;
import net.minecraft.block.PistonHeadBlock;
import net.minecraft.block.RedstoneTorchBlock;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.decoration.EndCrystalEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.Items;
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;

@Environment(EnvType.CLIENT)
public class PistonCrystal extends Module {
   public static PistonCrystal INSTANCE;
   private final BooleanSetting rotate = this.add(new BooleanSetting("Rotate", false));
   private final BooleanSetting pistonPacket = this.add(new BooleanSetting("PistonPacket", false));
   private final BooleanSetting noEating = this.add(new BooleanSetting("NoEating", true));
   private final BooleanSetting eatingBreak = this.add(new BooleanSetting("EatingBreak", false));
   private final BooleanSetting aggressive = this.add(new BooleanSetting("Aggressive", false));
   private final SliderSetting burst = this.add(new SliderSetting("Burst", 2, 1, 5, this.aggressive::getValue));
   private final BooleanSetting forceBreak = this.add(new BooleanSetting("ForceBreak", true, this.aggressive::getValue));
   private final SliderSetting minDamage = this.add(new SliderSetting("MinDamage", 4.0, 0.0, 36.0));
   private final SliderSetting maxSelfDmg = this.add(new SliderSetting("MaxSelfDmg", 12.0, 0.0, 36.0));
   private final BooleanSetting noSuicide = this.add(new BooleanSetting("NoSuicide", true));
   private final BooleanSetting yawDeceive = this.add(new BooleanSetting("YawDeceive", true));
   private final BooleanSetting mine = this.add(new BooleanSetting("Mine", true));
   private final BooleanSetting helpPlace = this.add(new BooleanSetting("HelpPlace", false));
   private final EnumSetting<PistonCrystal.RedstoneMode> redStoneMode = this.add(
      new EnumSetting<>("RedstoneMode", PistonCrystal.RedstoneMode.Block)
   );
   private final BooleanSetting postPushBreak = this.add(new BooleanSetting("PostPushBreak", true));
   private final SliderSetting breakDelay = this.add(new SliderSetting("BreakDelay", 300, 0, 1000));
   private final SliderSetting placeRange = this.add(new SliderSetting("PlaceRange", 5.0, 1.0, 8.0));
   private final SliderSetting range = this.add(new SliderSetting("Range", 4.0, 1.0, 8.0));
   private final BooleanSetting fire = this.add(new BooleanSetting("Fire", true));
   private final BooleanSetting switchPos = this.add(new BooleanSetting("Switch", false));
   private final BooleanSetting onlyGround = this.add(new BooleanSetting("SelfGround", true));
   private final BooleanSetting onlyStatic = this.add(new BooleanSetting("MovingPause", true));
   private final SliderSetting updateDelay = this.add(new SliderSetting("PlaceDelay", 100, 0, 500));
   private final SliderSetting posUpdateDelay = this.add(new SliderSetting("PosUpdateDelay", 500, 0, 1000));
   private final SliderSetting stageSetting = this.add(new SliderSetting("Stage", 4, 1, 10));
   private final SliderSetting pistonStage = this.add(new SliderSetting("PistonStage", 1, 1, 10));
   private final SliderSetting pistonMaxStage = this.add(new SliderSetting("PistonMaxStage", 1, 1, 10));
   private final SliderSetting powerStage = this.add(new SliderSetting("PowerStage", 3, 1, 10));
   private final SliderSetting powerMaxStage = this.add(new SliderSetting("PowerMaxStage", 3, 1, 10));
   private final SliderSetting crystalStage = this.add(new SliderSetting("CrystalStage", 4, 1, 10));
   private final SliderSetting crystalMaxStage = this.add(new SliderSetting("CrystalMaxStage", 4, 1, 10));
   private final SliderSetting fireStage = this.add(new SliderSetting("FireStage", 2, 1, 10));
   private final SliderSetting fireMaxStage = this.add(new SliderSetting("FireMaxStage", 2, 1, 10));
   private final BooleanSetting inventory = this.add(new BooleanSetting("InventorySwap", true));
   private final BooleanSetting debug = this.add(new BooleanSetting("Debug", false));
   private final BooleanSetting smartSwitch = this.add(new BooleanSetting("SmartSwitch", true));
   private final BooleanSetting render = this.add(new BooleanSetting("Render", true));
   private final ColorSetting crystalColor = this.add(
      new ColorSetting("CrystalColor", new Color(255, 0, 0, 50), this.render::getValue).injectBoolean(true)
   );
   private final ColorSetting pistonColor = this.add(
      new ColorSetting("PistonColor", new Color(255, 255, 255, 50), this.render::getValue).injectBoolean(true)
   );
   private final ColorSetting redstoneColor = this.add(
      new ColorSetting("RedstoneColor", new Color(255, 100, 0, 50), this.render::getValue).injectBoolean(true)
   );
   private final Timer timer = new Timer();
   private final Timer crystalTimer = new Timer();
   private final Timer postBreakTimer = new Timer();
   public BlockPos bestPos = null;
   public BlockPos bestOPos = null;
   public Direction bestFacing = null;
   public double getDistance = 100.0;
   private double bestDistanceSq = Double.POSITIVE_INFINITY;
   public boolean getPos = false;
   public int stage = 1;
   private PlayerEntity target = null;
   private boolean isPiston = false;
   private int cachedPistonSlot = -1;
   private int cachedPowerSlot = -1;
   private int cachedTorchSlot = -1;
   private int cachedCrystalSlot = -1;
   private int cachedFireSlot = -1;
   private int slotCacheTick = -1;
   private int lastPistonActionTick = -100;
   private int lastHelpPlaceTick = -100;
   private int lastPistonSlot = -1;
   private int lastPowerSlot = -1;
   private int lastCrystalSlot = -1;
   private int lastFireSlot = -1;
   private BlockPos lastHelpPos = null;
   private BlockPos lastCrystalPos = null;
   private BlockPos renderCrystalPos = null;
   private BlockPos renderPistonPos = null;
   private BlockPos renderPowerPos = null;

   public PistonCrystal() {
      super("PistonCrystal", Module.Category.Combat);
      this.setChinese("\u6d3b\u585e\u6c34\u6676");
      INSTANCE = this;
   }

   private static boolean canFire(BlockPos pos) {
      if (BlockUtil.canReplace(pos.down())) {
         return false;
      } else if (!mc.world.isAir(pos)) {
         return false;
      } else {
         return !BlockUtil.canClick(pos.offset(Direction.DOWN)) ? false : BlockUtil.isStrictDirection(pos.down(), Direction.UP);
      }
   }

   public void onTick() {
      if (this.pistonStage.getValue() > this.stageSetting.getValue()) {
         this.pistonStage.setValue(this.stageSetting.getValue());
      }

      if (this.fireStage.getValue() > this.stageSetting.getValue()) {
         this.fireStage.setValue(this.stageSetting.getValue());
      }

      if (this.powerStage.getValue() > this.stageSetting.getValue()) {
         this.powerStage.setValue(this.stageSetting.getValue());
      }

      if (this.crystalStage.getValue() > this.stageSetting.getValue()) {
         this.crystalStage.setValue(this.stageSetting.getValue());
      }

      if (this.pistonMaxStage.getValue() > this.stageSetting.getValue()) {
         this.pistonMaxStage.setValue(this.stageSetting.getValue());
      }

      if (this.fireMaxStage.getValue() > this.stageSetting.getValue()) {
         this.fireMaxStage.setValue(this.stageSetting.getValue());
      }

      if (this.powerMaxStage.getValue() > this.stageSetting.getValue()) {
         this.powerMaxStage.setValue(this.stageSetting.getValue());
      }

      if (this.crystalMaxStage.getValue() > this.stageSetting.getValue()) {
         this.crystalMaxStage.setValue(this.stageSetting.getValue());
      }

      if (this.crystalMaxStage.getValue() < this.crystalStage.getValue()) {
         this.crystalStage.setValue(this.crystalMaxStage.getValue());
      }

      if (this.powerMaxStage.getValue() < this.powerStage.getValue()) {
         this.powerStage.setValue(this.powerMaxStage.getValue());
      }

      if (this.pistonMaxStage.getValue() < this.pistonStage.getValue()) {
         this.pistonStage.setValue(this.pistonMaxStage.getValue());
      }

      if (this.fireMaxStage.getValue() < this.fireStage.getValue()) {
         this.fireStage.setValue(this.fireMaxStage.getValue());
      }
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      this.onTick();
      this.target = CombatUtil.getClosestEnemy(this.range.getValue());
      if (this.target != null) {
         boolean var2 = this.aggressive.getValue();
         if (!this.noEating.getValue() || !mc.player.isUsingItem() || var2) {
            this.refreshSlots();
            BlockPos var3 = EntityUtil.getEntityPos(this.target, true);
            if (!this.check(this.onlyStatic.getValue(), !mc.player.isOnGround(), this.onlyGround.getValue())) {
               if (this.tryUseCombatTask()) {
                  if (!mc.player.isUsingItem() || this.eatingBreak.getValue() || var2) {
                     if (this.checkCrystal(var3.up(0))) {
                        CombatUtil.attackCrystal(var3.up(0), this.rotate.getValue(), true);
                     }

                     if (this.checkCrystal(var3.up(1))) {
                        CombatUtil.attackCrystal(var3.up(1), this.rotate.getValue(), true);
                     }

                     if (this.checkCrystal(var3.up(2))) {
                        CombatUtil.attackCrystal(var3.up(2), this.rotate.getValue(), true);
                     }

                     this.breakPushedCrystals(var3);
                  }

                  if (this.bestPos != null && mc.world.getBlockState(this.bestPos).getBlock() instanceof PistonBlock) {
                     this.isPiston = true;
                     this.tryMinePiston(this.bestPos, this.bestFacing);
                  } else if (this.isPiston) {
                     this.isPiston = false;
                     this.crystalTimer.reset();
                     this.bestPos = null;
                     this.bestOPos = null;
                     this.bestFacing = null;
                     this.getPos = false;
                     this.getDistance = 100.0;
                     this.bestDistanceSq = Double.POSITIVE_INFINITY;
                     this.renderPistonPos = null;
                     this.renderCrystalPos = null;
                     this.renderPowerPos = null;
                  }

                  if (var2 || this.crystalTimer.passedMs((double)this.posUpdateDelay.getValueInt())) {
                     this.stage = 0;
                     this.getDistance = 100.0;
                     this.bestDistanceSq = Double.POSITIVE_INFINITY;
                     this.getPos = false;
                     this.getBestPos(var3.up());
                     this.getBestPos(var3.up(2));
                  }

                  if (var2 || this.timer.passedMs((double)this.updateDelay.getValueInt())) {
                     if (this.getPos && this.bestPos != null) {
                        if (this.shouldYieldToAutoCrystal()) {
                           this.releaseCombatTask();
                           return;
                        }

                        this.timer.reset();
                        if (this.debug.getValue()) {
                           this.sendMessage(
                              "[Debug] PistonPos:" + this.bestPos + " Facing:" + this.bestFacing + " CrystalPos:" + this.bestOPos.offset(this.bestFacing)
                           );
                        }

                        if (var2) {
                           int var4 = this.burst.getValueInt();

                           for (int var5 = 0; var5 < var4; var5++) {
                              this.doPistonAuraAggressive(this.bestPos, this.bestFacing, this.bestOPos);
                           }
                        } else {
                           this.doPistonAura(this.bestPos, this.bestFacing, this.bestOPos);
                        }

                        if (!mc.player.isUsingItem() || this.eatingBreak.getValue() || var2) {
                           this.breakPushedCrystals(var3);
                        }
                     }
                  }
               }
            }
         }
      }
   }

   public boolean check(boolean onlyStatic, boolean onGround, boolean onlyGround) {
      if (MovementUtil.isMoving() && onlyStatic) {
         return true;
      } else if (onGround && onlyGround) {
         return true;
      } else if (this.redStoneMode.is(PistonCrystal.RedstoneMode.Block) && this.cachedPowerSlot == -1) {
         return true;
      } else if (this.redStoneMode.is(PistonCrystal.RedstoneMode.Torch) && this.cachedTorchSlot == -1) {
         return true;
      } else {
         return this.cachedPistonSlot == -1 ? true : this.cachedCrystalSlot == -1;
      }
   }

   private boolean checkCrystal(BlockPos pos) {
      if (this.aggressive.getValue() && this.forceBreak.getValue()) {
         return !BlockUtil.getEndCrystals(new Box(pos)).isEmpty();
      } else {
         for (EndCrystalEntity var3 : BlockUtil.getEndCrystals(new Box(pos))) {
            if (AutoCrystal.INSTANCE.calculateDamage(var3.getPos(), this.target, this.target) > 7.0F) {
               return true;
            }
         }

         return false;
      }
   }

   private boolean checkCrystal2(BlockPos pos) {
      for (EndCrystalEntity var3 : BlockUtil.getEndCrystals(new Box(pos))) {
         if (EntityUtil.getEntityPos(var3).equals(pos)) {
            return true;
         }
      }

      return false;
   }

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

   private void getBestPos(BlockPos pos) {
      if (this.validDamage(pos)) {
         for (Direction var5 : Direction.values()) {
            if (var5 != Direction.DOWN && var5 != Direction.UP) {
               this.getPos(pos, var5);
            }
         }
      }
   }

   private void getPos(BlockPos pos, Direction i) {
      BlockPos var3 = pos.offset(i);
      if (this.checkCrystal2(var3) || BlockUtil.canPlaceCrystal(var3)) {
         int var4 = i.getOffsetZ();
         int var5 = i.getOffsetX();
         BlockPos var6 = pos.offset(i, 3);
         BlockPos var7 = pos.offset(i, 2);
         this.getPos(var6, i, pos);
         this.getPos(var6.up(), i, pos);
         this.getPos(var6.add(var4, 0, var5), i, pos);
         this.getPos(var6.add(-var4, 0, -var5), i, pos);
         this.getPos(var6.add(var4, 1, var5), i, pos);
         this.getPos(var6.add(-var4, 1, -var5), i, pos);
         this.getPos(var7, i, pos);
         this.getPos(var7.up(), i, pos);
         this.getPos(var7.add(var4, 0, var5), i, pos);
         this.getPos(var7.add(-var4, 0, -var5), i, pos);
         this.getPos(var7.add(var4, 1, var5), i, pos);
         this.getPos(var7.add(-var4, 1, -var5), i, pos);
      }
   }

   private void getPos(BlockPos pos, Direction facing, BlockPos oPos) {
      if (!this.switchPos.getValue() || this.bestPos == null || !this.bestPos.equals(pos) || !mc.world.isAir(this.bestPos)) {
         if (this.cachedPistonSlot != -1) {
            double var4 = mc.player.getX();
            double var6 = mc.player.getEyeY();
            double var8 = mc.player.getZ();
            double var10 = (double)pos.getX() + 0.5 - var4;
            double var12 = (double)pos.getY() + 0.5 - var6;
            double var14 = (double)pos.getZ() + 0.5 - var8;
            double var16 = var10 * var10 + var12 * var12 + var14 * var14;
            if (!this.getPos || !(var16 >= this.bestDistanceSq)) {
               Block var18 = this.getBlock(pos);
               boolean var19 = !(var18 instanceof PistonBlock) && BlockUtil.canPlace(pos, this.placeRange.getValue());
               if (var19 || var18 instanceof PistonBlock) {
                  if (!ClientSetting.INSTANCE.lowVersion.getValue()
                     || var18 instanceof PistonBlock
                     || !(mc.player.getY() - (double)pos.getY() <= -2.0) && !(mc.player.getY() - (double)pos.getY() >= 3.0)
                     || !(BlockUtil.distanceToXZ((double)pos.getX() + 0.5, (double)pos.getZ() + 0.5) < 2.6)) {
                     BlockPos var20 = pos.offset(facing.getOpposite());
                     Block var21 = this.getBlock(var20);
                     if (mc.world.isAir(var20) || var21 == Blocks.FIRE || var21 == Blocks.MOVING_PISTON) {
                        if (var21 != Blocks.MOVING_PISTON || this.checkCrystal2(var20)) {
                           if (var19 || this.isPiston(pos, facing)) {
                              this.bestPos = pos;
                              this.bestOPos = oPos;
                              this.bestFacing = facing;
                              this.renderPistonPos = pos;
                              this.renderCrystalPos = oPos.offset(facing);
                              this.bestDistanceSq = var16;
                              this.getDistance = (double)MathHelper.sqrt((float)var16);
                              this.getPos = true;
                              this.crystalTimer.reset();
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }

   private void doPistonAura(BlockPos pos, Direction facing, BlockPos oPos) {
      if ((double)this.stage >= this.stageSetting.getValue()) {
         this.stage = 0;
      }

      this.stage++;
      if (mc.world.isAir(pos)) {
         if (!BlockUtil.canPlace(pos)) {
            this.tryPlaceHelp(pos, facing);
            return;
         }

         if ((double)this.stage >= this.pistonStage.getValue() && (double)this.stage <= this.pistonMaxStage.getValue()) {
            Direction var4 = BlockUtil.getPlaceSide(pos);
            if (var4 == null) {
               return;
            }

            int var5 = mc.player.getInventory().selectedSlot;
            if (this.yawDeceive.getValue()) {
               AutoPush.pistonFacing(facing);
            }

            int var6 = this.getPistonSlot();
            if (var6 == -1) {
               return;
            }

            this.doSwap(var6);
            BlockUtil.placeBlock(pos, false, this.pistonPacket.getValue());
            this.markPistonAction();
            if (this.inventory.getValue()) {
               this.doSwap(var6);
               EntityUtil.syncInventory();
            } else {
               this.doSwap(var5);
            }

            BlockPos var7 = pos.offset(var4);
            Direction var8 = var4.getOpposite();
            if (this.rotate.getValue()) {
               CrucifixBeta.ROTATION.lookAt(var7, var8);
            }
         }
      }

      if ((double)this.stage >= this.powerStage.getValue() && (double)this.stage <= this.powerMaxStage.getValue()) {
         this.doRedStone(pos, facing, oPos.offset(facing));
      }

      if ((double)this.stage >= this.crystalStage.getValue() && (double)this.stage <= this.crystalMaxStage.getValue()) {
         this.placeCrystal(oPos, facing);
      }

      if ((double)this.stage >= this.fireStage.getValue() && (double)this.stage <= this.fireMaxStage.getValue()) {
         this.doFire(oPos, facing);
      }
   }

   private void doPistonAuraAggressive(BlockPos pos, Direction facing, BlockPos oPos) {
      if (mc.world.isAir(pos)) {
         if (!BlockUtil.canPlace(pos)) {
            return;
         }

         Direction var4 = BlockUtil.getPlaceSide(pos);
         if (var4 == null) {
            return;
         }

         int var5 = mc.player.getInventory().selectedSlot;
         if (this.yawDeceive.getValue()) {
            AutoPush.pistonFacing(facing);
         }

         int var6 = this.getPistonSlot();
         if (var6 == -1) {
            return;
         }

         this.doSwap(var6);
         BlockUtil.placeBlock(pos, false, this.pistonPacket.getValue());
         this.markPistonAction();
         if (this.inventory.getValue()) {
            this.doSwap(var6);
            EntityUtil.syncInventory();
         } else {
            this.doSwap(var5);
         }

         BlockPos var7 = pos.offset(var4);
         Direction var8 = var4.getOpposite();
         if (this.rotate.getValue()) {
            CrucifixBeta.ROTATION.lookAt(var7, var8);
         }

         if (mc.world.isAir(pos)) {
            return;
         }
      }

      this.doRedStone(pos, facing, oPos.offset(facing));
      this.placeCrystal(oPos, facing);
      this.doFire(oPos, facing);
   }

   private void placeCrystal(BlockPos pos, Direction facing) {
      if (BlockUtil.canPlaceCrystal(pos.offset(facing))) {
         int var3 = this.getCrystalSlot();
         if (var3 != -1) {
            int var4 = mc.player.getInventory().selectedSlot;
            this.doSwap(var3);
            BlockUtil.placeCrystal(pos.offset(facing), true);
            this.lastCrystalPos = pos.offset(facing);
            this.markPistonAction();
            if (this.inventory.getValue()) {
               this.doSwap(var3);
               EntityUtil.syncInventory();
            } else {
               this.doSwap(var4);
            }
         }
      }
   }

   private boolean isPiston(BlockPos pos, Direction facing) {
      if (!(mc.world.getBlockState(pos).getBlock() instanceof PistonBlock)) {
         return false;
      } else {
         return mc.world.getBlockState(pos).get(FacingBlock.FACING).getOpposite() != facing
            ? false
            : mc.world.isAir(pos.offset(facing, -1))
               || this.getBlock(pos.offset(facing, -1)) == Blocks.FIRE
               || this.getBlock(pos.offset(facing.getOpposite())) == Blocks.MOVING_PISTON;
      }
   }

   private void doFire(BlockPos pos, Direction facing) {
      if (this.fire.getValue()) {
         int var3 = this.getFireSlot();
         if (var3 != -1) {
            int var4 = mc.player.getInventory().selectedSlot;
            int[] var5 = new int[]{0, facing.getOffsetZ(), -facing.getOffsetZ()};
            int[] var6 = new int[]{0, 1};
            int[] var7 = new int[]{0, facing.getOffsetX(), -facing.getOffsetX()};

            for (int var11 : var5) {
               for (int var15 : var6) {
                  for (int var19 : var7) {
                     if (this.getBlock(pos.add(var11, var15, var19)) == Blocks.FIRE) {
                        return;
                     }
                  }
               }
            }

            for (int var23 : var5) {
               for (int var27 : var6) {
                  for (int var31 : var7) {
                     if (canFire(pos.add(var23, var27, var31))) {
                        this.doSwap(var3);
                        this.placeFire(pos.add(var23, var27, var31));
                        if (this.inventory.getValue()) {
                           this.doSwap(var3);
                           EntityUtil.syncInventory();
                        } else {
                           this.doSwap(var4);
                        }

                        return;
                     }
                  }
               }
            }
         }
      }
   }

   public void placeFire(BlockPos pos) {
      if (this.tryUseCombatTask()) {
         BlockPos var2 = pos.offset(Direction.DOWN);
         BlockUtil.clickBlock(var2, Direction.UP, this.rotate.getValue());
      }
   }

   private void doRedStone(BlockPos pos, Direction facing, BlockPos crystalPos) {
      BlockPos var4 = pos.offset(facing.getOpposite());
      Block var5 = this.getBlock(var4);
      if (mc.world.isAir(var4) || var5 == Blocks.FIRE || var5 == Blocks.MOVING_PISTON) {
         for (Direction var9 : Direction.values()) {
            Block var10 = this.getBlock(pos.offset(var9));
            if (var10 == Blocks.REDSTONE_BLOCK || var10 instanceof RedstoneTorchBlock) {
               return;
            }
         }

         int var13 = this.getPowerSlot();
         if (var13 != -1) {
            if (var5 != Blocks.MOVING_PISTON || this.checkCrystal2(var4)) {
               int var14 = mc.player.getInventory().selectedSlot;
               Direction var15 = BlockUtil.getBestNeighboring(pos, facing);
               if (var15 != null
                  && var15 != facing.getOpposite()
                  && BlockUtil.canPlace(pos.offset(var15), this.placeRange.getValue())
                  && !pos.offset(var15).equals(crystalPos)) {
                  this.placePower(pos.offset(var15), var13, var14);
               } else {
                  for (Direction var12 : Direction.values()) {
                     if (BlockUtil.canPlace(pos.offset(var12), this.placeRange.getValue())
                        && !pos.offset(var12).equals(crystalPos)
                        && var12 != facing.getOpposite()) {
                        this.placePower(pos.offset(var12), var13, var14);
                        return;
                     }
                  }
               }
            }
         }
      }
   }

   private void markPistonAction() {
      if (mc.player != null) {
         this.lastPistonActionTick = mc.player.age;
      }
   }

   private boolean validDamage(BlockPos pos) {
      if (AutoCrystal.INSTANCE != null && this.target != null && mc.player != null) {
         Vec3d var2 = new Vec3d((double)pos.getX() + 0.5, (double)pos.getY(), (double)pos.getZ() + 0.5);
         float var3 = AutoCrystal.INSTANCE.calculateDamage(var2, this.target, this.target);
         float var4 = AutoCrystal.INSTANCE.calculateDamage(var2, mc.player, mc.player);
         return this.noSuicide.getValue() && var4 > EntityUtil.getHealth(mc.player)
            ? false
            : (double)var3 >= this.minDamage.getValue() && (double)var4 <= this.maxSelfDmg.getValue();
      } else {
         return true;
      }
   }

   private boolean shouldYieldToAutoCrystal() {
      AutoCrystal var1 = AutoCrystal.INSTANCE;
      if (var1 != null && var1.isOn() && var1.canExecuteNow()) {
         float var2 = var1.getBestDamageEstimate();
         float var3 = this.getBestPistonDamage();
         return var2 > var3;
      } else {
         return false;
      }
   }

   private float getBestPistonDamage() {
      if (AutoCrystal.INSTANCE != null && this.target != null && this.bestOPos != null && this.bestFacing != null) {
         BlockPos var1 = this.bestOPos.offset(this.bestFacing);
         Vec3d var2 = new Vec3d((double)var1.getX() + 0.5, (double)var1.getY(), (double)var1.getZ() + 0.5);
         return AutoCrystal.INSTANCE.calculateDamage(var2, this.target, this.target);
      } else {
         return 0.0F;
      }
   }

   private int getPowerSlot() {
      if (this.redStoneMode.is(PistonCrystal.RedstoneMode.Torch)) {
         int var2 = this.smartSwitch.getValue() && this.lastPowerSlot != -1 && InventoryUtil.isItemInSlot(this.lastPowerSlot, Items.REDSTONE_TORCH)
            ? this.lastPowerSlot
            : -1;
         return var2 != -1 ? var2 : this.cachedTorchSlot;
      } else {
         int var1 = this.smartSwitch.getValue() && this.lastPowerSlot != -1 && InventoryUtil.isItemInSlot(this.lastPowerSlot, Blocks.REDSTONE_BLOCK)
            ? this.lastPowerSlot
            : -1;
         return var1 != -1 ? var1 : this.cachedPowerSlot;
      }
   }

   private void placePower(BlockPos pos, int power, int old) {
      if (this.tryUseCombatTask()) {
         if (this.redStoneMode.is(PistonCrystal.RedstoneMode.Torch)) {
            if (!this.placeTorch(pos, power, old)) {
               return;
            }
         } else {
            this.doSwap(power);
            BlockUtil.placeBlock(pos, this.rotate.getValue());
            this.restoreSlot(old, power);
         }

         this.renderPowerPos = pos;
         this.lastPowerSlot = power;
         this.markPistonAction();
      }
   }

   private boolean placeTorch(BlockPos pos, int slot, int old) {
      if (!BlockUtil.canPlace(pos, this.placeRange.getValue())) {
         return false;
      } else {
         for (Direction var7 : Direction.values()) {
            if (var7 != Direction.UP
               && !(this.getBlock(pos.offset(var7)) instanceof PistonBlock)
               && BlockUtil.canClick(pos.offset(var7))
               && !BlockUtil.canReplace(pos.offset(var7))) {
               this.doSwap(slot);
               BlockUtil.clickBlock(pos.offset(var7), var7.getOpposite(), this.rotate.getValue());
               this.restoreSlot(old, slot);
               return true;
            }
         }

         return false;
      }
   }

   private void restoreSlot(int old, int swapped) {
      if (this.inventory.getValue()) {
         this.doSwap(swapped);
         EntityUtil.syncInventory();
      } else {
         this.doSwap(old);
      }
   }

   private boolean tryPlaceHelp(BlockPos pistonPos, Direction facing) {
      if (!this.helpPlace.getValue()) {
         return false;
      } else if (mc.player.age == this.lastHelpPlaceTick) {
         return false;
      } else if (this.lastHelpPos == null
         || this.getBlock(this.lastHelpPos) != Blocks.REDSTONE_BLOCK && !(this.getBlock(this.lastHelpPos) instanceof RedstoneTorchBlock)) {
         int var3 = this.getPowerSlot();
         if (var3 == -1) {
            return false;
         } else {
            for (Direction var7 : Direction.values()) {
               if (var7 != facing.getOpposite()) {
                  BlockPos var8 = pistonPos.offset(var7);
                  if (BlockUtil.canPlace(var8, this.placeRange.getValue())) {
                     int var9 = mc.player.getInventory().selectedSlot;
                     this.placePower(var8, var3, var9);
                     this.lastHelpPlaceTick = mc.player.age;
                     this.lastHelpPos = var8;
                     return true;
                  }
               }
            }

            return false;
         }
      } else {
         return false;
      }
   }

   private void tryMinePiston(BlockPos pistonPos, Direction facing) {
      if (this.mine.getValue() && pistonPos != null && facing != null && mc.interactionManager != null) {
         BlockPos var3 = pistonPos.offset(facing.getOpposite());
         if (this.getBlock(var3) instanceof PistonHeadBlock) {
            Direction var4 = BlockUtil.getClickSide(pistonPos);
            if (var4 != null) {
               if (this.tryUseCombatTask()) {
                  mc.interactionManager.attackBlock(pistonPos, var4);
                  this.bestPos = null;
                  this.bestOPos = null;
                  this.bestFacing = null;
                  this.getPos = false;
               }
            }
         }
      }
   }

   @Override
   public void onRender3D(MatrixStack matrixStack) {
      if (this.render.getValue()) {
         if (this.renderCrystalPos != null) {
            Render3DUtil.draw3DBox(matrixStack, new Box(this.renderCrystalPos), this.crystalColor.getValue(), this.crystalColor.getValue());
         }

         if (this.renderPistonPos != null) {
            Render3DUtil.draw3DBox(matrixStack, new Box(this.renderPistonPos), this.pistonColor.getValue(), this.pistonColor.getValue());
         }

         if (this.renderPowerPos != null) {
            Render3DUtil.draw3DBox(matrixStack, new Box(this.renderPowerPos), this.redstoneColor.getValue(), this.redstoneColor.getValue());
         }
      }
   }

   private void breakPushedCrystals(BlockPos targetPos) {
      if (this.postPushBreak.getValue() && mc.player != null && mc.world != null) {
         if (this.postBreakTimer.passedMs((double)this.breakDelay.getValueInt())) {
            if (this.shouldPostBreak()) {
               EndCrystalEntity var2 = null;
               double var3 = Double.POSITIVE_INFINITY;
               Box var5 = new Box(targetPos).stretch(0.0, 3.0, 0.0).expand(3.0, 1.0, 3.0);

               for (EndCrystalEntity var7 : BlockUtil.getEndCrystals(var5)) {
                  double var8 = var7.squaredDistanceTo(this.target);
                  if (!(var8 > 16.0)) {
                     double var10 = this.lastCrystalPos == null
                        ? var8
                        : var7.squaredDistanceTo(
                           (double)this.lastCrystalPos.getX() + 0.5, (double)this.lastCrystalPos.getY() + 0.5, (double)this.lastCrystalPos.getZ() + 0.5
                        );
                     if (var10 < var3) {
                        var3 = var10;
                        var2 = var7;
                     }
                  }
               }

               if (var2 != null) {
                  CombatUtil.attack(var2, this.rotate.getValue());
                  this.postBreakTimer.reset();
               }
            }
         }
      }
   }

   private boolean shouldPostBreak() {
      if (mc.player.age - this.lastPistonActionTick <= 20) {
         return true;
      } else if (this.bestPos == null) {
         return false;
      } else {
         Block var1 = this.getBlock(this.bestPos);
         if (!(var1 instanceof PistonBlock) && var1 != Blocks.MOVING_PISTON) {
            for (Direction var5 : Direction.values()) {
               if (this.getBlock(this.bestPos.offset(var5)) == Blocks.REDSTONE_BLOCK) {
                  return true;
               }
            }

            return false;
         } else {
            return true;
         }
      }
   }

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

   private int getPistonSlot() {
      return this.smartSwitch.getValue() && this.lastPistonSlot != -1 && InventoryUtil.isItemInSlot(this.lastPistonSlot, PistonBlock.class)
         ? this.lastPistonSlot
         : this.cachedPistonSlot;
   }

   private int getCrystalSlot() {
      return this.smartSwitch.getValue() && this.lastCrystalSlot != -1 && InventoryUtil.isItemInSlot(this.lastCrystalSlot, Items.END_CRYSTAL)
         ? this.lastCrystalSlot
         : this.cachedCrystalSlot;
   }

   private int getFireSlot() {
      return this.smartSwitch.getValue() && this.lastFireSlot != -1 && InventoryUtil.isItemInSlot(this.lastFireSlot, Items.FLINT_AND_STEEL)
         ? this.lastFireSlot
         : this.cachedFireSlot;
   }

   private void refreshSlots() {
      if (mc.player != null) {
         int var1 = mc.player.age;
         if (this.slotCacheTick != var1) {
            this.slotCacheTick = var1;
            this.cachedPistonSlot = this.findClass(PistonBlock.class);
            this.cachedPowerSlot = this.findBlock(Blocks.REDSTONE_BLOCK);
            this.cachedTorchSlot = this.findItem(Items.REDSTONE_TORCH);
            this.cachedCrystalSlot = this.findItem(Items.END_CRYSTAL);
            this.cachedFireSlot = this.findItem(Items.FLINT_AND_STEEL);
            this.lastPistonSlot = this.cachedPistonSlot != -1 ? this.cachedPistonSlot : this.lastPistonSlot;
            this.lastPowerSlot = this.getRawPowerSlot();
            this.lastCrystalSlot = this.cachedCrystalSlot != -1 ? this.cachedCrystalSlot : this.lastCrystalSlot;
            this.lastFireSlot = this.cachedFireSlot != -1 ? this.cachedFireSlot : this.lastFireSlot;
         }
      }
   }

   private int getRawPowerSlot() {
      if (this.redStoneMode.is(PistonCrystal.RedstoneMode.Torch)) {
         return this.cachedTorchSlot != -1 ? this.cachedTorchSlot : this.lastPowerSlot;
      } else {
         return this.cachedPowerSlot != -1 ? this.cachedPowerSlot : this.lastPowerSlot;
      }
   }

   public int findItem(Item itemIn) {
      return this.inventory.getValue() ? InventoryUtil.findItemInventorySlot(itemIn) : InventoryUtil.findItem(itemIn);
   }

   public int findBlock(Block blockIn) {
      return this.inventory.getValue() ? InventoryUtil.findBlockInventorySlot(blockIn) : InventoryUtil.findBlock(blockIn);
   }

   public int findClass(Class<?> clazz) {
      return this.inventory.getValue() ? InventoryUtil.findClassInventorySlot(clazz) : InventoryUtil.findClass(clazz);
   }

   private Block getBlock(BlockPos pos) {
      return mc.world.getBlockState(pos).getBlock();
   }
@Environment(EnvType.CLIENT)
   public static enum RedstoneMode {
      Block,
      Torch;

}
}
