package wtf.opal.client.renderer;

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.util.Window;
import org.lwjgl.nanovg.NVGColor;
import org.lwjgl.nanovg.NVGPaint;
import org.lwjgl.nanovg.NanoVG;
import org.lwjgl.nanovg.NanoVGGL3;
import org.lwjgl.opengl.GL33C;
import wtf.opal.client.Constants;
import wtf.opal.utility.render.GLUtility;
import wtf.opal.utility.render.ScreenPosition;

@Environment(EnvType.CLIENT)
public final class NVGRenderer {
   private static final long VG = NanoVGGL3.nvgCreate(3);
   public static final NVGPaint NVG_PAINT = NVGPaint.create();
   public static final NVGPaint BLUR_PAINT = NVGPaint.create();
   public static final NVGPaint GLOW_PAINT = NVGPaint.create();
   public static final NVGColor NVG_COLOR_1 = NVGColor.create();
   public static final NVGColor NVG_COLOR_2 = NVGColor.create();
   private static boolean frameStarted;
   public static float globalAlpha = 1.0F;
   private static final List<ScreenPosition> scissors = new ArrayList<>();
   private static int scissorDepth;
   private static int blurPaintImageHandle;

   public static boolean beginFrame() {
      Window var0 = Constants.mc.getWindow();
      float var1 = (float)var0.getScaleFactor();
      if (!frameStarted) {
         GLUtility.setup();
         GLUtility.push();
         NanoVG.nvgBeginFrame(VG, (float)var0.getFramebufferWidth() / var1, (float)var0.getFramebufferHeight() / var1, var1);
         if (!scissors.isEmpty()) {
            useCurrentScissors();
         }

         frameStarted = true;
         return true;
      } else {
         return false;
      }
   }

   public static void endFrameAndReset(boolean createRenderPass) {
      endFrame(createRenderPass);
      clearScissors();
   }

   public static void endFrame(boolean createRenderPass) {
      if (frameStarted) {
         NanoVG.nvgEndFrame(VG);
         GLUtility.pop();
         GL33C.glViewport(0, 0, Constants.mc.getWindow().getFramebufferWidth(), Constants.mc.getWindow().getFramebufferHeight());
         frameStarted = false;
      }
   }

   public static void clearScissors() {
      scissorDepth = 0;
   }

   public static void globalAlpha(float alpha) {
      globalAlpha = alpha;
      NanoVG.nvgGlobalAlpha(VG, alpha);
   }

   public static void rect(float x, float y, float width, float height, int color) {
      applyColor(color, NVG_COLOR_1);
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillColor(VG, NVG_COLOR_1);
      NanoVG.nvgRect(VG, x, y, width, height);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
   }

   public static void rect(float x, float y, float width, float height, NVGPaint nvgPaint) {
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillPaint(VG, nvgPaint);
      NanoVG.nvgRect(VG, x, y, width, height);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
   }

   public static void scale(float factor, float x, float y, float width, float height, Runnable content) {
      float var6 = x + width / 2.0F;
      float var7 = y + height / 2.0F;
      NanoVG.nvgSave(VG);
      NanoVG.nvgTranslate(VG, var6, var7);
      NanoVG.nvgScale(VG, factor, factor);
      NanoVG.nvgTranslate(VG, -var6, -var7);
      content.run();
      NanoVG.nvgRestore(VG);
   }

   public static void rectStroke(float x, float y, float width, float height, float strokeThickness, int color, int strokeColor) {
      rect(x - strokeThickness, y - strokeThickness, width + strokeThickness * 2.0F, height + strokeThickness * 2.0F, strokeColor);
      rect(x, y, width, height, color);
   }

   public static void rotate(double degrees, float x, float y, float width, float height, Runnable content) {
      float var7 = x + width / 2.0F;
      float var8 = y + height / 2.0F;
      NanoVG.nvgSave(VG);
      NanoVG.nvgTranslate(VG, var7, var8);
      NanoVG.nvgRotate(VG, (float)Math.toRadians(degrees));
      content.run();
      NanoVG.nvgRestore(VG);
   }

   public static void rectOutlineStroke(
      float x, float y, float width, float height, float outlineThickness, float strokeThickness, int outlineColor, int strokeColor
   ) {
      rectOutline(x - outlineThickness, y - outlineThickness, width + outlineThickness * 2.0F, height + outlineThickness * 2.0F, strokeThickness, strokeColor);
      rectOutline(x, y, width, height, outlineThickness, outlineColor);
   }

