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

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.ClientTickEvent;
import dev.crucifixbeta.api.events.impl.MoveEvent;
import dev.crucifixbeta.api.events.impl.RotationEvent;
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.world.BlockPosX;
import dev.crucifixbeta.api.utils.world.BlockUtil;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.exploit.Blink;
import dev.crucifixbeta.mod.modules.impl.movement.ElytraFly;
import dev.crucifixbeta.mod.modules.impl.player.PacketMine;
import dev.crucifixbeta.mod.modules.settings.enums.Timing;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.EnumSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import java.util.Objects;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.Blocks;
import net.minecraft.client.network.AbstractClientPlayerEntity;
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.Vec2f;
import net.minecraft.util.math.Vec3d;

@Environment(EnvType.CLIENT)
public class Surround extends Module {
   public static Surround INSTANCE;
   public final EnumSetting<Surround.Page> page = this.add(new EnumSetting<>("Page", Surround.Page.General));
   public final SliderSetting placeDelay = this.add(new SliderSetting("PlaceDelay", 50, 0, 500, () -> this.page.is(Surround.Page.General)));
   private final BooleanSetting mineDownward = this.add(new BooleanSetting("MineDownward", false, () -> this.page.is(Surround.Page.General)));
   public final BooleanSetting extend = this.add(new BooleanSetting("Extend", true, () -> this.page.is(Surround.Page.General))).setParent();
   public final BooleanSetting onlySelf = this.add(
      new BooleanSetting("OnlySelf", false, () -> this.page.is(Surround.Page.General) && this.extend.isOpen())
   );
   public final BooleanSetting inAir = this.add(new BooleanSetting("InAir", true, () -> this.page.is(Surround.Page.Check)));
   private final Timer timer = new Timer();
   private final SliderSetting blocksPer = this.add(new SliderSetting("BlocksPer", 1, 1, 8, () -> this.page.is(Surround.Page.General)));
   private final BooleanSetting packetPlace = this.add(new BooleanSetting("PacketPlace", true, () -> this.page.is(Surround.Page.General)));
   private final EnumSetting<Timing> timing = this.add(new EnumSetting<>("Timing", Timing.All, () -> this.page.is(Surround.Page.General)));
   private final BooleanSetting breakCrystal = this.add(
      new BooleanSetting("Break", true, () -> this.page.is(Surround.Page.General)).setParent()
   );
   private final BooleanSetting eatPause = this.add(
      new BooleanSetting("EatingPause", true, () -> this.page.is(Surround.Page.General) && this.breakCrystal.isOpen())
   );
   private final BooleanSetting center = this.add(new BooleanSetting("Center", true, () -> this.page.is(Surround.Page.General)));
   private final BooleanSetting inventory = this.add(new BooleanSetting("InventorySwap", true, () -> this.page.is(Surround.Page.General)));
   private final BooleanSetting enderChest = this.add(new BooleanSetting("EnderChest", true, () -> this.page.is(Surround.Page.General)));
   private final BooleanSetting godMode = this.add(new BooleanSetting("GodMode", true, () -> this.page.is(Surround.Page.GodMode)));
   private final SliderSetting breakTime = this.add(new SliderSetting("BreakTime", 2.0, 0.0, 3.0, () -> this.page.is(Surround.Page.GodMode)));
   private final BooleanSetting failedSkip = this.add(new BooleanSetting("FailedSkip", true, () -> this.page.is(Surround.Page.GodMode)));
   private final BooleanSetting rotate = this.add(new BooleanSetting("Rotate", true, () -> this.page.getValue() == Surround.Page.Rotate));
   private final BooleanSetting yawStep = this.add(
      new BooleanSetting("YawStep", false, () -> this.rotate.isOpen() && this.page.getValue() == Surround.Page.Rotate).setParent()
   );
   private final BooleanSetting whenElytra = this.add(
      new BooleanSetting("FallFlying", true, () -> this.rotate.isOpen() && this.yawStep.isOpen() && this.page.getValue() == Surround.Page.Rotate)
   );
   private final SliderSetting steps = this.add(
      new SliderSetting("Steps", 0.05, 0.0, 1.0, 0.01, () -> this.page.getValue() == Surround.Page.Rotate && this.yawStep.isOpen())
   );
   private final BooleanSetting checkFov = this.add(
      new BooleanSetting("OnlyLooking", true, () -> this.page.getValue() == Surround.Page.Rotate && this.yawStep.isOpen()).setParent()
   );
   private final SliderSetting fov = this.add(
      new SliderSetting(
         "Fov",
         20.0,
         0.0,
         360.0,
         0.1,
         () -> this.checkFov.isOpen() && this.page.getValue() == Surround.Page.Rotate && this.yawStep.isOpen()
      )
   );
   private final SliderSetting priority = this.add(
      new SliderSetting("Priority", 10, 0, 100, () -> this.page.getValue() == Surround.Page.Rotate && this.yawStep.isOpen())
   );
   private final BooleanSetting detectMining = this.add(new BooleanSetting("DetectMining", false, () -> this.page.is(Surround.Page.Check)));
   private final BooleanSetting usingPause = this.add(new BooleanSetting("UsingPause", true, () -> this.page.is(Surround.Page.Check)));
   private final BooleanSetting moveDisable = this.add(new BooleanSetting("MoveDisable", true, () -> this.page.is(Surround.Page.Check)));
   private final BooleanSetting jumpDisable = this.add(new BooleanSetting("JumpDisable", true, () -> this.page.is(Surround.Page.Check)));
   private final BooleanSetting noBlockDisable = this.add(new BooleanSetting("NoBlockDisable", true, () -> this.page.is(Surround.Page.Check)));
   public Vec3d directionVec = null;
   double startX = 0.0;
   double startY = 0.0;
   double startZ = 0.0;
   int progress = 0;
   private boolean shouldCenter = true;

