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.MathUtil;
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.BlockPosX;
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.exploit.Blink;
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.Blocks;
import net.minecraft.block.ScaffoldingBlock;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ExperienceOrbEntity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.decoration.ItemFrameEntity;
import net.minecraft.entity.projectile.ArrowEntity;
import net.minecraft.entity.projectile.thrown.ExperienceBottleEntity;
import net.minecraft.item.Items;
import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket;
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.Vec3d;

@Environment(EnvType.CLIENT)
public class AntiPhase extends Module {
   private final SliderSetting placeRange = this.add(new SliderSetting("PlaceRange", 4.0, 0.0, 8.0, 0.1));
   private final BooleanSetting ladder = this.add(new BooleanSetting("Ladder", true).setParent());
   private final BooleanSetting onlyHard = this.add(new BooleanSetting("OnlyHard", true, this.ladder::isOpen));
   private final BooleanSetting itemFrame = this.add(new BooleanSetting("ItemFrame", true).setParent());
   private final BooleanSetting fill = this.add(new BooleanSetting("Fill", false, this.itemFrame::isOpen));
   private final BooleanSetting scaffolding = this.add(new BooleanSetting("Scaffolding", true));
   private final BooleanSetting rotate = this.add(new BooleanSetting("Rotate", true));
   private final BooleanSetting eatingPause = this.add(new BooleanSetting("EatingPause", true));
   private final BooleanSetting collideSkip = this.add(new BooleanSetting("CollideSkip", true));
   private final BooleanSetting crawlingSkip = this.add(new BooleanSetting("CrawlingSkip", true));
   private final BooleanSetting onlyGround = this.add(new BooleanSetting("InAirSkip", false));
   private final SliderSetting targetRange = this.add(new SliderSetting("TargetRange", 5.0, 0.0, 7.0, 0.1));
   private final SliderSetting delay = this.add(new SliderSetting("Delay", 100.0, 0.0, 2000.0, 1.0));
   private final BooleanSetting inventory = this.add(new BooleanSetting("InventorySwap", true));
   private final Timer timer = new Timer();

