package net.fabricmc.fabric.mixin.client.rendering;

import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.rendering.v1.InvalidateRenderStateCallback;
import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents;
import net.fabricmc.fabric.impl.client.rendering.world.WorldExtractionContextImpl;
import net.fabricmc.fabric.impl.client.rendering.world.WorldRenderContextImpl;
import net.minecraft.class_11532;
import net.minecraft.class_11658;
import net.minecraft.class_11661;
import net.minecraft.class_12078;
import net.minecraft.class_243;
import net.minecraft.class_2784;
import net.minecraft.class_310;
import net.minecraft.class_4184;
import net.minecraft.class_4587;
import net.minecraft.class_4597;
import net.minecraft.class_4599;
import net.minecraft.class_4604;
import net.minecraft.class_4618;
import net.minecraft.class_638;
import net.minecraft.class_761;
import net.minecraft.class_9779;
import net.minecraft.class_9922;
import net.minecraft.class_9978;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
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.Slice;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Environment(EnvType.CLIENT)
@Mixin({class_761.class})
public abstract class WorldRendererMixin {
   @Shadow
   @Final
   private class_310 field_4088;
   @Shadow
   @Final
   private class_4599 field_20951;
   @Shadow
   @Final
   private class_11658 field_61737;
   @Shadow
   private @Nullable class_638 field_4085;
   @Shadow
   @Final
   private class_11661 field_61738;
   @Unique
   private final WorldRenderContextImpl renderContext = new WorldRenderContextImpl();
   @Unique
   private final WorldExtractionContextImpl extractionContext = new WorldExtractionContextImpl();

   @Inject(
      method = {"method_22710(Lnet/minecraft/class_9922;Lnet/minecraft/class_9779;ZLnet/minecraft/class_4184;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lorg/joml/Vector4f;Z)V"},
      at = {@At("HEAD")}
   )
   private void beforeRender(class_9922 allocator, class_9779 tickCounter, boolean renderBlockOutline, class_4184 camera, Matrix4f viewMatrix, Matrix4f projectionMatrix, Matrix4f cullProjectionMatrix, GpuBufferSlice fogBuffer, Vector4f fogColor, boolean renderSky, CallbackInfo ci) {
      this.extractionContext.prepare(this.field_4088.field_1773, (class_761)this, this.field_61737, this.field_4085, tickCounter, renderBlockOutline, camera, viewMatrix, cullProjectionMatrix);
   }

   @ModifyExpressionValue(
      method = {"method_22710(Lnet/minecraft/class_9922;Lnet/minecraft/class_9779;ZLnet/minecraft/class_4184;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lorg/joml/Vector4f;Z)V"},
      at = {@At(
   value = "INVOKE",
   target = "Lnet/minecraft/class_761;method_32133(Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lnet/minecraft/class_243;)Lnet/minecraft/class_4604;"
)}
   )
   private class_4604 onSetupFrustum(class_4604 frustum) {
      this.extractionContext.setFrustum(frustum);
      return frustum;
   }

   @Inject(
      method = {"method_74923(Lnet/minecraft/class_4184;Lnet/minecraft/class_11658;)V"},
      at = {@At("RETURN")}
   )
   private void afterBlockOutlineExtraction(class_4184 camera, class_11658 renderStates, CallbackInfo ci) {
      ((WorldRenderEvents.AfterBlockOutlineExtraction)WorldRenderEvents.AFTER_BLOCK_OUTLINE_EXTRACTION.invoker()).afterBlockOutlineExtraction(this.extractionContext, this.field_4088.field_1765);
   }

   @WrapOperation(
      method = {"method_22710(Lnet/minecraft/class_9922;Lnet/minecraft/class_9779;ZLnet/minecraft/class_4184;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lorg/joml/Vector4f;Z)V"},
      at = {@At(
   value = "INVOKE",
   target = "Lnet/minecraft/class_9978;method_74927(Lnet/minecraft/class_2784;Lnet/minecraft/class_243;DLnet/minecraft/class_12078;)V"
)}
   )
   private void onWorldBorderExtraction(class_9978 instance, class_2784 worldBorder, class_243 vec3d, double d, class_12078 worldBorderRenderState, Operation<Void> original) {
      original.call(new Object[]{instance, worldBorder, vec3d, d, worldBorderRenderState});
      ((WorldRenderEvents.EndExtraction)WorldRenderEvents.END_EXTRACTION.invoker()).endExtraction(this.extractionContext);
   }

   @ModifyExpressionValue(
      method = {"method_62214(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lnet/minecraft/class_11658;Lnet/minecraft/class_3695;Lorg/joml/Matrix4f;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;ZLnet/minecraft/class_4604;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;)V"},
      at = {@At(
   value = "INVOKE",
   target = "Lnet/minecraft/class_761;method_72157(Lorg/joml/Matrix4fc;DDD)Lnet/minecraft/class_11532;"
)}
   )
   private class_11532 onRenderBlockLayers(class_11532 sectionRenderState) {
      this.renderContext.prepare(this.field_4088.field_1773, (class_761)this, this.field_61737, sectionRenderState, this.field_61738, this.field_20951.method_23000());
      return sectionRenderState;
   }

   @Inject(
      method = {"method_62214(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lnet/minecraft/class_11658;Lnet/minecraft/class_3695;Lorg/joml/Matrix4f;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;ZLnet/minecraft/class_4604;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;)V"},
      slice = {@Slice(
   from = @At(
   value = "INVOKE",
   target = "Lnet/minecraft/class_761;method_72157(Lorg/joml/Matrix4fc;DDD)Lnet/minecraft/class_11532;"
)
)},
      at = {@At(
   value = "INVOKE",
   target = "Lnet/minecraft/class_11532;method_72170(Lnet/minecraft/class_11531;)V",
   ordinal = 0
)}
   )
   private void beforeTerrainRender(CallbackInfo ci) {
      ((WorldRenderEvents.StartMain)WorldRenderEvents.START_MAIN.invoker()).startMain(this.renderContext);
   }