   public Surround() {
      super("Surround", Module.Category.Combat);
      this.setChinese("\u56f4\u811a");
      INSTANCE = this;
   }

   public static boolean selfIntersectPos(BlockPos pos) {
      return mc.player.getBoundingBox().intersects(new Box(pos));
   }

   public static boolean otherIntersectPos(BlockPos pos) {
      for (AbstractClientPlayerEntity var2 : CrucifixBeta.THREAD.getPlayers()) {
         if (var2.getBoundingBox().intersects(new Box(pos))) {
            return true;
         }
      }

      return false;
   }

   public static Vec2f getRotationTo(Vec3d posFrom, Vec3d posTo) {
      Vec3d var2 = posTo.subtract(posFrom);
      return getRotationFromVec(var2);
   }

   private static Vec2f getRotationFromVec(Vec3d vec) {
      double var1 = vec.x;
      double var3 = vec.z;
      double var5 = Math.hypot(var1, var3);
      var3 = vec.z;
      double var7 = vec.x;
      double var9 = normalizeAngle(Math.toDegrees(Math.atan2(var3, var7)) - 90.0);
      double var11 = normalizeAngle(Math.toDegrees(-Math.atan2(vec.y, var5)));
      return new Vec2f((float)var9, (float)var11);
   }

   private static double normalizeAngle(double angleIn) {
      double var2 = angleIn % 360.0;
      if (var2 >= 180.0) {
         var2 -= 360.0;
      }

      if (var2 < -180.0) {
         var2 += 360.0;
      }

      return var2;
   }

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