   public AntiPhase() {
      super("AntiPhase", Module.Category.Combat);
      this.setChinese("\u53cd\u7a7f\u5899");
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      if (!this.eatingPause.getValue() || !this.shouldPauseForUsingItem()) {
         if (!Blink.INSTANCE.isOn() || !Blink.INSTANCE.pauseModule.getValue()) {
            for (Entity var3 : CombatUtil.getEnemies(this.targetRange.getValue())) {
               if ((!this.crawlingSkip.getValue() || !var3.isCrawling())
                  && (!this.onlyGround.getValue() || var3.isOnGround())
                  && (!this.collideSkip.getValue() || !BlockUtil.canCollide(var3, var3.getBoundingBox()))
                  && this.timer.passed((long)this.delay.getValueInt())) {
                  int var4;
                  if (this.scaffolding.getValue() && BlockUtil.canReplace(var3.getBlockPos()) && (var4 = this.getScaffolding()) != -1) {
                     BlockPos var5 = var3.getBlockPos();
                     Direction var6 = null;
                     Direction var7 = BlockUtil.getPlaceSide(var5, 6.0);
                     if (var7 != null
                        || (var6 = this.getSideIgnore(var5.down())) != null
                           && BlockUtil.getBlock(var5.down()) instanceof ScaffoldingBlock
                           && !mc.player.isSneaking()
                        || mc.player.isSneaking()
                           && (var6 = this.getSideOnly(var5.down())) != null
                           && BlockUtil.getBlock(var5.down()) instanceof ScaffoldingBlock) {
                        Vec3d var8 = var7 != null
                           ? var5.offset(var7)
                              .toCenterPos()
                              .add(
                                 (double)var7.getOpposite().getVector().getX() * 0.5,
                                 (double)var7.getOpposite().getVector().getY() * 0.5,
                                 (double)var7.getOpposite().getVector().getZ() * 0.5
                              )
                           : var5.down()
                              .toCenterPos()
                              .add((double)var6.getVector().getX() * 0.5, (double)var6.getVector().getY() * 0.5, (double)var6.getVector().getZ() * 0.5);
                        double var9 = mc.player.getEyePos().distanceTo(var8);
                        if (var9 <= this.placeRange.getValue()) {
                           int var11 = mc.player.getInventory().selectedSlot;
                           this.doSwap(var4);
                           if (BlockUtil.getBlock(var5.down()) instanceof ScaffoldingBlock && var6 != null) {
                              BlockUtil.clickBlock(var5.down(), var6, this.rotate.getValue());
                           } else {
                              BlockUtil.placeBlock(var5, this.rotate.getValue());
                           }

                           this.timer.reset();
                           if (this.inventory.getValue()) {
                              this.doSwap(var4);
                              EntityUtil.syncInventory();
                           } else {
                              this.doSwap(var11);
                           }
                        }
                     }
                  }

                  if (this.itemFrame.getValue() && mc.world.isAir(var3.getBlockPos())) {
                     ItemFrameEntity var22 = this.hasItemFrame(new Box(var3.getBlockPos()));
                     int var20;
                     if (var22 == null && (var20 = this.getItemFrame()) != -1) {
                        BlockPos var23 = var3.getBlockPos().down();
                        double var12 = mc.player.getEyePos().distanceTo(var23.toBottomCenterPos().add(0.0, 1.0, 0.0));
                        if (var12 <= this.placeRange.getValue()
                           && BlockUtil.isStrictDirection(var23, Direction.UP)
                           && !BlockUtil.canReplace(var23)
                           && BlockUtil.canClick(var23)) {
                           int var14 = mc.player.getInventory().selectedSlot;
                           this.doSwap(var20);
                           BlockUtil.clickBlock(var23, Direction.UP, this.rotate.getValue());
                           this.timer.reset();
                           if (this.inventory.getValue()) {
                              this.doSwap(var20);
                              EntityUtil.syncInventory();
                           } else {
                              this.doSwap(var14);
                           }
                        }
                     }

                     if (this.fill.getValue() && var22 != null && var22.getHeldItemStack().isEmpty()) {
                        Vec3d var25 = MathUtil.getClosestPointToBox(mc.player.getEyePos(), var22.getBoundingBox());
                        int var24;
                        if (mc.player.getEyePos().distanceTo(var25) <= AntiCheat.INSTANCE.ieRange.getValue() && (var24 = this.getObsidian()) != -1) {
                           int var15 = mc.player.getInventory().selectedSlot;
                           this.doSwap(var24);
                           if (this.rotate.getValue()) {
                              CrucifixBeta.ROTATION.snapAt(var25);
                           }

                           mc.player.networkHandler.sendPacket(PlayerInteractEntityC2SPacket.interact(var22, mc.player.isSneaking(), Hand.MAIN_HAND));
                           this.timer.reset();
                           if (this.inventory.getValue()) {
                              this.doSwap(var24);
                              EntityUtil.syncInventory();
                           } else {
                              this.doSwap(var15);
                           }

                           if (this.rotate.getValue()) {
                              CrucifixBeta.ROTATION.snapBack();
                           }
                        }
                     }
                  }

                  Direction var16;
                  if (this.ladder.getValue()
                     && (var4 = this.getLadder()) != -1
                     && BlockUtil.canReplace(var3.getBlockPos())
                     && (var16 = this.targetFacing(var3.getPos())) != null) {
                     BlockPos var21 = var3.getBlockPos().offset(var16);
                     double var17 = mc.player
                        .getEyePos()
                        .distanceTo(
                           var21.toCenterPos()
                              .add(
                                 (double)var16.getOpposite().getVector().getX() * 0.5,
                                 (double)var16.getOpposite().getVector().getY() * 0.5,
                                 (double)var16.getOpposite().getVector().getZ() * 0.5
                              )
                        );
                     if (var17 <= this.placeRange.getValue()) {
                        BlockUtil.placedPos.add(var3.getBlockPos());
                        int var26 = mc.player.getInventory().selectedSlot;
                        this.doSwap(var4);
                        BlockUtil.clickBlock(var21, var16.getOpposite(), this.rotate.getValue());
                        this.timer.reset();
                        if (this.inventory.getValue()) {
                           this.doSwap(var4);
                           EntityUtil.syncInventory();
                        } else {
                           this.doSwap(var26);
                        }
                     }
                  }
               }
            }
         }
      }
   }

