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

import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.EntityVelocityUpdateEvent;
import dev.crucifixbeta.api.events.impl.KeyboardInputEvent;
import dev.crucifixbeta.api.events.impl.PacketEvent;
import dev.crucifixbeta.api.events.impl.TickEvent;
import dev.crucifixbeta.api.events.impl.UpdateEvent;
import dev.crucifixbeta.api.utils.math.Timer;
import dev.crucifixbeta.api.utils.player.InventoryUtil;
import dev.crucifixbeta.api.utils.player.MovementUtil;
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.ArrayDeque;
import java.util.Deque;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.item.BoatItem;
import net.minecraft.item.ItemStack;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.c2s.common.CommonPongC2SPacket;
import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;

@Environment(EnvType.CLIENT)
public class Fly extends PacketFly {
   public static Fly INSTANCE;
   public final EnumSetting<Fly.Mode> mode = this.add(new EnumSetting<>("Mode", Fly.Mode.Vanilla));
   private final SliderSetting speedConfig = this.add(new SliderSetting("Speed", 2.5, 0.1, 10.0));
   private final SliderSetting vspeedConfig = this.add(new SliderSetting("VerticalSpeed", 1.0, 0.1, 5.0));
   private final BooleanSetting antiKickConfig = this.add(new BooleanSetting("AntiKick", true).setParent());
   private final BooleanSetting up = this.add(new BooleanSetting("Up", true, this.antiKickConfig::isOpen));
   private final BooleanSetting allowSneak = this.add(new BooleanSetting("AllowSneak", false));
   private final Timer boatTimer = new Timer();
   private final Timer antiKickTimer = new Timer();
   private final Timer antiKick2Timer = new Timer();
   private boolean grimTriggered;
   private boolean grimStartQueued;
   private int grimTicks;
   private boolean grimSkidActive;
   private boolean grimSkidQueuedStart;
   private int grimSkidTicks;
   private boolean grimSkidFlushing;
   private final Deque<Packet<?>> grimSkidQueue = new ArrayDeque<>();

   public Fly() {
      super("Fly", Module.Category.Movement);
      this.setChinese("\u98de\u884c");
      INSTANCE = this;
   }

   @Override
   public void onLogin() {
      this.antiKickTimer.reset();
      this.antiKick2Timer.reset();
      this.resetGrim();
      this.resetGrimSkid();
      if (this.mode.is(Fly.Mode.Packet)) {
         super.onEnable();
      }
   }

   @Override
   public void onEnable() {
      if (!nullCheck()) {
         this.antiKickTimer.reset();
         this.antiKick2Timer.reset();
         this.resetGrim();
         this.resetGrimSkid();
      }
   }

   @Override
   public void onDisable() {
      this.flushGrimSkidQueue();
      this.resetGrim();
      this.resetGrimSkid();
      super.onDisable();
   }

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

   @EventListener
   @Override
   public void onUpdate(UpdateEvent event) {
      if (this.mode.is(Fly.Mode.Grim)) {
         this.onUpdateGrim();
      } else {
         if (this.mode.is(Fly.Mode.Packet)) {
            return;
         }

         if (this.mode.is(Fly.Mode.GrimSkid)) {
            this.onUpdateVanilla();
         } else {
            this.onUpdateVanilla();
         }
      }
   }

   @EventListener
   public void onTick(TickEvent event) {
      if (event.isPre() && !nullCheck()) {
         if (this.mode.is(Fly.Mode.Boat) && this.boatTimer.passedMs(500.0)) {
            this.boatTimer.reset();
            int var2 = this.findBoat();
            if (var2 != -1) {
               BlockPos var3 = mc.player.getBlockPos().down();
               if (!mc.world.getBlockState(var3).isAir()) {
                  int var4 = mc.player.getInventory().selectedSlot;
                  InventoryUtil.inventorySwap(var2, var4);
                  mc.getNetworkHandler()
                     .sendPacket(new PlayerInteractBlockC2SPacket(Hand.MAIN_HAND, new BlockHitResult(var3.toCenterPos(), Direction.UP, var3, false), 0));
                  InventoryUtil.inventorySwap(var2, var4);
                  if (!mc.player.isOnGround()) {
                     mc.player.jump();
                  }
               }
            }
         }

         if (this.mode.is(Fly.Mode.GrimSkid) && this.grimSkidQueuedStart) {
            if (++this.grimSkidTicks >= 8) {
               mc.getNetworkHandler().sendPacket(new ClientCommandC2SPacket(mc.player, ClientCommandC2SPacket.Mode.START_FALL_FLYING));
               this.grimSkidTicks = 0;
               this.grimSkidQueuedStart = false;
            }
         }
      }
   }

   @EventListener
   public void onVelocity(EntityVelocityUpdateEvent event) {
      if (!nullCheck()) {
         if (this.mode.is(Fly.Mode.GrimSkid)) {
            if (event.getEntity() == mc.player && !this.grimSkidActive && this.grimSkidQueue.isEmpty()) {
               event.cancel();
               this.grimSkidTicks = 0;
               this.grimSkidActive = true;
            }
         } else if (this.mode.is(Fly.Mode.Grim) && event.getEntity() == mc.player) {
            if (!this.grimTriggered) {
               event.cancel();
               this.grimTriggered = true;
               this.grimStartQueued = true;
               this.grimTicks = 0;
            }
         }
      }
   }