   @EventListener
   public void onTick(ClientTickEvent event) {
      if (!nullCheck()) {
         if (!this.inventory.getValue() || EntityUtil.inInventory()) {
            if ((!this.timing.is(Timing.Pre) || !event.isPost()) && (!this.timing.is(Timing.Post) || !event.isPre())) {
               if (this.timer.passed((long)this.placeDelay.getValue())) {
                  if (!this.usingPause.getValue() || !this.shouldPauseForUsingItem()) {
                     this.directionVec = null;
                     this.progress = 0;
                     if (!MovementUtil.isMoving() && !mc.options.jumpKey.isPressed()) {
                        this.startX = mc.player.getX();
                        this.startY = mc.player.getY();
                        this.startZ = mc.player.getZ();
                     }

                     double var2 = (double)MathHelper.sqrt((float)mc.player.squaredDistanceTo(this.startX, this.startY, this.startZ));
                     if (this.getBlock() == -1) {
                        if (this.noBlockDisable.getValue()) {
                           this.disable();
                        }
                     } else if ((!this.moveDisable.getValue() || !(var2 > 1.0)) && (!this.jumpDisable.getValue() || !mc.player.input.jumping)) {
                        if (!Blink.INSTANCE.isOn() || !Blink.INSTANCE.pauseModule.getValue()) {
                           if (this.inAir.getValue() || mc.player.isOnGround()) {
                              this.doSurround(new BlockPosX(mc.player.getX(), mc.player.getY(), mc.player.getZ()));
                              this.doSurround(new BlockPosX(mc.player.getX(), mc.player.getY() + 0.8, mc.player.getZ()));
                           }
                        }
                     } else {
                        this.disable();
                     }
                  }
               }
            }
         }
      }
   }

   public void doSurround(BlockPos pos) {
      for (Direction var5 : Direction.values()) {
         if (var5 != Direction.UP) {
            BlockPos var6 = pos.offset(var5);
            if (BlockUtil.getPlaceSide(var6) != null) {
               this.tryPlaceBlock(var6);
            } else if (BlockUtil.canReplace(var6)) {
               this.tryPlaceBlock(this.getHelperPos(var6));
            }

            if ((selfIntersectPos(var6) || !this.onlySelf.getValue() && otherIntersectPos(var6)) && this.extend.getValue()) {
               for (Direction var10 : Direction.values()) {
                  if (var10 != Direction.UP) {
                     BlockPos var11 = var6.offset(var10);
                     if (selfIntersectPos(var11) || !this.onlySelf.getValue() && otherIntersectPos(var11)) {
                        for (Direction var15 : Direction.values()) {
                           if (var15 != Direction.UP) {
                              this.tryPlaceBlock(var11);
                              BlockPos var16 = var11.offset(var15);
                              this.tryPlaceBlock(BlockUtil.getPlaceSide(var16) == null && BlockUtil.canReplace(var16) ? this.getHelperPos(var16) : var16);
                           }
                        }
                     }

                     this.tryPlaceBlock(BlockUtil.getPlaceSide(var11) == null && BlockUtil.canReplace(var11) ? this.getHelperPos(var11) : var11);
                  }
               }
            }
         }
      }
   }

   @Override
   public void onEnable() {
      if (!nullCheck()) {
         this.startX = mc.player.getX();
         this.startY = mc.player.getY();
         this.startZ = mc.player.getZ();
         this.shouldCenter = true;
      } else {
         if (this.moveDisable.getValue() || this.jumpDisable.getValue()) {
            this.disable();
         }
      }
   }

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

   @EventListener(
      priority = -1
   )
   public void onMove(MoveEvent event) {
      if (!nullCheck() && this.center.getValue() && !mc.player.isFallFlying()) {
         BlockPos var2 = EntityUtil.getPlayerPos(true);
         if (mc.player.getX() - (double)var2.getX() - 0.5 <= 0.2
            && mc.player.getX() - (double)var2.getX() - 0.5 >= -0.2
            && mc.player.getZ() - (double)var2.getZ() - 0.5 <= 0.2
            && mc.player.getZ() - 0.5 - (double)var2.getZ() >= -0.2) {
            if (this.shouldCenter && (mc.player.isOnGround() || MovementUtil.isMoving())) {
               event.setX(0.0);
               event.setZ(0.0);
               this.shouldCenter = false;
            }
         } else if (this.shouldCenter) {
            Vec3d var3 = EntityUtil.getPlayerPos(true).toCenterPos();
            float var4 = getRotationTo(mc.player.getPos(), var3).x;
            float var5 = var4 / 180.0F * (float) Math.PI;
            double var6 = mc.player.getPos().distanceTo(new Vec3d(var3.x, mc.player.getY(), var3.z));
            double var8 = Math.min(0.2873, var6);
            double var10 = (double)(-((float)Math.sin((double)var5))) * var8;
            double var12 = (double)((float)Math.cos((double)var5)) * var8;
            event.setX(var10);
            event.setZ(var12);
         }
      }
   }

