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

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.ClientTickEvent;
import dev.crucifixbeta.api.events.impl.EntityVelocityUpdateEvent;
import dev.crucifixbeta.api.events.impl.JumpEvent;
import dev.crucifixbeta.api.events.impl.KeyboardInputEvent;
import dev.crucifixbeta.api.events.impl.MoveEvent;
import dev.crucifixbeta.api.events.impl.MovedEvent;
import dev.crucifixbeta.api.events.impl.PacketEvent;
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.BlockUtil;
import dev.crucifixbeta.core.impl.CommandManager;
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 net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
import net.minecraft.util.math.Vec3d;

@Environment(EnvType.CLIENT)
public class HighJump extends Module {
   private final EnumSetting<HighJump.Mode> mode = this.add(new EnumSetting<>("Mode", HighJump.Mode.Normal));
   private final SliderSetting multiplier = this.add(
      new SliderSetting("Multiplier", 1.0, 0.0, 5.0, 0.1, () -> this.mode.is(HighJump.Mode.Normal))
   );
   private final BooleanSetting grimLogging = this.add(new BooleanSetting("GrimLogging", false, () -> this.mode.is(HighJump.Mode.GrimLowHop)));
   private final SliderSetting grimStartTicks = this.add(new SliderSetting("GrimStartTick", 2, 0, 10, () -> this.mode.is(HighJump.Mode.GrimLowHop)));
   private final SliderSetting grimSkipTicks = this.add(new SliderSetting("GrimSkipTicks", 2, 1, 10, () -> this.mode.is(HighJump.Mode.GrimLowHop)));
   private final SliderSetting grimTicks = this.add(new SliderSetting("GrimTicks", 3, 1, 10, () -> this.mode.is(HighJump.Mode.GrimLowHop)));
   private final SliderSetting longSpeed = this.add(
      new SliderSetting("LongSpeed", 0.2873, 0.0, 1.0, 1.0E-4, () -> this.mode.is(HighJump.Mode.LongJump))
   );
   private final BooleanSetting longJump = this.add(new BooleanSetting("LongJump", true, () -> this.mode.is(HighJump.Mode.LongJump)));
   private final SliderSetting longLagTime = this.add(
      new SliderSetting("LongLagTime", 500.0, 0.0, 1000.0, 1.0, () -> this.mode.is(HighJump.Mode.LongJump))
   );
   private final BooleanSetting longAirStop = this.add(new BooleanSetting("LongAirStop", false, () -> this.mode.is(HighJump.Mode.LongJump)));
   private final SliderSetting knockHeight = this.add(
      new SliderSetting("KnockHeight", 1.0, 0.5, 4.0, 0.1, () -> this.mode.is(HighJump.Mode.Knockback))
   );
   private final BooleanSetting knockOnlyGrounded = this.add(new BooleanSetting("KnockOnlyGrounded", true, () -> this.mode.is(HighJump.Mode.Knockback)));
   private final Timer longLagTimer = new Timer();
   private int grimAirTicks;
   private boolean grimCanTimer;
   private boolean grimTimer;
   private boolean longStop;
   private double longCurrentSpeed;
   private double longDistance;
   private int longStage;
   private boolean longBoost;

   public HighJump() {
      super("HighJump", Module.Category.Movement);
      this.setChinese("\u9ad8\u8df3");
   }

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

   @Override
   public void onEnable() {
      this.resetGrim();
      this.resetLongJump();
      if (mc.player != null) {
         this.longCurrentSpeed = MovementUtil.getSpeed(false);
         this.longDistance = MovementUtil.getDistance2D();
      }

      CrucifixBeta.TIMER.clearSkipTicks();
   }

   @Override
   public void onDisable() {
      this.resetGrim();
      this.resetLongJump();
      CrucifixBeta.TIMER.clearSkipTicks();
   }

   @EventListener
   public void onJump(JumpEvent event) {
      if (!nullCheck() && this.mode.is(HighJump.Mode.Normal)) {
         if (event.isPost()) {
            Vec3d var2 = mc.player.getVelocity();
            mc.player.setVelocity(var2.x, var2.y * this.multiplier.getValue(), var2.z);
         }
      }
   }

   @EventListener
   public void onPreTick(ClientTickEvent event) {
      if (this.mode.is(HighJump.Mode.GrimLowHop) && !event.isPost() && !nullCheck()) {
         if (this.grimAirTicks >= this.grimStartTicks.getValueInt() && !this.grimCanTimer) {
            CrucifixBeta.TIMER.addSkipTicks(this.grimSkipTicks.getValueInt());
            this.grimCanTimer = true;
            this.grimTimer = false;
         }
      }
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      if (this.mode.is(HighJump.Mode.GrimLowHop) && !nullCheck()) {
         if (mc.player.isOnGround()) {
            this.resetGrim();
         } else {
            this.grimAirTicks++;
            if (this.grimCanTimer && !this.grimTimer) {
               this.grimTimer = true;
               this.logGrim(
                  "GrimLowHop trigger: airTicks="
                     + this.grimAirTicks
                     + " skipTicks="
                     + this.grimSkipTicks.getValueInt()
                     + " ticks="
                     + this.grimTicks.getValueInt()
               );
               int var2 = this.grimTicks.getValueInt();

               for (int var3 = 0; var3 < var2; var3++) {
                  mc.player.tick();
               }

               this.logGrim("GrimLowHop done");
            }
         }
      }
   }

