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

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.MoveEvent;
import dev.crucifixbeta.api.events.impl.PacketEvent;
import dev.crucifixbeta.api.events.impl.TimerEvent;
import dev.crucifixbeta.api.events.impl.UpdateEvent;
import dev.crucifixbeta.api.utils.math.Timer;
import dev.crucifixbeta.api.utils.player.MovementUtil;
import dev.crucifixbeta.api.utils.world.BlockPosX;
import dev.crucifixbeta.mod.modules.Module;
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.HashMap;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.BlockState;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult.Type;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RaycastContext;
import net.minecraft.world.RaycastContext.FluidHandling;
import net.minecraft.world.RaycastContext.ShapeType;

@Environment(EnvType.CLIENT)
public class FastFall extends Module {
   private final EnumSetting<FastFall.Mode> mode = this.add(new EnumSetting<>("Mode", FastFall.Mode.Fast));
   private final BooleanSetting noLag = this.add(new BooleanSetting("NoLag", true, () -> this.mode.getValue() == FastFall.Mode.Fast));
   private final BooleanSetting useTimerSetting = this.add(new BooleanSetting("UseTimer", false));
   private final SliderSetting timer = this.add(new SliderSetting("Timer", 2.5, 1.0, 8.0, 0.1, this.useTimerSetting::getValue));
   private final BooleanSetting anchor = this.add(new BooleanSetting("Anchor", true));
   private final SliderSetting height = this.add(new SliderSetting("Height", 10.0, 1.0, 20.0, 0.5));
   private final Timer lagTimer = new Timer();
   boolean onGround = false;
   private boolean useTimer;

   public FastFall() {
      super("FastFall", "Miyagi son simulator", Module.Category.Movement);
      this.setChinese("\u5feb\u901f\u5760\u843d");
   }

   @Override
   public void onDisable() {
      this.useTimer = false;
   }

   @Override
   public String getInfo() {
      return this.mode.getValue().name();
   }

   @EventListener(
      priority = -100
   )
   public void onMove(MoveEvent event) {
      if (!nullCheck()) {
         if (mc.player.isOnGround() && this.anchor.getValue() && this.traceDown() != 0 && (double)this.traceDown() <= this.height.getValue() && this.trace()) {
            event.setX(event.getX() * 0.05);
            event.setZ(event.getZ() * 0.05);
         }
      }
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      if ((!(this.height.getValue() > 0.0) || !((double)this.traceDown() > this.height.getValue()))
         && !mc.player.isInsideWall()
         && !mc.player.isSubmergedInWater()
         && !mc.player.isInLava()
         && !mc.player.isHoldingOntoLadder()
         && this.lagTimer.passed(1000L)
         && !mc.player.isFallFlying()
         && !Fly.INSTANCE.isOn()
         && !nullCheck()) {
         if (!CrucifixBeta.PLAYER.isInWeb(mc.player)) {
            if (mc.player.isOnGround() && this.mode.getValue() == FastFall.Mode.Fast) {
               MovementUtil.setMotionY(MovementUtil.getMotionY() - (double)(this.noLag.getValue() ? 0.62F : 1.0F));
            }

            if (this.useTimerSetting.getValue()) {
               if (!mc.player.isOnGround()) {
                  if (this.onGround) {
                     this.useTimer = true;
                  }

                  if (MovementUtil.getMotionY() >= 0.0) {
                     this.useTimer = false;
                  }

                  this.onGround = false;
               } else {
                  this.useTimer = false;
                  MovementUtil.setMotionY(-0.08);
                  this.onGround = true;
               }
            } else {
               this.useTimer = false;
            }
         }
      }
   }

   @EventListener
   public void onTimer(TimerEvent event) {
      if (!nullCheck()) {
         if (!mc.player.isOnGround() && this.useTimer) {
            event.set(this.timer.getValueFloat());
         }
      }
   }

   @EventListener
   public void onPacket(PacketEvent.Receive event) {
      if (!nullCheck() && event.getPacket() instanceof PlayerPositionLookS2CPacket) {
         this.lagTimer.reset();
      }
   }

   private int traceDown() {
      int var1 = 0;

      for (int var3 = (int)Math.round(mc.player.getY()) - 1; var3 >= 0; var3--) {
         BlockHitResult var4 = mc.world
            .raycast(
               new RaycastContext(
                  mc.player.getPos(), new Vec3d(mc.player.getX(), (double)var3, mc.player.getZ()), ShapeType.COLLIDER, FluidHandling.NONE, mc.player
               )
            );
         if (var4 != null && var4.getType() == Type.BLOCK) {
            return var1;
         }

         var1++;
      }

      return var1;
   }

   private boolean trace() {
      Box var1 = mc.player.getBoundingBox();
      Vec3d var2 = var1.getCenter();
      double var3 = var1.minX;
      double var5 = var1.minZ;
      double var7 = var1.maxX;
      double var9 = var1.maxZ;
      HashMap<Vec3d, Vec3d> var11 = new HashMap<>();
      var11.put(var2, new Vec3d(var2.x, var2.y - 1.0, var2.z));
      var11.put(new Vec3d(var3, var2.y, var5), new Vec3d(var3, var2.y - 1.0, var5));
      var11.put(new Vec3d(var7, var2.y, var5), new Vec3d(var7, var2.y - 1.0, var5));
      var11.put(new Vec3d(var3, var2.y, var9), new Vec3d(var3, var2.y - 1.0, var9));
      var11.put(new Vec3d(var7, var2.y, var9), new Vec3d(var7, var2.y - 1.0, var9));

      for (Vec3d var13 : var11.keySet()) {
         RaycastContext var14 = new RaycastContext(var13, (Vec3d)var11.get(var13), ShapeType.COLLIDER, FluidHandling.NONE, mc.player);
         BlockHitResult var15 = mc.world.raycast(var14);
         if (var15 != null && var15.getType() == Type.BLOCK) {
            return false;
         }
      }

      BlockState var16 = mc.world.getBlockState(new BlockPosX(mc.player.getX(), mc.player.getY() - 1.0, mc.player.getZ()));
      return var16.isAir();
   }
@Environment(EnvType.CLIENT)
   private static enum Mode {
      Fast,
      None;

}
}
