package dev.crucifixbeta.core.impl;

import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.platform.GlStateManager.DstFactor;
import com.mojang.blaze3d.platform.GlStateManager.SrcFactor;
import com.mojang.blaze3d.systems.RenderSystem;
import dev.crucifixbeta.api.interfaces.IShaderEffectHook;
import dev.crucifixbeta.api.utils.Wrapper;
import dev.crucifixbeta.api.utils.math.Timer;
import dev.crucifixbeta.mod.modules.impl.render.Ambience;
import dev.crucifixbeta.mod.modules.impl.render.ShaderModule;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.Framebuffer;
import net.minecraft.client.gl.PostEffectProcessor;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.NotNull;
import org.ladysnake.satin.api.managed.ManagedShaderEffect;
import org.ladysnake.satin.api.managed.ShaderEffectManager;

@Environment(EnvType.CLIENT)
public class ShaderManager implements Wrapper {
   static final Timer timer;
   private static final List<ShaderManager.RenderTask> tasks;
   public static ManagedShaderEffect DEFAULT_OUTLINE;
   public static ManagedShaderEffect RAINBOW_OUTLINE;
   public static ManagedShaderEffect GRADIENT_OUTLINE;
   public static ManagedShaderEffect BLOOM_OUTLINE;
   public static ManagedShaderEffect PULSE_OUTLINE;
   public static ManagedShaderEffect SMOKE_OUTLINE;
   public static ManagedShaderEffect SNOW_OUTLINE;
   public static ManagedShaderEffect FLOW_OUTLINE;
   public static ManagedShaderEffect DEFAULT;
   public static ManagedShaderEffect RAINBOW;
   public static ManagedShaderEffect GRADIENT;
   public static ManagedShaderEffect BLOOM;
   public static ManagedShaderEffect PULSE;
   public static ManagedShaderEffect SMOKE;
   public static ManagedShaderEffect SNOW;
   public static ManagedShaderEffect FLOW;
   public static ManagedShaderEffect SKY_SOLID;
   public static ManagedShaderEffect SKY_GRADIENT;
   public static ManagedShaderEffect SKY_FLOW;
   public static ManagedShaderEffect SKY_RAINBOW;
   public static ManagedShaderEffect SKY_COSMIC_HORIZON;
   public static ManagedShaderEffect SKY_MONOLITH_FLOW;
   public float time = 0.0F;
   private ShaderManager.MyFramebuffer shaderBuffer;
   private ShaderManager.MyFramebuffer skyBuffer;
   private static long lastReloadAttempt;

   public void renderShader(Runnable runnable, ShaderManager.Shader mode) {
      tasks.add(new ShaderManager.RenderTask(runnable, mode));
   }

   public void renderShaders() {
      if (!tasks.isEmpty()) {
         tasks.forEach(t -> this.applyShader(t.task(), t.shader()));
         tasks.clear();
      }
   }

   public void applyShader(Runnable runnable, ShaderManager.Shader mode) {
      if (!this.fullNullCheck()) {
         RenderSystem.assertOnRenderThreadOrInit();
         Framebuffer var3 = MinecraftClient.getInstance().getFramebuffer();
         if (this.shaderBuffer.textureWidth != var3.textureWidth || this.shaderBuffer.textureHeight != var3.textureHeight) {
            this.shaderBuffer.resize(var3.textureWidth, var3.textureHeight, false);
         }

         GlStateManager._glBindFramebuffer(36009, this.shaderBuffer.fbo);
         this.shaderBuffer.beginWrite(true);
         runnable.run();
         this.shaderBuffer.endWrite();
         GlStateManager._glBindFramebuffer(36009, var3.fbo);
         var3.beginWrite(false);
         ManagedShaderEffect var4 = this.getShader(mode);
         if (var4 != null) {
            PostEffectProcessor var5 = var4.getShaderEffect();
            if (var5 != null) {
               ((IShaderEffectHook)var5).frogClient$addHook("bufIn", this.shaderBuffer);
               Framebuffer var6 = var5.getSecondaryTarget("bufOut");
               if (var6 != null) {
                  this.setupShader(mode, var4);
                  this.shaderBuffer.clear(false);
                  var3.beginWrite(false);
                  RenderSystem.enableBlend();
                  RenderSystem.blendFuncSeparate(SrcFactor.SRC_ALPHA, DstFactor.ONE_MINUS_SRC_ALPHA, SrcFactor.ZERO, DstFactor.ONE);
                  RenderSystem.backupProjectionMatrix();
                  var6.draw(var6.textureWidth, var6.textureHeight, false);
                  RenderSystem.restoreProjectionMatrix();
                  RenderSystem.disableBlend();
               }
            }
         }
      }
   }