   @ModifyExpressionValue(
      method = {"method_62214(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lnet/minecraft/class_11658;Lnet/minecraft/class_3695;Lorg/joml/Matrix4f;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;ZLnet/minecraft/class_4604;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;)V"},
      at = {@At(
   value = "NEW",
   target = "Lnet/minecraft/class_4587;"
)}
   )
   private class_4587 onCreateMatrixStack(class_4587 matrixStack) {
      this.renderContext.setMatrixStack(matrixStack);
      return matrixStack;
   }

   @Inject(
      method = {"method_62214(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lnet/minecraft/class_11658;Lnet/minecraft/class_3695;Lorg/joml/Matrix4f;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;ZLnet/minecraft/class_4604;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;)V"},
      at = {@At(
   value = "INVOKE_STRING",
   target = "Lnet/minecraft/class_3695;method_15405(Ljava/lang/String;)V",
   args = {"ldc=submitEntities"}
)}
   )
   private void beforeEntitySubmission(CallbackInfo ci) {
      ((WorldRenderEvents.BeforeEntities)WorldRenderEvents.BEFORE_ENTITIES.invoker()).beforeEntities(this.renderContext);
   }

   @WrapOperation(
      method = {"method_62214(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lnet/minecraft/class_11658;Lnet/minecraft/class_3695;Lorg/joml/Matrix4f;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;ZLnet/minecraft/class_4604;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;)V"},
      slice = {@Slice(
   from = @At(
   value = "INVOKE_STRING",
   target = "Lnet/minecraft/class_3695;method_15405(Ljava/lang/String;)V",
   args = {"ldc=submitEntities"}
)
)},
      at = {@At(
   value = "INVOKE",
   target = "Lnet/minecraft/class_4618;method_23285()V"
)}
   )
   private void afterEntityRender(class_4618 instance, Operation<Void> original) {
      original.call(new Object[]{instance});
      ((WorldRenderEvents.AfterEntities)WorldRenderEvents.AFTER_ENTITIES.invoker()).afterEntities(this.renderContext);
   }

   @Inject(
      method = {"method_62214(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lnet/minecraft/class_11658;Lnet/minecraft/class_3695;Lorg/joml/Matrix4f;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;ZLnet/minecraft/class_4604;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;)V"},
      at = {@At(
   value = "INVOKE",
   target = "Lnet/minecraft/class_863;method_23099(Lnet/minecraft/class_4587;Lnet/minecraft/class_4604;Lnet/minecraft/class_4597$class_4598;DDDZ)V"
)}
   )
   private void beforeDebugRender(CallbackInfo ci) {
      ((WorldRenderEvents.DebugRender)WorldRenderEvents.BEFORE_DEBUG_RENDER.invoker()).beforeDebugRender(this.renderContext);
   }

   @Inject(
      method = {"method_62214(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lnet/minecraft/class_11658;Lnet/minecraft/class_3695;Lorg/joml/Matrix4f;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;ZLnet/minecraft/class_4604;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;)V"},
      at = {@At(
   value = "INVOKE_STRING",
   target = "Lnet/minecraft/class_3695;method_15396(Ljava/lang/String;)V",
   args = {"ldc=translucent"}
)}
   )
   private void beforeTranslucentRender(CallbackInfo ci) {
      ((WorldRenderEvents.BeforeTranslucent)WorldRenderEvents.BEFORE_TRANSLUCENT.invoker()).beforeTranslucent(this.renderContext);
   }

   @Inject(
      method = {"method_62210(Lnet/minecraft/class_4597$class_4598;Lnet/minecraft/class_4587;ZLnet/minecraft/class_11658;)V"},
      at = {@At(
   value = "FIELD",
   target = "Lnet/minecraft/class_12075;field_63078:Lnet/minecraft/class_243;"
)},
      cancellable = true
   )
   private void beforeDrawBlockOutline(class_4597.class_4598 consumers, class_4587 matrices, boolean bl, class_11658 worldRenderState, CallbackInfo ci) {
      if (!((WorldRenderEvents.BeforeBlockOutline)WorldRenderEvents.BEFORE_BLOCK_OUTLINE.invoker()).beforeBlockOutline(this.renderContext, this.renderContext.worldState().field_63083)) {
         consumers.method_37104();
         ci.cancel();
      }

   }

   @Inject(
      method = {"method_62214(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lnet/minecraft/class_11658;Lnet/minecraft/class_3695;Lorg/joml/Matrix4f;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;ZLnet/minecraft/class_4604;Lnet/minecraft/class_9925;Lnet/minecraft/class_9925;)V"},
      at = {@At(
   value = "INVOKE:LAST",
   target = "Lnet/minecraft/class_4597$class_4598;method_22993()V"
)}
   )
   private void endMainRender(CallbackInfo ci) {
      ((WorldRenderEvents.EndMain)WorldRenderEvents.END_MAIN.invoker()).endMain(this.renderContext);
   }

   @Inject(
      method = {"method_3279()V"},
      at = {@At("HEAD")}
   )
   private void onReload(CallbackInfo ci) {
      ((InvalidateRenderStateCallback)InvalidateRenderStateCallback.EVENT.invoker()).onInvalidate();
   }
}
