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

import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.mojang.blaze3d.pipeline.BlendFunction;
import com.mojang.blaze3d.pipeline.RenderPipeline;
import com.mojang.blaze3d.platform.DepthTestFunction;
import com.mojang.blaze3d.platform.LogicOp;
import com.mojang.blaze3d.platform.PolygonMode;
import com.mojang.blaze3d.vertex.VertexFormat;
import java.util.List;
import java.util.Optional;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.rendering.v1.FabricRenderPipeline;
import net.fabricmc.fabric.impl.client.rendering.FabricRenderPipelineImpl;
import net.fabricmc.fabric.impl.client.rendering.FabricRenderPipelineInternals;
import net.minecraft.class_10149;
import net.minecraft.class_2960;
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.callback.CallbackInfo;

@Environment(EnvType.CLIENT)
@Mixin({RenderPipeline.Builder.class})
class RenderPipelineBuilderMixin implements FabricRenderPipeline.Builder {
   @Unique
   private Optional<Boolean> usePipelineDrawModeForGui = Optional.empty();

   public RenderPipeline.Builder withUsePipelineDrawModeForGui(boolean usePipelineDrawMode) {
      this.usePipelineDrawModeForGui = Optional.of(usePipelineDrawMode);
      return (RenderPipeline.Builder)this;
   }

   public RenderPipeline.Builder withoutUsePipelineDrawModeForGui() {
      this.usePipelineDrawModeForGui = Optional.empty();
      return (RenderPipeline.Builder)this;
   }

   @Inject(
      method = {"withSnippet(Lcom/mojang/blaze3d/pipeline/RenderPipeline$Snippet;)V"},
      at = {@At("TAIL")}
   )
   private void copyUsePipelineDrawModeForGuiFromSnippet(RenderPipeline.Snippet snippet, CallbackInfo ci) {
      snippet.usePipelineDrawModeForGui().ifPresent((value) -> this.usePipelineDrawModeForGui = Optional.of(value));
   }

   @WrapOperation(
      method = {"buildSnippet()Lcom/mojang/blaze3d/pipeline/RenderPipeline$Snippet;"},
      at = {@At(
   value = "NEW",
   target = "Lcom/mojang/blaze3d/pipeline/RenderPipeline$Snippet;"
)}
   )
   private RenderPipeline.Snippet copyUsePipelineDrawModeForGuiToSnippet(Optional<class_2960> vertexShader, Optional<class_2960> fragmentShader, Optional<class_10149> shaderDefines, Optional<List<String>> samplers, Optional<List<RenderPipeline.UniformDescription>> uniforms, Optional<BlendFunction> blendFunction, Optional<DepthTestFunction> depthTestFunction, Optional<PolygonMode> polygonMode, Optional<Boolean> cull, Optional<Boolean> writeColor, Optional<Boolean> writeAlpha, Optional<Boolean> writeDepth, Optional<LogicOp> colorLogic, Optional<VertexFormat> vertexFormat, Optional<VertexFormat.class_5596> vertexFormatMode, Operation<RenderPipeline.Snippet> original) {
      return FabricRenderPipelineInternals.withSnippetUsePipelineVertexFormatForGui(() -> (RenderPipeline.Snippet)original.call(new Object[]{vertexShader, fragmentShader, shaderDefines, samplers, uniforms, blendFunction, depthTestFunction, polygonMode, cull, writeColor, writeAlpha, writeDepth, colorLogic, vertexFormat, vertexFormatMode}), this.usePipelineDrawModeForGui);
   }

   @ModifyReturnValue(
      method = {"build()Lcom/mojang/blaze3d/pipeline/RenderPipeline;"},
      at = {@At("RETURN")}
   )
   private RenderPipeline copyUsePipelineDrawModeForGuiToPipeline(RenderPipeline original) {
      ((FabricRenderPipelineImpl)original).fabric$setUsePipelineDrawModeForGuiSetter((Boolean)this.usePipelineDrawModeForGui.orElse(false));
      return original;
   }
}
