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.RotationEvent;
import dev.crucifixbeta.api.utils.combat.CombatUtil;
import dev.crucifixbeta.api.utils.math.PredictUtil;
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.impl.movement.ElytraFly;
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.ArrayList;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.Blocks;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
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 AutoWeb extends Module {
   public static AutoWeb INSTANCE;
   public static boolean force;
   public static boolean ignore;
   public final EnumSetting<AutoWeb.Page> page = this.add(new EnumSetting<>("Page", AutoWeb.Page.General));
   public final SliderSetting placeDelay = this.add(
      new SliderSetting("PlaceDelay", 50, 0, 500, () -> this.page.getValue() == AutoWeb.Page.General)
   );
   public final SliderSetting blocksPer = this.add(new SliderSetting("BlocksPer", 2, 1, 10, () -> this.page.getValue() == AutoWeb.Page.General));
   public final SliderSetting predictTicks = this.add(
      new SliderSetting("PredictTicks", 2.0, 0.0, 50.0, 1.0, () -> this.page.getValue() == AutoWeb.Page.General)
   );
   public final SliderSetting maxWebs = this.add(
      new SliderSetting("MaxWebs", 2.0, 1.0, 8.0, 1.0, () -> this.page.getValue() == AutoWeb.Page.General)
   );
   public final SliderSetting offset = this.add(
      new SliderSetting("Offset", 0.25, 0.0, 0.3, 0.01, () -> this.page.getValue() == AutoWeb.Page.General)
   );
   public final SliderSetting placeRange = this.add(
      new SliderSetting("PlaceRange", 5.0, 0.0, 6.0, 0.1, () -> this.page.getValue() == AutoWeb.Page.General)
   );
   public final SliderSetting targetRange = this.add(
      new SliderSetting("TargetRange", 8.0, 0.0, 8.0, 0.1, () -> this.page.getValue() == AutoWeb.Page.General)
   );
   final ArrayList<BlockPos> pos = new ArrayList<>();
   private final BooleanSetting preferAnchor = this.add(
      new BooleanSetting("PreferAnchor", true, () -> this.page.getValue() == AutoWeb.Page.General)
   );
   private final BooleanSetting detectMining = this.add(
      new BooleanSetting("DetectMining", true, () -> this.page.getValue() == AutoWeb.Page.General)
   );
   private final EnumSetting<Timing> timing = this.add(
      new EnumSetting<>("Timing", Timing.All, () -> this.page.getValue() == AutoWeb.Page.General)
   );
   private final BooleanSetting feet = this.add(new BooleanSetting("Feet", true, () -> this.page.getValue() == AutoWeb.Page.General));
   private final BooleanSetting feetExtend = this.add(
      new BooleanSetting("FeetExtend", true, () -> this.page.getValue() == AutoWeb.Page.General)
   );
   private final BooleanSetting face = this.add(new BooleanSetting("Face", true, () -> this.page.getValue() == AutoWeb.Page.General));
   private final BooleanSetting down = this.add(new BooleanSetting("Down", true, () -> this.page.getValue() == AutoWeb.Page.General));
   private final BooleanSetting inventorySwap = this.add(
      new BooleanSetting("InventorySwap", true, () -> this.page.getValue() == AutoWeb.Page.General)
   );
   private final BooleanSetting usingPause = this.add(new BooleanSetting("UsingPause", true, () -> this.page.getValue() == AutoWeb.Page.General));
   private final BooleanSetting rotate = this.add(
      new BooleanSetting("Rotate", true, () -> this.page.getValue() == AutoWeb.Page.Rotate).setParent()
   );
   private final BooleanSetting yawStep = this.add(
      new BooleanSetting("YawStep", false, () -> this.rotate.isOpen() && this.page.getValue() == AutoWeb.Page.Rotate).setParent()
   );
   private final BooleanSetting whenElytra = this.add(
      new BooleanSetting("FallFlying", true, () -> this.rotate.isOpen() && this.yawStep.isOpen() && this.page.getValue() == AutoWeb.Page.Rotate)
   );
   private final SliderSetting steps = this.add(
      new SliderSetting(
         "Steps", 0.3, 0.1, 1.0, 0.01, () -> this.rotate.isOpen() && this.yawStep.isOpen() && this.page.getValue() == AutoWeb.Page.Rotate
      )
   );
   private final BooleanSetting checkFov = this.add(
      new BooleanSetting("OnlyLooking", true, () -> this.rotate.isOpen() && this.yawStep.isOpen() && this.page.getValue() == AutoWeb.Page.Rotate)
   );
   private final SliderSetting fov = this.add(
      new SliderSetting(
         "Fov",
         20.0,
         0.0,
         360.0,
         0.1,
         () -> this.rotate.isOpen() && this.yawStep.isOpen() && this.checkFov.getValue() && this.page.getValue() == AutoWeb.Page.Rotate
      )
   );
   private final SliderSetting priority = this.add(
      new SliderSetting(
         "Priority", 10, 0, 100, () -> this.rotate.isOpen() && this.yawStep.isOpen() && this.page.getValue() == AutoWeb.Page.Rotate
      )
   );
   private final Timer timer = new Timer();
   public Vec3d directionVec = null;
   int progress = 0;

   public AutoWeb() {
      super("AutoWeb", Module.Category.Combat);
      this.setChinese("\u8718\u86db\u7f51\u5149\u73af");
      INSTANCE = this;
   }

   @Override
   public String getInfo() {
      return this.pos.isEmpty() ? null : "Working";
   }

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

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

   @EventListener
   public void onTick(ClientTickEvent event) {
      if (!nullCheck()) {
         if ((!this.timing.is(Timing.Pre) || !event.isPost()) && (!this.timing.is(Timing.Post) || !event.isPre())) {
            if (force) {
               ignore = true;
            }

            this.update();
            ignore = false;
         }
      }
   }

   @Override
   public void onDisable() {
      force = false;
   }

   private void update() {
      if (this.timer.passed((long)this.placeDelay.getValueInt())) {
         if (!this.inventorySwap.getValue() || EntityUtil.inInventory()) {
            this.pos.clear();
            this.progress = 0;
            this.directionVec = null;
            if (!this.preferAnchor.getValue() || AutoAnchor.INSTANCE.currentPos == null) {
               if (this.getWebSlot() != -1) {
                  if (!Blink.INSTANCE.isOn() || !Blink.INSTANCE.pauseModule.getValue()) {
                     if (!this.usingPause.getValue() || !this.shouldPauseForUsingItem()) {
                        label171:
                        for (PlayerEntity var2 : CombatUtil.getEnemies(this.targetRange.getValue())) {
                           Vec3d var3 = this.predictTicks.getValue() > 0.0 ? PredictUtil.getPos(var2, this.predictTicks.getValueInt()) : var2.getPos();
                           int var4 = 0;
                           if (this.feet.getValue() && this.placeWeb(new BlockPosX(var3.getX(), var3.getY(), var3.getZ()))) {
                              var4++;
                           }

                           if (this.down.getValue()) {
                              this.placeWeb(new BlockPosX(var3.getX(), var3.getY() - 0.8, var3.getZ()));
                           }

                           ArrayList var5 = new ArrayList();

                           for (float var9 : new float[]{0.0F, this.offset.getValueFloat(), -this.offset.getValueFloat()}) {
                              for (float var13 : new float[]{0.0F, this.offset.getValueFloat(), -this.offset.getValueFloat()}) {
                                 for (float var17 : new float[]{0.0F, 1.0F, -1.0F}) {
                                    BlockPosX var18 = new BlockPosX(var3.getX() + (double)var9, var3.getY() + (double)var17, var3.getZ() + (double)var13);
                                    if (!var5.contains(var18)) {
                                       var5.add(var18);
                                       if (this.isTargetHere(var18, var2)
                                          && mc.world.getBlockState(var18).getBlock() == Blocks.COBWEB
                                          && !CrucifixBeta.BREAK.isMining(var18)) {
                                          var4++;
                                       }
                                    }
                                 }
                              }
                           }

                           if (!((float)var4 >= this.maxWebs.getValueFloat()) || ignore) {
                              boolean var19 = false;
                              if (this.feetExtend.getValue()) {
                                 label145:
                                 for (float var26 : new float[]{0.0F, this.offset.getValueFloat(), -this.offset.getValueFloat()}) {
                                    for (float var34 : new float[]{0.0F, this.offset.getValueFloat(), -this.offset.getValueFloat()}) {
                                       BlockPosX var36 = new BlockPosX(var3.getX() + (double)var26, var3.getY(), var3.getZ() + (double)var34);
                                       if (this.isTargetHere(var36, var2) && this.placeWeb(var36)) {
                                          if ((float)(++var4) >= this.maxWebs.getValueFloat()) {
                                             var19 = true;
                                             break label145;
                                          }
                                       }
                                    }
                                 }
                              }

                              if (!var19 && this.face.getValue()) {
                                 for (float var27 : new float[]{0.0F, this.offset.getValueFloat(), -this.offset.getValueFloat()}) {
                                    for (float var35 : new float[]{0.0F, this.offset.getValueFloat(), -this.offset.getValueFloat()}) {
                                       BlockPosX var37 = new BlockPosX(var3.getX() + (double)var27, var3.getY() + 1.1, var3.getZ() + (double)var35);
                                       if (this.isTargetHere(var37, var2) && this.placeWeb(var37)) {
                                          if ((float)(++var4) >= this.maxWebs.getValueFloat()) {
                                             continue label171;
                                          }
                                       }
                                    }
                                 }
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }

   private boolean isTargetHere(BlockPos pos, PlayerEntity target) {
      return new Box(pos).intersects(target.getBoundingBox());
   }

   private boolean placeWeb(BlockPos pos) {
      if (this.pos.contains(pos)) {
         return false;
      } else {
         this.pos.add(pos);
         if (this.progress >= this.blocksPer.getValueInt()) {
            return false;
         } else if (this.getWebSlot() == -1) {
            return false;
         } else if (this.detectMining.getValue() && CrucifixBeta.BREAK.isMining(pos)) {
            return false;
         } else if (BlockUtil.getPlaceSide(pos, this.placeRange.getValue()) != null
            && (mc.world.isAir(pos) || ignore && BlockUtil.getBlock(pos) == Blocks.COBWEB)
            && pos.getY() < 320) {
            int var2 = mc.player.getInventory().selectedSlot;
            int var3 = this.getWebSlot();
            if (!this.placeBlock(pos, this.rotate.getValue(), var3)) {
               return false;
            } else {
               BlockUtil.placedPos.add(pos);
               this.progress++;
               if (this.inventorySwap.getValue()) {
                  this.doSwap(var3);
                  EntityUtil.syncInventory();
               } else {
                  this.doSwap(var2);
               }

               force = false;
               this.timer.reset();
               return true;
            }
         } else {
            return false;
         }
      }
   }

   public boolean placeBlock(BlockPos pos, boolean rotate, int slot) {
      Direction var4 = BlockUtil.getPlaceSide(pos);
      if (var4 == null) {
         return BlockUtil.allowAirPlace() ? this.clickBlock(pos, Direction.DOWN, rotate, slot) : false;
      } else {
         return this.clickBlock(pos.offset(var4), var4.getOpposite(), rotate, slot);
      }
   }

   public boolean clickBlock(BlockPos pos, Direction side, boolean rotate, int slot) {
      Vec3d var5 = new Vec3d(
         (double)pos.getX() + 0.5 + (double)side.getVector().getX() * 0.5,
         (double)pos.getY() + 0.5 + (double)side.getVector().getY() * 0.5,
         (double)pos.getZ() + 0.5 + (double)side.getVector().getZ() * 0.5
      );
      if (rotate && !this.faceVector(var5)) {
         return false;
      } else {
         this.doSwap(slot);
         EntityUtil.swingHand(Hand.MAIN_HAND, AntiCheat.INSTANCE.interactSwing.getValue());
         BlockHitResult var6 = new BlockHitResult(var5, side, pos, false);
         Module.sendSequencedPacket(id -> new PlayerInteractBlockC2SPacket(Hand.MAIN_HAND, var6, id));
         if (rotate && !this.shouldYawStep()) {
            CrucifixBeta.ROTATION.snapBack();
         }

         return true;
      }
   }

   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.inventorySwap.getValue()) {
         InventoryUtil.inventorySwap(slot, mc.player.getInventory().selectedSlot);
      } else {
         InventoryUtil.switchToSlot(slot);
      }
   }

   private int getWebSlot() {
      return this.inventorySwap.getValue() ? InventoryUtil.findBlockInventorySlot(Blocks.COBWEB) : InventoryUtil.findBlock(Blocks.COBWEB);
   }

   static {

      force = false;
      ignore = false;
   }
@Environment(EnvType.CLIENT)
   public static enum Page {
      General,
      Rotate;

}
}