   public static void rectOutline(float x, float y, float width, float height, float thickness, int color) {
      applyColor(color, NVG_COLOR_1);
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillColor(VG, NVG_COLOR_1);
      NanoVG.nvgRect(VG, x, y, width, thickness);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillColor(VG, NVG_COLOR_1);
      NanoVG.nvgRect(VG, x + width - thickness, y + thickness, thickness, height - thickness);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillColor(VG, NVG_COLOR_1);
      NanoVG.nvgRect(VG, x, y + height - thickness, width - thickness, thickness);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillColor(VG, NVG_COLOR_1);
      NanoVG.nvgRect(VG, x, y + thickness, thickness, height - thickness);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
   }

   public static void rainbowRect(float x, float y, float width, float height) {
      for (float var4 = y; var4 < y + height; var4 += 0.5F) {
         float var5 = (var4 - y) / height;
         int var6 = Color.HSBtoRGB(var5, 1.0F, 1.0F);
         float var7 = Math.min(0.5F, y + height - var4);
         NanoVG.nvgShapeAntiAlias(VG, false);
         rect(x, var4, width, var7, var6);
         NanoVG.nvgShapeAntiAlias(VG, true);
      }
   }

   public static void roundedRectOutline(float x, float y, float width, float height, float radius, float thickness, int color) {
      applyColor(color, NVG_COLOR_1);
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgStrokeColor(VG, NVG_COLOR_1);
      NanoVG.nvgStrokeWidth(VG, thickness);
      NanoVG.nvgRoundedRect(VG, x, y, width, height, radius);
      NanoVG.nvgStroke(VG);
      NanoVG.nvgClosePath(VG);
   }

   public static void scissor(float x, float y, float width, float height, Runnable content) {
      while (scissors.size() <= scissorDepth) {
         scissors.add(new ScreenPosition());
      }

      ScreenPosition var5 = scissors.get(scissorDepth++);
      var5.setDimensions(x, y, width, height);
      NanoVG.nvgIntersectScissor(VG, x, y, width, height);

      try {
         content.run();
      } finally {
         NanoVG.nvgResetScissor(VG);
         scissorDepth = Math.max(0, scissorDepth - 1);
         useCurrentScissors();
      }
   }

   private static void useCurrentScissors() {
      for (int var0 = 0; var0 < scissorDepth; var0++) {
         ScreenPosition var1 = scissors.get(var0);
         NanoVG.nvgIntersectScissor(VG, var1.getX(), var1.getY(), var1.getWidth(), var1.getHeight());
      }
   }

   public static void roundedRect(float x, float y, float width, float height, float radius, int color) {
      applyColor(color, NVG_COLOR_1);
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillColor(VG, NVG_COLOR_1);
      NanoVG.nvgRoundedRect(VG, x, y, width, height, radius);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
   }

   public static void roundedRectGradient(float x, float y, float width, float height, float radius, int color1, int color2, float angleDegrees) {
      applyColor(color1, NVG_COLOR_1);
      applyColor(color2, NVG_COLOR_2);
      float var8 = (float)Math.toRadians((double)angleDegrees);
      float var9 = (float)Math.cos((double)var8);
      float var10 = (float)Math.sin((double)var8);
      NanoVG.nvgLinearGradient(
         VG,
         x + width * 0.5F - var9 * width * 0.5F,
         y + height * 0.5F - var10 * height * 0.5F,
         x + width * 0.5F + var9 * width * 0.5F,
         y + height * 0.5F + var10 * height * 0.5F,
         NVG_COLOR_1,
         NVG_COLOR_2,
         NVG_PAINT
      );
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillPaint(VG, NVG_PAINT);
      NanoVG.nvgRoundedRect(VG, x, y, width, height, radius);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
   }