   @EventListener
   public void onReceive(PacketEvent.Receive event) {
      if (this.mode.is(Fly.Mode.GrimSkid)) {
         if (event.getPacket() instanceof PlayerPositionLookS2CPacket && this.grimSkidActive && !this.grimSkidQueue.isEmpty()) {
            this.flushGrimSkidQueue();
         }
      } else {
         if (this.mode.is(Fly.Mode.Grim) && event.getPacket() instanceof PlayerPositionLookS2CPacket) {
            this.grimStartQueued = true;
            this.grimTicks = 0;
         }
      }
   }

   @EventListener
   public void onSend(PacketEvent.Send event) {
      if (!this.grimSkidFlushing && !nullCheck() && this.mode.is(Fly.Mode.GrimSkid)) {
         Packet var2 = event.getPacket();
         if (var2 instanceof CommonPongC2SPacket && this.grimSkidActive) {
            boolean var3 = this.grimSkidQueue.isEmpty();
            event.cancel();
            this.grimSkidQueue.add(var2);
            if (var3) {
               this.grimSkidQueuedStart = true;
               this.grimSkidTicks = 0;
            }
         } else {
            if (var2 instanceof PlayerInteractEntityC2SPacket && this.grimSkidActive && !this.grimSkidQueue.isEmpty()) {
               this.flushGrimSkidQueue();
            }
         }
      }
   }

   @EventListener(
      priority = -100
   )
   public void keyboard(KeyboardInputEvent event) {
      if (!this.allowSneak.getValue()) {
         mc.player.input.sneaking = false;
      }
   }

   private void onUpdateVanilla() {
      if (this.antiKickTimer.passed(3900L) && this.antiKickConfig.getValue() && !mc.player.isOnGround()) {
         MovementUtil.setMotionY(-0.04);
         this.antiKickTimer.reset();
      } else if (this.antiKick2Timer.passed(4000L) && this.antiKickConfig.getValue() && !mc.player.isOnGround() && this.up.getValue()) {
         MovementUtil.setMotionY(0.04);
         this.antiKick2Timer.reset();
      } else {
         MovementUtil.setMotionY(0.0);
         if (mc.options.jumpKey.isPressed()) {
            MovementUtil.setMotionY(this.vspeedConfig.getValue());
         } else if (mc.options.sneakKey.isPressed()) {
            MovementUtil.setMotionY(-this.vspeedConfig.getValue());
         }
      }

      double[] var1 = MovementUtil.directionSpeed((double)this.speedConfig.getValueFloat());
      MovementUtil.setMotionX(var1[0]);
      MovementUtil.setMotionZ(var1[1]);
   }

   private void onUpdateGrim() {
      this.onUpdateVanilla();
      if (this.grimStartQueued) {
         if (mc.player.isOnGround()) {
            this.grimTicks = 0;
         } else if (++this.grimTicks >= 8) {
            mc.getNetworkHandler().sendPacket(new ClientCommandC2SPacket(mc.player, ClientCommandC2SPacket.Mode.START_FALL_FLYING));
            this.grimTicks = 0;
            this.grimStartQueued = false;
         }
      }
   }

   private void resetGrim() {
      this.grimTriggered = false;
      this.grimStartQueued = false;
      this.grimTicks = 0;
   }

   private void resetGrimSkid() {
      this.grimSkidActive = false;
      this.grimSkidQueuedStart = false;
      this.grimSkidTicks = 0;
      this.grimSkidFlushing = false;
      this.grimSkidQueue.clear();
   }

   private void flushGrimSkidQueue() {
      this.grimSkidQueuedStart = false;
      this.grimSkidTicks = 0;
      if (!this.grimSkidQueue.isEmpty()) {
         if (nullCheck()) {
            this.grimSkidQueue.clear();
         } else {
            this.grimSkidFlushing = true;

            while (!this.grimSkidQueue.isEmpty()) {
               mc.getNetworkHandler().sendPacket(this.grimSkidQueue.poll());
            }

            this.grimSkidFlushing = false;
         }
      }
   }

   private int findBoat() {
      for (int var1 = 0; var1 < 45; var1++) {
         ItemStack var2 = mc.player.getInventory().getStack(var1);
         if (var2.getItem() instanceof BoatItem) {
            return var1;
         }
      }

      return -1;
   }

   @Override
   protected boolean packetFlyActive() {
      return this.isOn() && this.mode.is(Fly.Mode.Packet);
   }

   @Override
   protected boolean packetSettingsVisible() {
      return this.mode.is(Fly.Mode.Packet);
   }
@Environment(EnvType.CLIENT)
   public static enum Mode {
      Vanilla,
      Grim,
      GrimSkid,
      Boat,
      Packet;

}
}