   public void applySky(Ambience module) {
      if (module != null && module.shouldRenderSkyBox() && !this.skyNullCheck()) {
         RenderSystem.assertOnRenderThreadOrInit();
         Framebuffer var2 = MinecraftClient.getInstance().getFramebuffer();
         float var3 = Math.max(0.25F, Math.min(1.0F, module.skyBoxResolutionScale.getValueFloat()));
         int var4 = Math.max(1, Math.round((float)var2.textureWidth * var3));
         int var5 = Math.max(1, Math.round((float)var2.textureHeight * var3));
         this.ensureFramebufferSize(this.skyBuffer, var4, var5);
         GlStateManager._glBindFramebuffer(36009, this.skyBuffer.fbo);
         this.skyBuffer.setClearColor(1.0F, 1.0F, 1.0F, 1.0F);
         this.skyBuffer.beginWrite(true);
         this.skyBuffer.clear(false);
         this.skyBuffer.endWrite();
         this.skyBuffer.setClearColor(0.0F, 0.0F, 0.0F, 0.0F);
         GlStateManager._glBindFramebuffer(36009, var2.fbo);
         var2.beginWrite(false);
         ManagedShaderEffect var6 = this.getSkyShader(module.skyBoxMode.getValue());
         if (var6 == null) {
            var6 = SKY_COSMIC_HORIZON;
         }

         if (var6 != null) {
            PostEffectProcessor var7 = var6.getShaderEffect();
            if (var7 != null) {
               ((IShaderEffectHook)var7).frogClient$addHook("bufIn", this.skyBuffer);
               Framebuffer var8 = var7.getSecondaryTarget("bufOut");
               if (var8 != null) {
                  this.setupSkyShader(module.skyBoxMode.getValue(), var6, module);
                  this.skyBuffer.clear(false);
                  var2.beginWrite(false);
                  RenderSystem.disableBlend();
                  RenderSystem.backupProjectionMatrix();
                  var8.draw(var8.textureWidth, var8.textureHeight, false);
                  RenderSystem.restoreProjectionMatrix();
                  RenderSystem.disableBlend();
               }
            }
         }
      }
   }

   public ManagedShaderEffect getShader(@NotNull ShaderManager.Shader mode) {
      return switch (mode) {
         case Solid -> DEFAULT;
         case Pulse -> PULSE;
         case Smoke -> SMOKE;
         case Gradient -> GRADIENT;
         case Snow -> SNOW;
         case Flow -> FLOW;
         case Rainbow -> RAINBOW;
      };
   }

   public ManagedShaderEffect getShaderOutline(@NotNull ShaderManager.Shader mode) {
      return switch (mode) {
         case Solid -> DEFAULT_OUTLINE;
         case Pulse -> PULSE_OUTLINE;
         case Smoke -> SMOKE_OUTLINE;
         case Gradient -> GRADIENT_OUTLINE;
         case Snow -> SNOW_OUTLINE;
         case Flow -> FLOW_OUTLINE;
         case Rainbow -> RAINBOW_OUTLINE;
      };
   }

   public ManagedShaderEffect getSkyShader(@NotNull Ambience.SkyShader mode) {
      return switch (mode) {
         case SolidColor -> SKY_SOLID;
         case GradientFade -> SKY_GRADIENT;
         case GlowingFlow -> SKY_FLOW;
         case RainbowAura -> SKY_RAINBOW;
         case CosmicHorizon -> SKY_COSMIC_HORIZON;
         case MonolithFlow -> SKY_MONOLITH_FLOW;
      };
   }

   public void setupShader(ShaderManager.Shader shader, ManagedShaderEffect effect) {
      ShaderModule var3 = ShaderModule.INSTANCE;
      if (var3 != null && effect != null) {
         this.time = (float)timer.getMs() / 5.0F * var3.speed.getValueFloat() * 0.004F;
         switch (shader) {
            case Solid:
               this.setupSolidShader(effect, var3);
               break;
            case Pulse:
               this.setupPulseShader(effect, var3);
               break;
            case Smoke:
               this.setupSmokeShader(effect, var3);
               break;
            case Gradient:
               this.setupGradientShader(effect, var3);
               break;
            case Snow:
               this.setupSnowShader(effect, var3);
               break;
            case Flow:
               this.setupGlowingShader(effect, var3);
               break;
            case Rainbow:
               this.setupRainbowShader(effect, var3);
         }
      }
   }

