package asm.mixins;

import com.mojang.blaze3d.systems.RenderSystem;
import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.Event;
import dev.crucifixbeta.api.events.impl.ClientTickEvent;
import dev.crucifixbeta.api.events.impl.DoAttackEvent;
import dev.crucifixbeta.api.events.impl.GameLeftEvent;
import dev.crucifixbeta.api.events.impl.OpenScreenEvent;
import dev.crucifixbeta.api.events.impl.ResizeEvent;
import dev.crucifixbeta.core.impl.CommandManager;
import dev.crucifixbeta.core.impl.FontManager;
import dev.crucifixbeta.mod.modules.impl.client.ClientSetting;
import dev.crucifixbeta.mod.modules.impl.player.InteractTweaks;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.SharedConstants;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.RunArgs;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.client.network.ServerInfo;
import net.minecraft.client.particle.ParticleManager;
import net.minecraft.client.resource.language.I18n;
import net.minecraft.client.util.Window;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.hit.HitResult.Type;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.thread.ReentrantThreadExecutor;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Environment(EnvType.CLIENT)
@Mixin({MinecraftClient.class})
public abstract class MixinMinecraftClient extends ReentrantThreadExecutor<Runnable> {
   @Shadow
   @Final
   public InGameHud inGameHud;
   @Shadow
   public int attackCooldown;
   @Shadow
   public ClientPlayerEntity player;
   @Shadow
   public HitResult crosshairTarget;
   @Shadow
   public ClientPlayerInteractionManager interactionManager;
   @Final
   @Shadow
   public ParticleManager particleManager;
   @Shadow
   public ClientWorld world;
   @Shadow
   private IntegratedServer server;
   @Shadow
   public Screen currentScreen;
   @Shadow
   @Final
   private Window window;

   public MixinMinecraftClient(String string) {
      super(string);
   }

   @Inject(
      method = {"onResolutionChanged"},
      at = {@At("TAIL")}
   )
   private void captureResize(CallbackInfo ci) {
      CrucifixBeta.EVENT_BUS.post(new ResizeEvent(this.window));
   }

   @Redirect(
      method = {"render"},
      at = @At(
         value = "INVOKE",
         target = "Lcom/mojang/blaze3d/systems/RenderSystem;limitDisplayFPS(I)V"
      ),
      require = 0
   )
   public void fpsHook(int fps) {
      if (!ClientSetting.INSTANCE.fuckFPSLimit.getValue()) {
         RenderSystem.limitDisplayFPS(fps);
      }
   }

   @Inject(
      method = {"<init>"},
      at = {@At("TAIL")}
   )
   void postWindowInit(RunArgs args, CallbackInfo ci) {
      FontManager.init();
   }

   @Inject(
      method = {"setScreen"},
      at = {@At("HEAD")},
      cancellable = true
   )
   private void onSetScreen(Screen screen, CallbackInfo info) {
      if (CrucifixBeta.EVENT_BUS != null) {
         OpenScreenEvent var3 = OpenScreenEvent.get(screen);
         CrucifixBeta.EVENT_BUS.post(var3);
         if (var3.isCancelled()) {
            info.cancel();
         }
      }
   }

   @Inject(
      method = {"doAttack"},
      at = {@At(
         value = "INVOKE",
         target = "Lnet/minecraft/util/hit/HitResult;getType()Lnet/minecraft/util/hit/HitResult$Type;",
         shift = Shift.BEFORE
      )}
   )
   public void onAttack(CallbackInfoReturnable<Boolean> cir) {
      CrucifixBeta.EVENT_BUS.post(DoAttackEvent.getPre());
   }

   @Inject(
      method = {"doAttack"},
      at = {@At(
         value = "INVOKE",
         target = "Lnet/minecraft/client/network/ClientPlayerEntity;swingHand(Lnet/minecraft/util/Hand;)V",
         shift = Shift.AFTER
      )}
   )
   public void onAttackPost(CallbackInfoReturnable<Boolean> cir) {
      if (CrucifixBeta.EVENT_BUS != null) {
         CrucifixBeta.EVENT_BUS.post(DoAttackEvent.getPost());
      }
   }

   @Inject(
      method = {"doAttack"},
      at = {@At("HEAD")},
      cancellable = true
   )
   public void doAttackHook(CallbackInfoReturnable<Boolean> cir) {
      if (CrucifixBeta.EVENT_BUS != null) {
         DoAttackEvent var2 = new DoAttackEvent();
         CrucifixBeta.EVENT_BUS.post(var2);
         if (var2.isCancelled()) {
            cir.setReturnValue(false);
         }
      }
   }

