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

import asm.accessors.IScreenHandler;
import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.InventoryS2CPacketEvent;
import dev.crucifixbeta.api.events.impl.LerpToEvent;
import dev.crucifixbeta.api.events.impl.PacketEvent;
import dev.crucifixbeta.api.events.impl.S2CCloseScreenEvent;
import dev.crucifixbeta.api.events.impl.ServerConnectBeginEvent;
import dev.crucifixbeta.api.events.impl.TickEvent;
import dev.crucifixbeta.api.utils.math.Timer;
import dev.crucifixbeta.api.utils.player.EntityUtil;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.player.Sorter;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import java.util.List;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.item.ItemStack;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.c2s.play.ClickSlotC2SPacket;
import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket;
import net.minecraft.network.packet.s2c.play.InventoryS2CPacket;
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
import net.minecraft.network.packet.s2c.play.UpdateSelectedSlotS2CPacket;
import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;

@Environment(EnvType.CLIENT)
public class AntiPacket extends Module {
   public static AntiPacket INSTANCE;
   public final BooleanSetting decode = this.add(new BooleanSetting("Decode", true));
   public final BooleanSetting lerp = this.add(new BooleanSetting("Lerp", false));
   public final BooleanSetting s2CSlot = this.add(new BooleanSetting("S2CSlot", true));
   public final BooleanSetting s2CRotate = this.add(new BooleanSetting("S2CRotate", true).setParent());
   public final BooleanSetting applyYaw = this.add(new BooleanSetting("ApplyYaw", true, this.s2CRotate::isOpen));
   public final BooleanSetting s2CCloseScreen = this.add(new BooleanSetting("S2CCloseScreen", true));
   public final BooleanSetting s2CInventory = this.add(new BooleanSetting("S2CInventory", false).setParent());
   public final SliderSetting timeout = this.add(new SliderSetting("Timeout", 500, 0, 1000, this.s2CInventory::isOpen));
   public final BooleanSetting autoSync = this.add(new BooleanSetting("AutoSync", false, this.s2CInventory::isOpen).setParent());
   public final SliderSetting delay = this.add(
      new SliderSetting("Delay", 2000, 0, 5000, () -> this.s2CInventory.isOpen() && this.autoSync.isOpen())
   );
   private final Timer applyTimer = new Timer();
   private final Timer clickSlot = new Timer();
   private final Timer sync = new Timer();
   ClickSlotC2SPacket syncPacket;

   public AntiPacket() {
      super("AntiPacket", Module.Category.Exploit);
      this.setChinese("\u53cd\u6570\u636e\u5305");
      INSTANCE = this;
   }

   @EventListener
   public void onServerConnectBegin(ServerConnectBeginEvent event) {
      this.applyTimer.reset();
   }

   @Override
   public void onLogin() {
      this.applyTimer.reset();
   }

   @EventListener
   public void onCloseScreen(S2CCloseScreenEvent event) {
      if (this.s2CCloseScreen.getValue() && EntityUtil.inInventory()) {
         event.cancel();
      }
   }

   @EventListener
   public void onEvent(LerpToEvent event) {
      if (this.lerp.getValue()) {
         event.getEntity().setPos(event.getX(), event.getY(), event.getZ());
         event.getEntity().setPitch(event.getXRot());
         event.getEntity().setYaw(event.getYRot());
      }
   }

   @EventListener
   public void onPacketSend(PacketEvent.Send event) {
      Packet var2 = event.getPacket();
      ClickSlotC2SPacket var3;
      if (var2 instanceof ClickSlotC2SPacket && (var3 = (ClickSlotC2SPacket)var2) != this.syncPacket) {
         this.clickSlot.reset();
         this.sync.reset();
      }
   }

   @EventListener
   public void onTick(TickEvent event) {
      if (event.isPre()
         && this.s2CInventory.getValue()
         && !mc.player.isCreative()
         && Sorter.INSTANCE.isOff()
         && this.autoSync.getValue()
         && this.clickSlot.passed((long)this.timeout.getValueInt())
         && this.sync.passed((long)this.delay.getValueInt())) {
         this.syncPacket = new ClickSlotC2SPacket(
            mc.player.playerScreenHandler.syncId,
            mc.player.playerScreenHandler.getRevision(),
            -1,
            0,
            SlotActionType.PICKUP,
            mc.player.playerScreenHandler.getCursorStack().copy(),
            new Int2ObjectArrayMap()
         );
         mc.getNetworkHandler().sendPacket(this.syncPacket);
         this.sync.reset();
      }
   }

   @EventListener
   public void onInventory(InventoryS2CPacketEvent event) {
      InventoryS2CPacket var2 = event.packet;
      if (this.s2CInventory.getValue() && Sorter.INSTANCE.isOff() && !mc.player.isCreative()) {
         List var3 = var2.getContents();
         boolean var4 = this.clickSlot.passed((long)this.timeout.getValueInt());

         for (int var5 = 0; var5 < var3.size(); var5++) {
            ItemStack var6 = (ItemStack)var3.get(var5);
            Slot var7 = mc.player.playerScreenHandler.getSlot(var5);
            ItemStack var8 = var7.getStack();
            if (var4 || var8.getItem() == var6.getItem() || var6.isEmpty() && var8.isStackable()) {
               var7.setStackNoCallbacks(var6);
            }
         }

         if (var4) {
            mc.player.playerScreenHandler.setCursorStack(var2.getCursorStack());
            ((IScreenHandler)mc.player.playerScreenHandler).setRevision(var2.getRevision());
            this.clickSlot.reset();
         }

         event.cancel();
      }
   }

   @EventListener
   public void onPacketReceive(PacketEvent.Receive event) {
      if (this.applyTimer.passed(1000L) && !nullCheck()) {
         Packet var2;
         if (this.s2CSlot.getValue() && (var2 = event.getPacket()) instanceof UpdateSelectedSlotS2CPacket) {
            UpdateSelectedSlotS2CPacket var3 = (UpdateSelectedSlotS2CPacket)var2;
            event.setCancelled(true);
            CrucifixBeta.SERVER.currentSlot = -1;
            mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(var3.getSlot()));
            CrucifixBeta.SERVER.currentSlot = -1;
            mc.getNetworkHandler().sendPacket(new UpdateSelectedSlotC2SPacket(mc.player.getInventory().selectedSlot));
         } else if (this.s2CInventory.getValue()
            && event.getPacket() instanceof ScreenHandlerSlotUpdateS2CPacket
            && Sorter.INSTANCE.isOff()
            && !mc.player.isCreative()) {
            event.cancel();
         }
      }
   }
}
