package asm.mixins;

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.utils.Wrapper;
import dev.crucifixbeta.core.impl.ShaderManager;
import dev.crucifixbeta.mod.modules.impl.player.Freecam;
import dev.crucifixbeta.mod.modules.impl.render.Ambience;
import dev.crucifixbeta.mod.modules.impl.render.Chams;
import dev.crucifixbeta.mod.modules.impl.render.ShaderModule;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gl.PostEffectProcessor;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import net.minecraft.world.World;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL11;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Environment(EnvType.CLIENT)
@Mixin({WorldRenderer.class})
public abstract class MixinWorldRenderer {
   @Unique
   boolean renderingChams = false;
   @Unique
   boolean renderingEntity = false;
   @Unique
   private boolean crucifixSkyboxRenderedThisFrame = false;

   @Inject(
      method = {"renderEntity"},
      at = {@At("HEAD")}
   )
   private void injectChamsForEntity(
      Entity entity,
      double cameraX,
      double cameraY,
      double cameraZ,
      float tickDelta,
      MatrixStack matrices,
      VertexConsumerProvider vertexConsumers,
      CallbackInfo ci
   ) {
      Chams var12 = Chams.INSTANCE;
      if (var12 != null && var12.isOn() && var12.throughWall.getValue()) {
         if (var12.chams(entity)) {
            if (this.renderingEntity) {
               Wrapper.mc.getBufferBuilders().getEntityVertexConsumers().draw();
               this.renderingEntity = false;
            }

            GL11.glEnable(32823);
            GL11.glPolygonOffset(1.0F, -1000000.0F);
            this.renderingChams = true;
         } else {
            this.renderingEntity = true;
         }
      }
   }

   @Inject(
      method = {"renderEntity"},
      at = {@At("RETURN")}
   )
   private void injectChamsForEntityPost(
      Entity entity,
      double cameraX,
      double cameraY,
      double cameraZ,
      float tickDelta,
      MatrixStack matrices,
      VertexConsumerProvider vertexConsumers,
      CallbackInfo ci
   ) {
      Chams var12 = Chams.INSTANCE;
      if (var12 != null && var12.isOn() && var12.throughWall.getValue() && this.renderingChams) {
         Wrapper.mc.getBufferBuilders().getEntityVertexConsumers().draw();
         GL11.glPolygonOffset(1.0F, 1000000.0F);
         GL11.glDisable(32823);
         this.renderingChams = false;
      }
   }

   @Inject(
      method = {"renderSky"},
      at = {@At("HEAD")},
      cancellable = true,
      require = 0
   )
   private void injectSkyboxHead(
      Matrix4f projectionMatrix, Matrix4f positionMatrix, float tickDelta, Camera camera, boolean thickFog, Runnable fogCallback, CallbackInfo ci
   ) {
      if (Ambience.INSTANCE != null && Ambience.INSTANCE.shouldReplaceVanillaSkyBox()) {
         Ambience.INSTANCE.renderSkybox(positionMatrix, camera, tickDelta);
         this.crucifixSkyboxRenderedThisFrame = true;
         ci.cancel();
      }
   }

   @Inject(
      method = {"renderSky"},
      at = {@At("TAIL")},
      require = 0
   )
   private void injectSkyboxTail(
      Matrix4f projectionMatrix, Matrix4f positionMatrix, float tickDelta, Camera camera, boolean thickFog, Runnable fogCallback, CallbackInfo ci
   ) {
      if (Ambience.INSTANCE != null && Ambience.INSTANCE.shouldRenderSkyBox() && !Ambience.INSTANCE.shouldReplaceVanillaSkyBox()) {
         Ambience.INSTANCE.renderSkybox(positionMatrix, camera, tickDelta);
         this.crucifixSkyboxRenderedThisFrame = true;
      }
   }

   @Redirect(
      method = {"render"},
      at = @At(
         value = "INVOKE",
         target = "Lnet/minecraft/client/gl/PostEffectProcessor;render(F)V",
         ordinal = 0
      ),
      require = 0
   )
   void replaceShaderHook(PostEffectProcessor instance, float tickDelta) {
      ShaderModule var3 = ShaderModule.INSTANCE;
      if (var3 != null
         && var3.isOn()
         && var3.shouldRenderOutlinePass()
         && Wrapper.mc.world != null
         && CrucifixBeta.SHADER != null
         && CrucifixBeta.SHADER.ensureOutlineShadersReady()) {
         ShaderManager.Shader var4 = var3.mode.getValue();
         CrucifixBeta.SHADER.setupShader(var4, CrucifixBeta.SHADER.getShaderOutline(var4));
      } else {
         instance.render(tickDelta);
      }
   }

   @ModifyArg(
      method = {"render"},
      at = @At(
         value = "INVOKE",
         target = "Lnet/minecraft/client/render/WorldRenderer;setupTerrain(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/Frustum;ZZ)V"
      ),
      index = 3
   )
   private boolean renderSetupTerrainModifyArg(boolean spectator) {
      this.renderNetherSkyboxFallback();
      this.crucifixSkyboxRenderedThisFrame = false;
      return Freecam.INSTANCE.isOn() || spectator;
   }

   @Unique
   private void renderNetherSkyboxFallback() {
      if (!this.crucifixSkyboxRenderedThisFrame
         && Ambience.INSTANCE != null
         && Ambience.INSTANCE.shouldRenderSkyBox()
         && Wrapper.mc.world != null
         && Wrapper.mc.world.getRegistryKey() == World.NETHER) {
         Ambience.INSTANCE.renderSkybox(null, null, Wrapper.mc.getRenderTickCounter().getTickDelta(true));
         this.crucifixSkyboxRenderedThisFrame = true;
      }
   }
}