   @Inject(
      method = {"disconnect(Lnet/minecraft/client/gui/screen/Screen;)V"},
      at = {@At("HEAD")}
   )
   private void onDisconnect(Screen screen, CallbackInfo info) {
      if (CrucifixBeta.EVENT_BUS != null) {
         CrucifixBeta.EVENT_BUS.post(GameLeftEvent.INSTANCE);
      }

      if (CrucifixBeta.MODULE != null) {
         CrucifixBeta.MODULE.onLogout();
      }
   }

   @Inject(
      method = {"disconnect(Lnet/minecraft/client/gui/screen/Screen;)V"},
      at = {@At("HEAD")}
   )
   private void clearTitleMixin(Screen screen, CallbackInfo info) {
      if (ClientSetting.INSTANCE.titleFix.getValue()) {
         this.inGameHud.clearTitle();
         this.inGameHud.setDefaultTitleFade();
      }
   }

   @Inject(
      method = {"handleBlockBreaking"},
      at = {@At("HEAD")},
      cancellable = true
   )
   private void handleBlockBreaking(boolean breaking, CallbackInfo ci) {
      if (this.attackCooldown <= 0 && this.player.isUsingItem() && InteractTweaks.INSTANCE.multiTask()) {
         if (breaking && this.crosshairTarget != null && this.crosshairTarget.getType() == Type.BLOCK) {
            BlockHitResult var3 = (BlockHitResult)this.crosshairTarget;
            BlockPos var4 = var3.getBlockPos();
            Direction var5;
            if (!this.world.getBlockState(var4).isAir() && this.interactionManager.updateBlockBreakingProgress(var4, var5 = var3.getSide())) {
               this.particleManager.addBlockBreakingParticles(var4, var5);
               this.player.swingHand(Hand.MAIN_HAND);
            }
         } else {
            this.interactionManager.cancelBlockBreaking();
         }

         ci.cancel();
      }
   }

   @Inject(
      at = {@At("HEAD")},
      method = {"tick()V"}
   )
   public void tickHead(CallbackInfo info) {
      try {
         if (CrucifixBeta.EVENT_BUS != null) {
            CrucifixBeta.EVENT_BUS.post(ClientTickEvent.get(Event.Stage.Pre));
         }
      } catch (Exception var3) {
         var3.printStackTrace();
         if (ClientSetting.INSTANCE.debug.getValue()) {
            CommandManager.sendMessage("§4An error has occurred (MinecraftClient.tick() [HEAD]) Message: [" + var3.getMessage() + "]");
         }
      }
   }

   @Inject(
      at = {@At("TAIL")},
      method = {"tick()V"}
   )
   public void tickTail(CallbackInfo info) {
      try {
         if (CrucifixBeta.EVENT_BUS != null) {
            CrucifixBeta.EVENT_BUS.post(ClientTickEvent.get(Event.Stage.Post));
         }
      } catch (Exception var3) {
         var3.printStackTrace();
         if (ClientSetting.INSTANCE.debug.getValue()) {
            CommandManager.sendMessage("§4An error has occurred (MinecraftClient.tick() [TAIL]) Message: [" + var3.getMessage() + "]");
         }
      }
   }

   @Overwrite
   private String getWindowTitle() {
      if (ClientSetting.INSTANCE == null) {
         return "CrucifiX Beta: Loading..";
      } else if (ClientSetting.INSTANCE.titleOverride.getValue()) {
         return ClientSetting.INSTANCE.windowTitle.getValue();
      } else {
         StringBuilder var1 = new StringBuilder("CrucifiX Beta");
         var1.append(" ");
         var1.append(SharedConstants.getGameVersion().getName());
         ClientPlayNetworkHandler var2 = this.getNetworkHandler();
         if (var2 != null && var2.getConnection().isOpen()) {
            var1.append(" - ");
            ServerInfo var3 = this.getCurrentServerEntry();
            if (this.server != null && !this.server.isRemote()) {
               var1.append(I18n.translate("title.singleplayer"));
            } else if (var3 != null && var3.isRealm()) {
               var1.append(I18n.translate("title.multiplayer.realms"));
            } else if (this.server != null || var3 != null && var3.isLocal()) {
               var1.append(I18n.translate("title.multiplayer.lan"));
            } else {
               var1.append(I18n.translate("title.multiplayer.other"));
            }
         }

         return var1.toString();
      }
   }

   @Shadow
   public ClientPlayNetworkHandler getNetworkHandler() {
      return null;
   }

   @Shadow
   public ServerInfo getCurrentServerEntry() {
      return null;
   }
}