   @EventListener(
      priority = -200
   )
   public void onKeyboardInput(KeyboardInputEvent event) {
      if (this.mode.is(HighJump.Mode.GrimLowHop) && !nullCheck()) {
         mc.player.input.jumping = true;
      }
   }

   @EventListener(
      priority = 100
   )
   public void onPacketReceive(PacketEvent.Receive event) {
      if (this.mode.is(HighJump.Mode.LongJump)) {
         if (event.getPacket() instanceof PlayerPositionLookS2CPacket) {
            this.longLagTimer.reset();
            this.resetLongJump();
         }
      }
   }

   @EventListener
   public void onMoved(MovedEvent event) {
      if (this.mode.is(HighJump.Mode.LongJump) && !nullCheck()) {
         double var2 = mc.player.getX() - mc.player.prevX;
         double var4 = mc.player.getZ() - mc.player.prevZ;
         this.longDistance = Math.sqrt(var2 * var2 + var4 * var4);
      }
   }

   @EventListener
   public void onMove(MoveEvent event) {
      if (this.mode.is(HighJump.Mode.LongJump) && !nullCheck()) {
         if (!MovementUtil.isMoving() && this.longAirStop.getValue()) {
            MovementUtil.setMotionX(0.0);
            MovementUtil.setMotionZ(0.0);
         }

         if (mc.player.isRiding() || mc.player.isHoldingOntoLadder() || mc.player.getAbilities().flying || mc.player.isFallFlying() || !MovementUtil.isMoving()
            )
          {
            this.resetLongJump();
            this.longStop = true;
         } else if (this.longStop) {
            this.longStop = false;
         } else if (this.longLagTimer.passed((long)this.longLagTime.getValueInt())) {
            if (this.longStage == 1) {
               this.longCurrentSpeed = 1.35 * MovementUtil.getSpeed(false, this.longSpeed.getValue()) - 0.01;
            } else if (this.longStage != 2 || !mc.player.isOnGround() || !mc.options.jumpKey.isPressed() && !this.longJump.getValue()) {
               if (this.longStage == 3) {
                  this.longCurrentSpeed = this.longDistance - 0.66 * (this.longDistance - MovementUtil.getSpeed(false, this.longSpeed.getValue()));
                  this.longBoost = !this.longBoost;
               } else {
                  if ((BlockUtil.canCollide(null, mc.player.getBoundingBox().offset(0.0, MovementUtil.getMotionY(), 0.0)) || mc.player.collidedSoftly)
                     && this.longStage > 0) {
                     this.longStage = 1;
                  }

                  this.longCurrentSpeed = this.longDistance - this.longDistance / 159.0;
               }
            } else {
               double var2 = 0.3999 + MovementUtil.getJumpSpeed();
               MovementUtil.setMotionY(var2);
               event.setY(var2);
               this.longCurrentSpeed = this.longCurrentSpeed * (this.longBoost ? 1.6835 : 1.395);
            }

            this.longCurrentSpeed = Math.min(this.longCurrentSpeed, 10.0);
            this.longCurrentSpeed = Math.max(this.longCurrentSpeed, MovementUtil.getSpeed(false, this.longSpeed.getValue()));
            double var8 = (double)mc.player.input.movementForward;
            double var4 = (double)mc.player.input.movementSideways;
            double var6 = (double)mc.player.getYaw();
            if (var8 == 0.0 && var4 == 0.0) {
               event.setX(0.0);
               event.setZ(0.0);
            } else {
               if (var8 != 0.0 && var4 != 0.0) {
                  var8 *= Math.sin(Math.PI / 4);
                  var4 *= Math.cos(Math.PI / 4);
               }

               event.setX(
                  (var8 * this.longCurrentSpeed * -Math.sin(Math.toRadians(var6)) + var4 * this.longCurrentSpeed * Math.cos(Math.toRadians(var6))) * 0.99
               );
               event.setZ(
                  (var8 * this.longCurrentSpeed * Math.cos(Math.toRadians(var6)) - var4 * this.longCurrentSpeed * -Math.sin(Math.toRadians(var6))) * 0.99
               );
            }

            this.longStage++;
         }
      }
   }

   @EventListener
   public void onVelocity(EntityVelocityUpdateEvent event) {
      if (this.mode.is(HighJump.Mode.Knockback) && !nullCheck() && event.getEntity() == mc.player) {
         boolean var2 = event.isExplosion() || Math.abs(event.getX()) > 0.1 || Math.abs(event.getY()) > 0.1 || Math.abs(event.getZ()) > 0.1;
         if (var2 && (!this.knockOnlyGrounded.getValue() || mc.player.isOnGround())) {
            mc.player.addVelocity(0.0, this.knockHeight.getValue(), 0.0);
         }
      }
   }

   private void resetGrim() {
      this.grimAirTicks = 0;
      this.grimCanTimer = false;
      this.grimTimer = false;
   }

   private void resetLongJump() {
      this.longStage = 4;
      this.longCurrentSpeed = 0.0;
      this.longDistance = 0.0;
      this.longStop = false;
      this.longBoost = false;
   }

   private void logGrim(String message) {
      if (this.grimLogging.getValue()) {
         CommandManager.sendMessage(message);
      }
   }
@Environment(EnvType.CLIENT)
   public static enum Mode {
      Normal,
      GrimLowHop,
      LongJump,
      Knockback;

}
}