   private Direction getSideOnly(BlockPos pos) {
      return BlockUtil.isStrictDirection(pos, Direction.UP) ? Direction.UP : null;
   }

   private Direction getSideIgnore(BlockPos pos) {
      for (Direction var5 : Direction.values()) {
         if (var5 != Direction.UP && BlockUtil.isStrictDirection(pos, var5)) {
            return var5;
         }
      }

      return null;
   }

   private ItemFrameEntity hasItemFrame(Box box) {
      for (Entity var3 : BlockUtil.getEntities(box)) {
         if (var3 instanceof ItemFrameEntity var4 && var3.getFacing() == Direction.UP) {
            return var4;
         }
      }

      return null;
   }

   private static Box getBox(Direction facing, BlockPos bp) {
      Box var2 = null;
      double var3 = 0.1875;
      double var5 = (double)facing.getOffsetX() * 0.5 + (double)bp.getX() + 0.5;
      double var7 = (double)bp.getY();
      double var9 = (double)facing.getOffsetZ() * 0.5 + (double)bp.getZ() + 0.5;
      switch (facing) {
         case WEST:
            var2 = new Box(var5, var7, var9, var5 + var3, var7 + 1.0, var9 + 1.0);
            break;
         case EAST:
            var2 = new Box(var5, var7, var9, var5 - var3, var7 + 1.0, var9 + 1.0);
            break;
         case NORTH:
            var2 = new Box(var5, var7, var9, var5 + 1.0, var7 + 1.0, var9 + var3);
            break;
         case SOUTH:
            var2 = new Box(var5, var7, var9, var5 + 1.0, var7 + 1.0, var9 - var3);
      }

      return var2;
   }

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

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

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

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

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

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

   private Direction targetFacing(Vec3d vec3d) {
      BlockPosX var2 = new BlockPosX(vec3d);
      Vec3d var3 = var2.toBottomCenterPos();
      float var4 = 0.4F;
      double var5 = Double.MAX_VALUE;
      Direction var7 = null;

      for (Direction var11 : Direction.values()) {
         BlockPos var12;
         if (var11 != Direction.UP
            && var11 != Direction.DOWN
            && BlockUtil.isStrictDirection(var12 = var2.offset(var11), var11.getOpposite())
            && (!this.onlyHard.getValue() ? !BlockUtil.canReplace(var12) && BlockUtil.canClick(var12) : CrucifixBeta.HOLE.isHard(var12))) {
            Box var13 = getBox(var11, var2);
            double var15;
            if (var13 != null
               && !hasEntity(var13)
               && (var15 = var3.add((double)((float)var11.getOffsetX() * var4), 0.0, (double)((float)var11.getOffsetZ() * var4)).distanceTo(vec3d)) < var5) {
               var5 = var15;
               var7 = var11;
            }
         }
      }

      return var7;
   }

   public static boolean hasEntity(Box box) {
      for (Entity var2 : BlockUtil.getEntities(box)) {
         if (var2.isAlive()
            && !(var2 instanceof ItemEntity)
            && !(var2 instanceof ExperienceOrbEntity)
            && !(var2 instanceof ExperienceBottleEntity)
            && !(var2 instanceof ArrowEntity)
            && !(var2 instanceof ItemFrameEntity)) {
            return true;
         }
      }

      return false;
   }
}