   private void tryPlaceBlock(BlockPos pos) {
      if (pos != null) {
         if (!this.detectMining.getValue() || !CrucifixBeta.BREAK.isMining(pos)) {
            if ((double)this.progress < this.blocksPer.getValue()) {
               BlockPos var2 = EntityUtil.getPlayerPos(true);
               if (!this.mineDownward.getValue() || !Objects.equals(PacketMine.getBreakPos(), var2.down()) || !Objects.equals(PacketMine.getBreakPos(), pos)) {
                  int var3 = this.getBlock();
                  if (var3 != -1) {
                     Direction var4 = BlockUtil.getPlaceSide(pos);
                     if (var4 != null) {
                        Vec3d var5 = new Vec3d(
                           (double)pos.getX() + 0.5 + (double)var4.getVector().getX() * 0.5,
                           (double)pos.getY() + 0.5 + (double)var4.getVector().getY() * 0.5,
                           (double)pos.getZ() + 0.5 + (double)var4.getVector().getZ() * 0.5
                        );
                        if (BlockUtil.canPlace(pos, 6.0, true)) {
                           if (!this.rotate.getValue() || this.faceVector(var5)) {
                              if (this.tryUseCombatTask()) {
                                 if (this.breakCrystal.getValue()) {
                                    CombatUtil.attackCrystal(pos, this.rotate.getValue(), this.eatPause.getValue());
                                 } else if (BlockUtil.hasEntity(pos, false)) {
                                    return;
                                 }

                                 int var6 = mc.player.getInventory().selectedSlot;
                                 this.doSwap(var3);
                                 BlockUtil.placedPos.add(pos);
                                 if (BlockUtil.allowAirPlace()) {
                                    BlockUtil.airPlace(pos, false, Hand.MAIN_HAND, this.packetPlace.getValue());
                                 } else {
                                    BlockUtil.clickBlock(pos.offset(var4), var4.getOpposite(), false, Hand.MAIN_HAND, this.packetPlace.getValue());
                                 }

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

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

                                 this.progress++;
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }

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

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

   private int getBlock() {
      if (this.inventory.getValue()) {
         return InventoryUtil.findBlockInventorySlot(Blocks.OBSIDIAN) == -1 && this.enderChest.getValue()
            ? InventoryUtil.findBlockInventorySlot(Blocks.ENDER_CHEST)
            : InventoryUtil.findBlockInventorySlot(Blocks.OBSIDIAN);
      } else {
         return InventoryUtil.findBlock(Blocks.OBSIDIAN) == -1 && this.enderChest.getValue()
            ? InventoryUtil.findBlock(Blocks.ENDER_CHEST)
            : InventoryUtil.findBlock(Blocks.OBSIDIAN);
      }
   }

   public BlockPos getHelperPos(BlockPos pos) {
      for (Direction var5 : Direction.values()) {
         if ((!this.detectMining.getValue() || !CrucifixBeta.BREAK.isMining(pos.offset(var5)))
            && BlockUtil.isStrictDirection(pos.offset(var5), var5.getOpposite())
            && BlockUtil.canPlace(pos.offset(var5))) {
            return pos.offset(var5);
         }
      }

      return null;
   }
@Environment(EnvType.CLIENT)
   public static enum Page {
      General,
      Rotate,
      Check,
      GodMode;

}
}