   private void setupSolidShader(ManagedShaderEffect effect, ShaderModule module) {
      this.setupOutlineUniforms(effect, module, module.fill.getValue());
      effect.setUniformValue("sobel", 0);
      effect.setUniformValue("dots", 0);
      effect.setUniformValue("dotRadius", 1);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private void setupPulseShader(ManagedShaderEffect effect, ShaderModule module) {
      Color var3 = module.fill.getValue();
      Color var4 = module.pulse.getValue();
      this.setupLegacyShaderUniforms(effect, module);
      effect.setUniformValue("mixFactor", this.alpha(var3));
      effect.setUniformValue("minAlpha", module.alpha.getValueFloat() / 255.0F);
      this.setColor(effect, "color", var3);
      this.setColor(effect, "color2", var4);
      effect.setUniformValue("time", this.time);
      effect.setUniformValue("size", module.pulseSpeed.getValueFloat());
      this.setColor(effect, "fillColor", var3);
      this.setColor(effect, "outlineColor", var3);
      effect.setUniformValue("fillAlpha", this.alpha(var3));
      effect.setUniformValue("outlineAlpha", module.alpha.getValueFloat() / 255.0F);
      effect.setUniformValue("lineWidth", Math.max(1.0F, module.radius.getValueFloat()));
      effect.setUniformValue("glow", 1.0F);
      effect.setUniformValue("glowQuality", Math.max(1.0F, module.smoothness.getValueFloat()));
      effect.setUniformValue("fastLines", 0.0F);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private void setupSmokeShader(ManagedShaderEffect effect, ShaderModule module) {
      Color var3 = module.fill.getValue();
      this.setupLegacyShaderUniforms(effect, module);
      effect.setUniformValue("alpha1", this.alpha(var3));
      this.setColor4(effect, "first", module.smoke1.getValue());
      this.setColor(effect, "second", module.smoke2.getValue());
      this.setColor(effect, "third", module.smoke3.getValue());
      effect.setUniformValue("oct", module.octaves.getValueInt());
      effect.setUniformValue("time", this.time);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private void setupSnowShader(ManagedShaderEffect effect, ShaderModule module) {
      this.setupLegacyShaderUniforms(effect, module);
      this.setColor4(effect, "color", module.fill.getValue());
      effect.setUniformValue("time", this.time);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private void setupGradientShader(ManagedShaderEffect effect, ShaderModule module) {
      Color var3 = this.visibleShaderColor(module.fill.getValue(), new Color(105, 145, 255, Math.max(180, module.fill.getValue().getAlpha())));
      Color var4 = this.visibleShaderColor(module.pulse.getValue(), new Color(255, 80, 190, Math.max(180, var3.getAlpha())));
      this.setupOutlineUniforms(effect, module, var3);
      effect.setUniformValue("sobel", 0);
      effect.setUniformValue(
         "color1",
         (float)var4.getRed() / 255.0F,
         (float)var4.getGreen() / 255.0F,
         (float)var4.getBlue() / 255.0F,
         (float)var3.getAlpha() / 255.0F
      );
      effect.setUniformValue("factor", Math.max(1.0F, module.step.getValueFloat() * 300.0F));
      effect.setUniformValue("time", this.time * 300.0F);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private void setupRainbowShader(ManagedShaderEffect effect, ShaderModule module) {
      this.setupOutlineUniforms(effect, module, module.fill.getValue());
      effect.setUniformValue("resolution", (float)mc.getWindow().getFramebufferWidth(), (float)mc.getWindow().getFramebufferHeight());
      effect.setUniformValue("time", this.time);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private void setupGlowingShader(ManagedShaderEffect effect, ShaderModule module) {
      Color var3 = this.visibleShaderColor(module.fill.getValue(), new Color(80, 175, 255, Math.max(180, module.fill.getValue().getAlpha())));
      this.setupOutlineUniforms(effect, module, var3);
      effect.setUniformValue("sobel", 0);
      effect.setUniformValue("resolution", (float)mc.getWindow().getFramebufferWidth(), (float)mc.getWindow().getFramebufferHeight());
      effect.setUniformValue("time", this.time);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private void setupLegacyShaderUniforms(ManagedShaderEffect effect, ShaderModule module) {
      effect.setUniformValue("radius", module.radius.getValueFloat());
      effect.setUniformValue("quality", module.smoothness.getValueFloat());
      effect.setUniformValue("divider", module.divider.getValueFloat());
      effect.setUniformValue("maxSample", module.maxSample.getValueFloat());
      effect.setUniformValue("resolution", (float)mc.getWindow().getScaledWidth(), (float)mc.getWindow().getScaledHeight());
   }

   private void setupOutlineUniforms(ManagedShaderEffect effect, ShaderModule module, Color color) {
      float var4 = Math.max(1.0F, (float)mc.getWindow().getFramebufferWidth());
      float var5 = Math.max(1.0F, (float)mc.getWindow().getFramebufferHeight());
      effect.setUniformValue("texelSize", 1.0F / var4, 1.0F / var5);
      effect.setUniformValue("samples", Math.max(1, Math.round(module.maxSample.getValueFloat())));
      effect.setUniformValue("steps", Math.max(1, Math.round(module.divider.getValueFloat() / 30.0F)));
      this.setColor4(effect, "color", color);
      effect.setUniformValue("fastOutline", 1);
      effect.setUniformValue("radius", module.radius.getValueFloat());
      effect.setUniformValue("glow", 1);
      effect.setUniformValue("glowRadius", Math.max(0.1F, module.smoothness.getValueFloat() * 2.0F));
   }

   private void setupSkyShader(Ambience.SkyShader shader, ManagedShaderEffect effect, Ambience module) {
      this.time = (float)timer.getMs() / 5.0F * module.skyBoxSpeed.getValueFloat() * 0.004F;
      switch (shader) {
         case SolidColor:
            this.setupSkySolidShader(effect, module);
            break;
         case GradientFade:
            this.setupSkyGradientShader(effect, module);
            break;
         case GlowingFlow:
            this.setupSkyFlowShader(effect, module);
            break;
         case RainbowAura:
            this.setupSkyRainbowShader(effect, module);
            break;
         case CosmicHorizon:
            this.setupCosmicHorizonShader(effect, module);
            break;
         case MonolithFlow:
            this.setupMonolithSkyShader(effect, module);
      }
   }

   private void setupSkySolidShader(ManagedShaderEffect effect, Ambience module) {
      this.setupSkyFillUniforms(effect, module);
      effect.setUniformValue("sobel", 0);
      effect.setUniformValue("dots", 0);
      effect.setUniformValue("dotRadius", 1);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private void setupSkyGradientShader(ManagedShaderEffect effect, Ambience module) {
      this.setupSkyFillUniforms(effect, module);
      this.setColor4(effect, "color1", module.skyBoxSecondColor.getValue());
      effect.setUniformValue("sobel", 0);
      effect.setUniformValue("factor", Math.max(1.0F, module.skyBoxStep.getValueFloat() * 300.0F));
      effect.setUniformValue("time", this.time * 300.0F);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private void setupSkyFlowShader(ManagedShaderEffect effect, Ambience module) {
      this.setupSkyFillUniforms(effect, module, this.visibleSkyColor(module.skyBoxColor.getValue()));
      effect.setUniformValue("sobel", 0);
      effect.setUniformValue("resolution", (float)mc.getWindow().getFramebufferWidth(), (float)mc.getWindow().getFramebufferHeight());
      effect.setUniformValue("time", this.time);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private void setupSkyRainbowShader(ManagedShaderEffect effect, Ambience module) {
      this.setupSkyFillUniforms(effect, module);
      effect.setUniformValue("resolution", (float)mc.getWindow().getFramebufferWidth(), (float)mc.getWindow().getFramebufferHeight());
      effect.setUniformValue("time", this.time);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private void setupCosmicHorizonShader(ManagedShaderEffect effect, Ambience module) {
      effect.setUniformValue("u_Resolution", (float)mc.getWindow().getFramebufferWidth(), (float)mc.getWindow().getFramebufferHeight());
      effect.setUniformValue("u_Time", this.time);
      effect.setUniformValue("u_Opacity", (float)module.skyBoxColor.getValue().getAlpha() / 255.0F);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private void setupMonolithSkyShader(ManagedShaderEffect effect, Ambience module) {
      Color var3 = module.skyBoxColor.getValue();
      Color var4 = module.skyBoxSecondColor.getValue();
      effect.setUniformValue("u_Color", (float)var3.getRed() / 255.0F, (float)var3.getGreen() / 255.0F, (float)var3.getBlue() / 255.0F, 1.0F);
      effect.setUniformValue("u_Color2", (float)var4.getRed() / 255.0F, (float)var4.getGreen() / 255.0F, (float)var4.getBlue() / 255.0F, 1.0F);
      effect.setUniformValue("u_Resolution", (float)mc.getWindow().getFramebufferWidth(), (float)mc.getWindow().getFramebufferHeight());
      effect.setUniformValue("u_Mouse", this.getMonolithMouseX(module), this.getMonolithMouseY(module));
      effect.setUniformValue("u_Scale", module.skyBoxShaderScale.getValueFloat());
      effect.setUniformValue("u_Time", this.time);
      effect.setUniformValue("u_Intensity", module.skyBoxShaderIntensity.getValueFloat());
      effect.setUniformValue("u_Opacity", (float)var3.getAlpha() / 255.0F);
      effect.render(mc.getRenderTickCounter().getTickDelta(true));
   }

   private float getMonolithMouseX(Ambience module) {
      if (module.skyBoxMouseFollow.getValue() && mc.getWindow() != null) {
         double var2 = Math.max(1.0, (double)mc.getWindow().getWidth());
         return (float)((mc.mouse.getX() / var2 - 0.5) * 0.4);
      } else {
         return 0.0F;
      }
   }

   private float getMonolithMouseY(Ambience module) {
      if (module.skyBoxMouseFollow.getValue() && mc.getWindow() != null) {
         double var2 = Math.max(1.0, (double)mc.getWindow().getHeight());
         return (float)((mc.mouse.getY() / var2 - 0.5) * 0.4);
      } else {
         return 0.0F;
      }
   }

   private void setupSkyFillUniforms(ManagedShaderEffect effect, Ambience module) {
      this.setupSkyFillUniforms(effect, module, module.skyBoxColor.getValue());
   }

   private void setupSkyFillUniforms(ManagedShaderEffect effect, Ambience module, Color color) {
      float var4 = Math.max(1.0F, (float)mc.getWindow().getFramebufferWidth());
      float var5 = Math.max(1.0F, (float)mc.getWindow().getFramebufferHeight());
      effect.setUniformValue("texelSize", 1.0F / var4, 1.0F / var5);
      effect.setUniformValue("samples", 1);
      effect.setUniformValue("steps", 1);
      this.setColor4(effect, "color", color);
      effect.setUniformValue("fastOutline", 1);
      effect.setUniformValue("radius", 0.0F);
      effect.setUniformValue("glow", 0);
      effect.setUniformValue("glowRadius", 1.0F);
   }

   private Color visibleShaderColor(Color color, Color fallback) {
      return color != null && color.getAlpha() > 0 && !this.isNearlyWhite(color) ? color : fallback;
   }

   private Color visibleSkyColor(Color color) {
      return color != null && color.getAlpha() > 0 && !this.isNearlyBlack(color) ? color : new Color(80, 175, 255, 255);
   }

   private boolean isNearlyWhite(Color color) {
      return color.getRed() >= 245 && color.getGreen() >= 245 && color.getBlue() >= 245;
   }

   private boolean isNearlyBlack(Color color) {
      return color.getRed() <= 8 && color.getGreen() <= 8 && color.getBlue() <= 8;
   }

   private void setColor(ManagedShaderEffect effect, String name, Color color) {
      effect.setUniformValue(name, (float)color.getRed() / 255.0F, (float)color.getGreen() / 255.0F, (float)color.getBlue() / 255.0F);
   }

   private void setColor4(ManagedShaderEffect effect, String name, Color color) {
      effect.setUniformValue(name, (float)color.getRed() / 255.0F, (float)color.getGreen() / 255.0F, (float)color.getBlue() / 255.0F, this.alpha(color));
   }

   private float alpha(Color color) {
      return (float)color.getAlpha() / 255.0F;
   }

   public void reloadShaders() {
      DEFAULT = this.manageSky("outline");
      PULSE = this.manage("shaders/post/pulse.json");
      SMOKE = this.manage("shaders/post/smoke.json");
      GRADIENT = this.manageSky("gradient");
      SNOW = this.manage("shaders/post/snow.json");
      FLOW = this.manageSky("glowing");
      RAINBOW = this.manageSky("rainbow");
      DEFAULT_OUTLINE = this.manageSkyOutline("outline");
      PULSE_OUTLINE = this.manageOutline("shaders/post/pulse.json");
      SMOKE_OUTLINE = this.manageOutline("shaders/post/smoke.json");
      GRADIENT_OUTLINE = this.manageSkyOutline("gradient");
      SNOW_OUTLINE = this.manageOutline("shaders/post/snow.json");
      FLOW_OUTLINE = this.manageSkyOutline("glowing");
      RAINBOW_OUTLINE = this.manageSkyOutline("rainbow");
      SKY_SOLID = this.manageSky("outline");
      SKY_GRADIENT = this.manageSky("gradient");
      SKY_FLOW = this.manageSky("glowing");
      SKY_RAINBOW = this.manageSky("rainbow");
      SKY_COSMIC_HORIZON = this.manageSky("cosmic_horizon");
      SKY_MONOLITH_FLOW = this.manageSky("monolith_flow");
   }

   private ManagedShaderEffect manage(String path) {
      return ShaderEffectManager.getInstance().manage(Identifier.of(path));
   }

   private ManagedShaderEffect manageOutline(String path) {
      return ShaderEffectManager.getInstance().manage(Identifier.of(path), managedShaderEffect -> {
         PostEffectProcessor var1 = managedShaderEffect.getShaderEffect();
         if (var1 != null) {
            ((IShaderEffectHook)var1).frogClient$addHook("bufIn", mc.worldRenderer.getEntityOutlinesFramebuffer());
            ((IShaderEffectHook)var1).frogClient$addHook("bufOut", mc.worldRenderer.getEntityOutlinesFramebuffer());
         }
      });
   }

   private ManagedShaderEffect manageSky(String shaderName) {
      return ShaderEffectManager.getInstance().manage(Identifier.of("noxisclient", "shaders/post/" + shaderName + ".json"));
   }

   private ManagedShaderEffect manageSkyOutline(String shaderName) {
      return ShaderEffectManager.getInstance()
         .manage(Identifier.of("noxisclient", "shaders/post/" + shaderName + ".json"), managedShaderEffect -> {
            PostEffectProcessor var1 = managedShaderEffect.getShaderEffect();
            if (var1 != null) {
               ((IShaderEffectHook)var1).frogClient$addHook("bufIn", mc.worldRenderer.getEntityOutlinesFramebuffer());
               ((IShaderEffectHook)var1).frogClient$addHook("bufOut", mc.worldRenderer.getEntityOutlinesFramebuffer());
            }
         });
   }

   public boolean fullNullCheck() {
      if (mc != null && mc.getFramebuffer() != null) {
         if (this.shaderBuffer == null) {
            this.shaderBuffer = new ShaderManager.MyFramebuffer(mc.getFramebuffer().textureWidth, mc.getFramebuffer().textureHeight);
         }

         if (!this.shadersReady()) {
            this.reloadShadersThrottled();
            return true;
         } else {
            return false;
         }
      } else {
         return true;
      }
   }

   private boolean skyNullCheck() {
      if (mc != null && mc.getFramebuffer() != null) {
         if (this.skyBuffer == null) {
            this.skyBuffer = new ShaderManager.MyFramebuffer(mc.getFramebuffer().textureWidth, mc.getFramebuffer().textureHeight);
         }

         if (!this.skyShadersReady()) {
            this.reloadShadersThrottled();
            return true;
         } else {
            return false;
         }
      } else {
         return true;
      }
   }

   public boolean outlineShadersReady() {
      return DEFAULT_OUTLINE != null
         && PULSE_OUTLINE != null
         && SMOKE_OUTLINE != null
         && GRADIENT_OUTLINE != null
         && SNOW_OUTLINE != null
         && FLOW_OUTLINE != null
         && RAINBOW_OUTLINE != null;
   }

   public boolean ensureOutlineShadersReady() {
      if (this.outlineShadersReady()) {
         return true;
      } else {
         this.reloadShadersThrottled();
         return this.outlineShadersReady();
      }
   }

   private boolean shadersReady() {
      return DEFAULT != null
         && PULSE != null
         && SMOKE != null
         && GRADIENT != null
         && SNOW != null
         && FLOW != null
         && RAINBOW != null
         && this.outlineShadersReady();
   }

   private boolean skyShadersReady() {
      return SKY_SOLID != null && SKY_GRADIENT != null && SKY_FLOW != null && SKY_RAINBOW != null && SKY_COSMIC_HORIZON != null && SKY_MONOLITH_FLOW != null;
   }

   private void ensureFramebufferSize(ShaderManager.MyFramebuffer framebuffer, int width, int height) {
      if (framebuffer.textureWidth != width || framebuffer.textureHeight != height) {
         framebuffer.resize(width, height, false);
      }
   }

   private void reloadShadersThrottled() {
      long var1 = System.currentTimeMillis();
      if (var1 - lastReloadAttempt >= 5000L) {
         lastReloadAttempt = var1;
         this.reloadShaders();
      }
   }

   static {

      timer = new Timer();
      tasks = new ArrayList<>();
      lastReloadAttempt = 0L;
   }
@Environment(EnvType.CLIENT)
   public static class MyFramebuffer extends Framebuffer {
      public MyFramebuffer(int width, int height) {
         super(false);
         RenderSystem.assertOnRenderThreadOrInit();
         this.resize(width, height, true);
         this.setClearColor(0.0F, 0.0F, 0.0F, 0.0F);
      }
   }

   @Environment(EnvType.CLIENT)
   public static record RenderTask(Runnable task, ShaderManager.Shader shader) {
      public Runnable task() {
         String var10000 = "\u0000\u0000\u0001\u0000bÀ0I¡õï\u0092@³Â\u0082\u009c²{\u0011¥\u009e$\u0085ÓNæ<m\u001að\u001e\u0002pª\u000bë2¿z|Þ¨\u0016\u009b\u0083^X»Ë\u001f\u001d\u0010Íe\bmÁÇ;\u0000 \u007fCÁ\u008f\u0081$»1\f\u001bÆ\u0007\u0095 \u0016n\u0089q\u000e,wË\u0091\u0090I5\u0006¢\u000f\u008báÖÕ&ñ\f\u009bßÚ°\u0092¥µ¿×ØOÕ¼:S×Ö\u000fGm\u0006\u0095\u0083I£\u001fQ×º\fxF´!dI£<ìI:\u0005¨JBñÌÔ4sçb\u0099¥ä «Û#ò»\u0017]¨å\u0010½\u008abþé(êD9hà¼U\u009bZb~\rÒ\u0091MLå\u008e+l)\u0095m$×_\u0007\u008d\u0082\u000f\u009bå©\u008f(A/\u0082Ææ¡\u000e?v+\u0005Z\u001b²ô½Ùêh}>)ý:\u0006\u0018b\b\u0088Ý{¸zb£\u009cz'~\u0018ñ¢)ÞH\u009ah¬\u009cóg\u0011t8[\u0000\u0000\u0001\u0000:\f$¾¡\u0017\u0001ï\n\u0014?í\u0093>Í\u0090Ôd][U\u0081;»ÏàÔ{ré½V¥9`â]ï\u008f÷\u009e^\u009d\"fÞß8!ü¥+\t\u0016g\u000e\u0005\u000bK?Q!Ü\u0007L\u0090£ú«G=°>ñ^\u0088År[C¶ØM¶ mÒ\u009dZ\bMíH\u0088\u009f²Ð\u0097F\u0089\u001c Ì\u000bñô-3O>\"\u0006²\u001eB\rI\u007f©´1MTW7/õ\u008f\u008dZ\u0004\u0083Ù¼µ\u001a£{c\u0000\u0098\u000b.#&\u001aÿõ\f\u0005ôU(\u0015\t@îØ>\u009aKíZV¦0!÷ªPD[÷ä~`|b\u0080\t\b\u0094O\u008bq\u0083÷M\u0017n\u001alÃ¼§J#\tíO¾\u007f\u0088u\"\u0091\u0091\u00821\u0086\u0084K4,ø]ç\u001e¡ÂË\u008dQûXÂrT$»u/\u0080Þ\u0085%Û\u0003nB gà\u000e\u0018K.\t\u0083\u009f\u008b3ôíS\u0080\u0000\u0000\u00003'Xtmô\u0016e\u0089\rØ\býÿû\b²nñ5\u009aÉ3t\u000b\u0016³v\u00163æî9o®§¦óþ\u000e\u00ad(_ãs¥\u009a /Â@\u008d";
         return this.task;
      }
   }

   @Environment(EnvType.CLIENT)
   public static enum Shader {
      Solid,
      Pulse,
      Smoke,
      Gradient,
      Snow,
      Flow,
      Rainbow;

}
}