   public static void rectGradient(float x, float y, float width, float height, int color1, int color2, float angleDegrees) {
      applyColor(color1, NVG_COLOR_1);
      applyColor(color2, NVG_COLOR_2);
      float var7 = (float)Math.toRadians((double)angleDegrees);
      float var8 = (float)Math.cos((double)var7);
      float var9 = (float)Math.sin((double)var7);
      NanoVG.nvgLinearGradient(
         VG,
         x + width * 0.5F - var8 * width * 0.5F,
         y + height * 0.5F - var9 * height * 0.5F,
         x + width * 0.5F + var8 * width * 0.5F,
         y + height * 0.5F + var9 * height * 0.5F,
         NVG_COLOR_1,
         NVG_COLOR_2,
         NVG_PAINT
      );
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillPaint(VG, NVG_PAINT);
      NanoVG.nvgRect(VG, x, y, width, height);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
   }

   public static void roundedRect(float x, float y, float width, float height, float radius, NVGPaint nvgPaint) {
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillPaint(VG, nvgPaint);
      NanoVG.nvgRoundedRect(VG, x, y, width, height, radius);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
   }

   public static void roundedRectVarying(
      float x, float y, float width, float height, float radiusTopLeft, float radiusTopRight, float radiusBottomRight, float radiusBottomLeft, int color
   ) {
      applyColor(color, NVG_COLOR_1);
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillColor(VG, NVG_COLOR_1);
      NanoVG.nvgRoundedRectVarying(VG, x, y, width, height, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
   }

   public static void roundedRectVaryingGradient(
      float x,
      float y,
      float width,
      float height,
      float radiusTopLeft,
      float radiusTopRight,
      float radiusBottomRight,
      float radiusBottomLeft,
      int color1,
      int color2,
      float angleDegrees
   ) {
      applyColor(color1, NVG_COLOR_1);
      applyColor(color2, NVG_COLOR_2);
      float var11 = (float)Math.toRadians((double)angleDegrees);
      float var12 = (float)Math.cos((double)var11);
      float var13 = (float)Math.sin((double)var11);
      NanoVG.nvgLinearGradient(
         VG,
         x + width * 0.5F - var12 * width * 0.5F,
         y + height * 0.5F - var13 * height * 0.5F,
         x + width * 0.5F + var12 * width * 0.5F,
         y + height * 0.5F + var13 * height * 0.5F,
         NVG_COLOR_1,
         NVG_COLOR_2,
         NVG_PAINT
      );
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillPaint(VG, NVG_PAINT);
      NanoVG.nvgRoundedRectVarying(VG, x, y, width, height, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
   }

   public static void roundedRectVarying(
      float x,
      float y,
      float width,
      float height,
      float radiusTopLeft,
      float radiusTopRight,
      float radiusBottomRight,
      float radiusBottomLeft,
      NVGPaint nvgPaint
   ) {
      NanoVG.nvgBeginPath(VG);
      NanoVG.nvgFillPaint(VG, nvgPaint);
      NanoVG.nvgRoundedRectVarying(VG, x, y, width, height, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft);
      NanoVG.nvgFill(VG);
      NanoVG.nvgClosePath(VG);
   }

   public static void applyColor(int color, NVGColor nvgColor) {
      NanoVG.nvgRGBAf(
         (float)(color >> 16 & 0xFF) / 255.0F,
         (float)(color >> 8 & 0xFF) / 255.0F,
         (float)(color & 0xFF) / 255.0F,
         (float)(color >>> 24 & 0xFF) / 255.0F,
         nvgColor
      );
   }

   public static void createNVGPaintFromTex(int width, int height, int glTex, NVGPaint nvgPaint) {
      if (nvgPaint == BLUR_PAINT && blurPaintImageHandle != 0) {
         NanoVG.nvgDeleteImage(VG, blurPaintImageHandle);
         blurPaintImageHandle = 0;
      }

      int var4 = NanoVGGL3.nvglCreateImageFromHandle(VG, glTex, width, height, 65544);
      if (nvgPaint == BLUR_PAINT) {
         blurPaintImageHandle = var4;
      }

      NanoVG.nvgImagePattern(
         VG, 0.0F, 0.0F, (float)Constants.mc.getWindow().getScaledWidth(), (float)Constants.mc.getWindow().getScaledHeight(), 0.0F, var4, 1.0F, nvgPaint
      );
   }

   public static void deleteBlurPaintImage() {
      if (blurPaintImageHandle != 0) {
         NanoVG.nvgDeleteImage(VG, blurPaintImageHandle);
         blurPaintImageHandle = 0;
      }
   }

   public static long getContext() {
      return VG;
   }
}
