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

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.world.BlockUtil;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.client.AntiCheat;
import dev.crucifixbeta.mod.modules.impl.client.ClientSetting;
import dev.crucifixbeta.mod.modules.impl.player.PacketMine;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.decoration.ArmorStandEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Items;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;

@Environment(EnvType.CLIENT)
public class CevBreaker extends Module {
   public static CevBreaker INSTANCE;
   private final SliderSetting targetRange = this.add(new SliderSetting("TargetRange", 5.0, 0.0, 8.0, 0.1));
   private final SliderSetting breakRange = this.add(new SliderSetting("BreakRange", 5.0, 0.0, 8.0, 0.1));
   private final SliderSetting delay = this.add(new SliderSetting("Delay", 100, 0, 500).setSuffix("ms"));
   private final BooleanSetting rotate = this.add(new BooleanSetting("Rotate", true));
   private final BooleanSetting ground = this.add(new BooleanSetting("Ground", true));
   private final BooleanSetting inventory = this.add(new BooleanSetting("InventorySwap", true));
   private final BooleanSetting top = this.add(new BooleanSetting("Top", false));
   private final BooleanSetting bevel = this.add(new BooleanSetting("Bevel", true));
   private final BooleanSetting crystal = this.add(new BooleanSetting("Crystal", true));
   private final BooleanSetting checkDamage = this.add(new BooleanSetting("DetectProgress", true));
   private final SliderSetting crystalDamage = this.add(
      new SliderSetting("Progress", 0.9F, 0.0, 1.0, 0.01, () -> this.crystal.isOpen() && this.checkDamage.getValue())
   );
   private final BooleanSetting afterBreak = this.add(new BooleanSetting("AfterBreak", true));
   private final BooleanSetting placeRotate = this.add(new BooleanSetting("PlaceRotate", true));
   private final SliderSetting placeDelay = this.add(new SliderSetting("PlaceDelay", 100, 0, 1000));
   private final Timer timer = new Timer();
   private final Timer placeTimer = new Timer();
   private PlayerEntity target = null;

   public CevBreaker() {
      super("CevBreaker", Module.Category.Combat);
      this.setChinese("\u81ea\u52a8\u70b8\u5934");
      INSTANCE = this;
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      if (!this.inventory.getValue() || EntityUtil.inInventory()) {
         if (!this.ground.getValue() || mc.player.isOnGround()) {
            this.target = CombatUtil.getClosestEnemy(this.targetRange.getValue());
            if (this.target != null) {
               BlockPos var2 = EntityUtil.getEntityPos(this.target);
               BlockPos var3 = PacketMine.getBreakPos();

               for (Direction var7 : Direction.values()) {
                  if (var7 != Direction.DOWN && (var7 == Direction.UP || this.bevel.getValue()) && (var7 != Direction.UP || this.top.getValue())) {
                     BlockPos var8 = var2.up(1).offset(var7);
                     if (!(var8.up().toCenterPos().distanceTo(mc.player.getPos()) > this.breakRange.getValue())) {
                        Block var9 = this.getBlock(var8);
                        if (var9 == Blocks.OBSIDIAN || var9 == Blocks.BEDROCK) {
                           if (var3 == null || !var3.equals(var8)) {
                              PacketMine.INSTANCE.mine(var8);
                           }

                           if (this.crystal.getValue()) {
                              if (this.canPlaceCrystal(var8.up())) {
                                 boolean var10 = !this.checkDamage.getValue() || PacketMine.progress >= this.crystalDamage.getValue();
                                 if (var10 && this.placeTimer.passedMs(this.placeDelay.getValue())) {
                                    this.placeCrystal(var8);
                                    this.placeTimer.reset();
                                 }
                              }

                              if (this.afterBreak.getValue() && PacketMine.complete) {
                                 for (Direction var13 : Direction.values()) {
                                    CombatUtil.attackCrystal(var8.offset(var13), this.placeRotate.getValue(), true);
                                 }
                              }
                           }
                           break;
                        }

                        if (!mc.world.isAir(var8)) {
                           if (!this.timer.passedMs(this.delay.getValue())) {
                              return;
                           }

                           PacketMine.INSTANCE.mine(var8);
                           this.timer.reset();
                           break;
                        }

                        if (BlockUtil.canPlace(var8)) {
                           if (!this.timer.passedMs(this.delay.getValue())) {
                              return;
                           }

                           this.placeBlock(var8);
                           this.timer.reset();
                           break;
                        }
                     }
                  }
               }
            }
         }
      }
   }

   private boolean canPlaceCrystal(BlockPos crystalPos) {
      BlockPos var2 = crystalPos.down();
      Block var3 = this.getBlock(var2);
      if (var3 != Blocks.BEDROCK && var3 != Blocks.OBSIDIAN) {
         return false;
      } else if (BlockUtil.getClickSideStrict(var2) == null) {
         return false;
      } else {
         return this.noEntity(crystalPos) && this.noEntity(crystalPos.up())
            ? !ClientSetting.INSTANCE.lowVersion.getValue() || mc.world.isAir(crystalPos.up())
            : false;
      }
   }

   private boolean noEntity(BlockPos pos) {
      for (Entity var3 : BlockUtil.getEntities(new Box(pos))) {
         if (!(var3 instanceof ItemEntity) && (!(var3 instanceof ArmorStandEntity) || !AntiCheat.INSTANCE.ignoreArmorStand.getValue())) {
            return false;
         }
      }

      return true;
   }

   private void placeCrystal(BlockPos obsidianPos) {
      int var2 = this.findCrystal();
      if (var2 != -1) {
         BlockPos var3 = obsidianPos.up();
         int var4 = mc.player.getInventory().selectedSlot;
         this.doSwap(var2, var2);
         BlockUtil.placeCrystal(var3, this.placeRotate.getValue());
         this.doSwap(var4, var2);
      }
   }

   private void placeBlock(BlockPos pos) {
      int var2 = this.getBlockSlot();
      if (var2 != -1) {
         int var3 = mc.player.getInventory().selectedSlot;
         this.doSwap(var2, var2);
         if (BlockUtil.canPlace(pos)) {
            if (BlockUtil.allowAirPlace()) {
               BlockUtil.airPlace(pos, this.rotate.getValue());
            } else {
               Direction var4 = BlockUtil.getPlaceSide(pos);
               if (var4 != null) {
                  BlockUtil.clickBlock(pos.offset(var4), var4.getOpposite(), this.rotate.getValue());
               }
            }
         }

         this.doSwap(var3, var2);
      }
   }

   private int getBlockSlot() {
      return this.inventory.getValue() ? InventoryUtil.findBlockInventorySlot(Blocks.OBSIDIAN) : InventoryUtil.findBlock(Blocks.OBSIDIAN);
   }

   private int findCrystal() {
      return this.inventory.getValue() ? InventoryUtil.findItemInventorySlot(Items.END_CRYSTAL) : InventoryUtil.findItem(Items.END_CRYSTAL);
   }

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

   private Block getBlock(BlockPos pos) {
      return mc.world.getBlockState(pos).getBlock();
   }

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