package zenith.zov.utility.render.display.shader;

import com.mojang.blaze3d.systems.RenderSystem;
import lombok.Generated;
import net.minecraft.client.gl.GlUniform;
import net.minecraft.client.gl.ShaderProgramKeys;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.BufferRenderer;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.render.VertexFormat.DrawMode;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Vec2f;
import org.joml.Matrix4f;
import zenith.zov.Zenith;
import zenith.zov.client.modules.impl.render.Interface;
import zenith.zov.utility.interfaces.IWindow;
import zenith.zov.utility.math.MathUtil;
import zenith.zov.utility.render.display.Render2DUtil;
import zenith.zov.utility.render.display.base.BorderRadius;
import zenith.zov.utility.render.display.base.CustomSprite;
import zenith.zov.utility.render.display.base.Gradient;
import zenith.zov.utility.render.display.base.color.ColorRGBA;

public final class DrawUtil implements IWindow {
    public static final float DEFAULT_SMOOTHNESS = 0.8F;
    public static GlProgram rectangleProgram;
    private static GlProgram squircleProgram;
    private static GlProgram roundedTextureProgram;
    private static GlProgram squircleTextureProgram;
    private static GlProgram borderProgram;
    private static GlProgram figmaBorderProgram;
    private static GlProgram loadingProgram;
    private static GlProgram gradientRectangleProgram;
    public static BlurProgram blurProgram;
    private static final CustomRenderTarget buffer = new CustomRenderTarget(false);

    public static void initializeShaders() {
        rectangleProgram = new GlProgram(Zenith.id("rectangle/data"), VertexFormats.POSITION_COLOR);
        squircleProgram = new GlProgram(Zenith.id("squircle/data"), VertexFormats.POSITION_COLOR);
        squircleTextureProgram = new GlProgram(Zenith.id("squircle_texture/data"), VertexFormats.POSITION_TEXTURE_COLOR);
        roundedTextureProgram = new GlProgram(Zenith.id("texture/data"), VertexFormats.POSITION_TEXTURE_COLOR);
        borderProgram = new GlProgram(Zenith.id("border/data"), VertexFormats.POSITION_COLOR);
        figmaBorderProgram = new GlProgram(Zenith.id("corner/data"), VertexFormats.POSITION_COLOR);
        loadingProgram = new GlProgram(Zenith.id("loading/data"), VertexFormats.POSITION_COLOR);
        gradientRectangleProgram = new GlProgram(Zenith.id("gradient_rectangle/data"), VertexFormats.POSITION_COLOR);
        blurProgram = new BlurProgram();
        blurProgram.initShaders();
    }

    public static void updateBuffer() {
        buffer.setClearColor(0.0F, 0.0F, 0.0F, 1.0F);
        buffer.setup();
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        mc.getFramebuffer().beginRead();
        RenderSystem.setShader(ShaderProgramKeys.POSITION_TEX_COLOR);
        RenderSystem.setShaderTexture(0, mc.getFramebuffer().getColorAttachment());
        drawQuad(0.0F, 0.0F, (float)mw.getScaledWidth(), (float)mw.getScaledHeight(), true);
        mc.getFramebuffer().endRead();
        RenderSystem.disableBlend();
        mc.getFramebuffer().beginWrite(true);
        buffer.stop();
    }

    private static void drawQuad(float x, float y, float width, float height, boolean flip) {
        BufferBuilder builder = Tessellator.getInstance().begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
        int color = -1;
        float vTop = flip ? 0.0F : 1.0F;
        float vBottom = flip ? 1.0F : 0.0F;
        builder.vertex(x, y, 0.0F).texture(0.0F, vBottom).color(-1);
        builder.vertex(x, y + height, 0.0F).texture(0.0F, vTop).color(-1);
        builder.vertex(x + width, y + height, 0.0F).texture(1.0F, vTop).color(-1);
        builder.vertex(x + width, y, 0.0F).texture(1.0F, vBottom).color(-1);
        BufferRenderer.drawWithGlobalProgram(builder.end());
    }

    public static void drawLine(MatrixStack matrices, Vec2f from, Vec2f to, ColorRGBA color) {
        matrices.push();

        try {
            Matrix4f matrix4f = matrices.peek().getPositionMatrix();
            RenderSystem.enableBlend();
            RenderSystem.defaultBlendFunc();
            RenderSystem.setShader(ShaderProgramKeys.POSITION_COLOR);
            RenderSystem.lineWidth(1.0F);
            drawSetup();
            BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.DEBUG_LINE_STRIP, VertexFormats.POSITION_COLOR);
            builder.vertex(matrix4f, from.x, from.y, 0.0F).color(color.getRGB());
            builder.vertex(matrix4f, to.x, to.y, 0.0F).color(color.getRGB());
            BufferRenderer.drawWithGlobalProgram(builder.end());
            drawEnd();
        } finally {
            RenderSystem.disableBlend();
            RenderSystem.lineWidth(1.0F);
            matrices.pop();
        }
    }

    public static void drawBezier(MatrixStack matrices, Vec2f p0, Vec2f p1, Vec2f p2, Vec2f p3, ColorRGBA color, int resolution) {
        matrices.push();

        try {
            Matrix4f matrix4f = matrices.peek().getPositionMatrix();
            RenderSystem.enableBlend();
            RenderSystem.defaultBlendFunc();
            RenderSystem.setShader(ShaderProgramKeys.POSITION_COLOR);
            RenderSystem.lineWidth(1.0F);
            drawSetup();
            BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.DEBUG_LINE_STRIP, VertexFormats.POSITION_COLOR);

            for (int i = 0; i <= resolution; i++) {
                float t = (float)i / (float)resolution;
                float x = (float)MathUtil.cubicBezier((double)t, (double)p0.x, (double)p1.x, (double)p2.x, (double)p3.x);
                float y = (float)MathUtil.cubicBezier((double)t, (double)p0.y, (double)p1.y, (double)p2.y, (double)p3.y);
                builder.vertex(matrix4f, x, y, 0.0F).color(color.getRGB());
            }

            BufferRenderer.drawWithGlobalProgram(builder.end());
            drawEnd();
        } finally {
            RenderSystem.disableBlend();
            RenderSystem.lineWidth(1.0F);
            matrices.pop();
        }
    }

    private static float cubicBezier(float t, float p0, float p1, float p2, float p3) {
        float u = 1.0F - t;
        float tt = t * t;
        float uu = u * u;
        return uu * u * p0 + 3.0F * uu * t * p1 + 3.0F * u * tt * p2 + tt * t * p3;
    }

    public static void drawRect(MatrixStack matrices, float x, float y, float width, float height, ColorRGBA color) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        RenderSystem.setShader(ShaderProgramKeys.POSITION_COLOR);
        drawSetup();
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR);
        builder.vertex(matrix4f, x, y + height, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, x + width, y + height, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, x + width, y, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, x, y, 0.0F).color(color.getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        matrices.pop();
    }

    public static void drawSquircle(MatrixStack matrices, float x, float y, float width, float height, float squirt, BorderRadius borderRadius, ColorRGBA color) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        float smoothness = 0.8F;
        squircleProgram.use();
        GlUniform sizeUniform = squircleProgram.findUniform("Size");
        GlUniform radiusUniform = squircleProgram.findUniform("Radius");
        GlUniform smoothnessUniform = squircleProgram.findUniform("Smoothness");
        GlUniform cornerSmoothnessUniform = squircleProgram.findUniform("CornerSmoothness");
        if (sizeUniform != null) {
            sizeUniform.set(width, height);
        }

        if (radiusUniform != null) {
            radiusUniform.set(
                borderRadius.topLeftRadius() * squirt / 2.0F,
                borderRadius.bottomLeftRadius() * squirt / 2.0F,
                borderRadius.topRightRadius() * squirt / 2.0F,
                borderRadius.bottomRightRadius() * squirt / 2.0F
            );
        }

        if (smoothnessUniform != null) {
            smoothnessUniform.set(smoothness);
        }

        if (cornerSmoothnessUniform != null) {
            cornerSmoothnessUniform.set(squirt);
        }

        drawSetup();
        float horizontalPadding = -smoothness / 2.0F + smoothness * 2.0F;
        float verticalPadding = smoothness / 2.0F + smoothness;
        float adjustedX = x - horizontalPadding / 2.0F;
        float adjustedY = y - verticalPadding / 2.0F;
        float adjustedWidth = width + horizontalPadding;
        float adjustedHeight = height + verticalPadding;
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR);
        builder.vertex(matrix4f, adjustedX, adjustedY, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX, adjustedY + adjustedHeight, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY + adjustedHeight, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY, 0.0F).color(color.getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        matrices.pop();
    }

    public static void drawLoadingRect(
        MatrixStack matrices, float x, float y, float width, float height, float progress, BorderRadius borderRadius, ColorRGBA color
    ) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        float smoothness = 0.8F;
        loadingProgram.use();
        GlUniform sizeUniform = loadingProgram.findUniform("Size");
        GlUniform radiusUniform = loadingProgram.findUniform("Radius");
        GlUniform smoothnessUniform = loadingProgram.findUniform("Smoothness");
        GlUniform progressUniform = loadingProgram.findUniform("Progress");
        GlUniform stripeWidthUniform = loadingProgram.findUniform("StripeWidth");
        GlUniform fadeUniform = loadingProgram.findUniform("Fade");
        if (sizeUniform != null) {
            sizeUniform.set(width, height);
        }

        if (radiusUniform != null) {
            radiusUniform.set(
                borderRadius.topLeftRadius(), borderRadius.bottomLeftRadius(), borderRadius.topRightRadius(), borderRadius.bottomRightRadius()
            );
        }

        if (smoothnessUniform != null) {
            smoothnessUniform.set(smoothness);
        }

        if (progressUniform != null) {
            progressUniform.set(progress);
        }

        if (stripeWidthUniform != null) {
            stripeWidthUniform.set(0.0F);
        }

        if (fadeUniform != null) {
            fadeUniform.set(0.5F);
        }

        drawSetup();
        float horizontalPadding = -smoothness / 2.0F + smoothness * 2.0F;
        float verticalPadding = smoothness / 2.0F + smoothness;
        float adjustedX = x - horizontalPadding / 2.0F;
        float adjustedY = y - verticalPadding / 2.0F;
        float adjustedWidth = width + horizontalPadding;
        float adjustedHeight = height + verticalPadding;
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR);
        builder.vertex(matrix4f, adjustedX, adjustedY, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX, adjustedY + adjustedHeight, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY + adjustedHeight, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY, 0.0F).color(color.getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        matrices.pop();
    }

    public static void drawRoundedRect(MatrixStack matrices, float x, float y, float width, float height, BorderRadius borderRadius, ColorRGBA color) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        float smoothness = 0.8F;
        rectangleProgram.use();
        GlUniform sizeUniform = rectangleProgram.findUniform("Size");
        GlUniform radiusUniform = rectangleProgram.findUniform("Radius");
        GlUniform smoothnessUniform = rectangleProgram.findUniform("Smoothness");
        if (sizeUniform != null) {
            sizeUniform.set(width, height);
        }

        if (radiusUniform != null) {
            radiusUniform.set(
                borderRadius.topLeftRadius(), borderRadius.bottomLeftRadius(), borderRadius.topRightRadius(), borderRadius.bottomRightRadius()
            );
        }

        if (smoothnessUniform != null) {
            smoothnessUniform.set(smoothness);
        }

        drawSetup();
        float horizontalPadding = -smoothness / 2.0F + smoothness * 2.0F;
        float verticalPadding = smoothness / 2.0F + smoothness;
        float adjustedX = x - horizontalPadding / 2.0F;
        float adjustedY = y - verticalPadding / 2.0F;
        float adjustedWidth = width + horizontalPadding;
        float adjustedHeight = height + verticalPadding;
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR);
        builder.vertex(matrix4f, adjustedX, adjustedY, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX, adjustedY + adjustedHeight, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY + adjustedHeight, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY, 0.0F).color(color.getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        matrices.pop();
    }

    public static void drawRoundedRect(
        MatrixStack matrices,
        float x,
        float y,
        float width,
        float height,
        BorderRadius borderRadius,
        ColorRGBA color1,
        ColorRGBA color2,
        ColorRGBA color3,
        ColorRGBA color4
    ) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        float smoothness = 0.8F;
        gradientRectangleProgram.use();
        GlUniform sizeUniform = gradientRectangleProgram.findUniform("Size");
        GlUniform radiusUniform = gradientRectangleProgram.findUniform("Radius");
        GlUniform smoothnessUniform = gradientRectangleProgram.findUniform("Smoothness");
        GlUniform topLeftColorUniform = gradientRectangleProgram.findUniform("TopLeftColor");
        GlUniform bottomLeftColorUniform = gradientRectangleProgram.findUniform("BottomLeftColor");
        GlUniform bottomRightColorUniform = gradientRectangleProgram.findUniform("BottomRightColor");
        GlUniform topRightColorUniform = gradientRectangleProgram.findUniform("TopRightColor");
        if (sizeUniform != null) {
            sizeUniform.set(width, height);
        }

        if (radiusUniform != null) {
            radiusUniform.set(
                borderRadius.topLeftRadius(), borderRadius.bottomLeftRadius(), borderRadius.topRightRadius(), borderRadius.bottomRightRadius()
            );
        }

        if (smoothnessUniform != null) {
            smoothnessUniform.set(smoothness);
        }

        if (topLeftColorUniform != null) {
            topLeftColorUniform.set(
                (float)color1.getRed() / 255.0F, (float)color1.getGreen() / 255.0F, (float)color1.getBlue() / 255.0F, (float)color1.getAlpha() / 255.0F
            );
        }

        if (bottomLeftColorUniform != null) {
            bottomLeftColorUniform.set(
                (float)color2.getRed() / 255.0F, (float)color2.getGreen() / 255.0F, (float)color2.getBlue() / 255.0F, (float)color2.getAlpha() / 255.0F
            );
        }

        if (bottomRightColorUniform != null) {
            bottomRightColorUniform.set(
                (float)color3.getRed() / 255.0F, (float)color3.getGreen() / 255.0F, (float)color3.getBlue() / 255.0F, (float)color3.getAlpha() / 255.0F
            );
        }

        if (topRightColorUniform != null) {
            topRightColorUniform.set(
                (float)color4.getRed() / 255.0F, (float)color4.getGreen() / 255.0F, (float)color4.getBlue() / 255.0F, (float)color4.getAlpha() / 255.0F
            );
        }

        drawSetup();
        float horizontalPadding = -smoothness / 2.0F + smoothness * 2.0F;
        float verticalPadding = smoothness / 2.0F + smoothness;
        float adjustedX = x - horizontalPadding / 2.0F;
        float adjustedY = y - verticalPadding / 2.0F;
        float adjustedWidth = width + horizontalPadding;
        float adjustedHeight = height + verticalPadding;
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR);
        builder.vertex(matrix4f, adjustedX, adjustedY, 0.0F).color(color1.getRGB());
        builder.vertex(matrix4f, adjustedX, adjustedY + adjustedHeight, 0.0F).color(color2.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY + adjustedHeight, 0.0F).color(color3.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY, 0.0F).color(color4.getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        matrices.pop();
    }

    public static void drawRoundedRect(MatrixStack matrices, float x, float y, float width, float height, BorderRadius borderRadius, Gradient gradient) {
        drawRoundedRect(
            matrices,
            x,
            y,
            width,
            height,
            borderRadius,
            gradient.getTopLeftColor(),
            gradient.getBottomLeftColor(),
            gradient.getBottomRightColor(),
            gradient.getTopRightColor()
        );
    }

    public static void drawRoundedBorder(
        MatrixStack matrices, float x, float y, float width, float height, float borderThickness, BorderRadius borderRadius, ColorRGBA borderColor
    ) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        float internalSmoothness = 0.8F;
        float externalSmoothness = 1.0F;
        borderProgram.use();
        GlUniform sizeUniform = borderProgram.findUniform("Size");
        GlUniform radiusUniform = borderProgram.findUniform("Radius");
        GlUniform smoothnessUniform = borderProgram.findUniform("Smoothness");
        GlUniform thicknessUniform = borderProgram.findUniform("Thickness");
        if (sizeUniform != null) {
            sizeUniform.set(width, height);
        }

        if (radiusUniform != null) {
            radiusUniform.set(
                borderRadius.topLeftRadius(), borderRadius.bottomLeftRadius(), borderRadius.topRightRadius(), borderRadius.bottomRightRadius()
            );
        }

        if (smoothnessUniform != null) {
            smoothnessUniform.set(internalSmoothness, externalSmoothness);
        }

        if (thicknessUniform != null) {
            thicknessUniform.set(borderThickness);
        }

        drawSetup();
        float horizontalPadding = -externalSmoothness / 2.0F + externalSmoothness * 2.0F;
        float verticalPadding = externalSmoothness / 2.0F + externalSmoothness;
        float adjustedX = x - horizontalPadding / 2.0F;
        float adjustedY = y - verticalPadding / 2.0F;
        float adjustedWidth = width + horizontalPadding;
        float adjustedHeight = height + verticalPadding;
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR);
        builder.vertex(matrix4f, adjustedX, adjustedY, 0.0F).color(borderColor.getRGB());
        builder.vertex(matrix4f, adjustedX, adjustedY + adjustedHeight, 0.0F).color(borderColor.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY + adjustedHeight, 0.0F).color(borderColor.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY, 0.0F).color(borderColor.getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        matrices.pop();
    }

    public static void drawRoundedCorner(
        MatrixStack matrices, float x, float y, float width, float height, float borderThikenes, float delta, ColorRGBA color, BorderRadius radius
    ) {
        if (Interface.INSTANCE.isCorners()) {
            x -= 0.3F;
            y -= 0.3F;
            width += 0.6F;
            height += 0.6F;
            drawRoundedCornerOnly(matrices, x, y, delta, delta, borderThikenes, radius, color, 0.0F);
            drawRoundedCornerOnly(matrices, x + width - delta, y, delta, delta, borderThikenes, radius, color, 1.0F);
            drawRoundedCornerOnly(matrices, x, y + height - delta, delta, delta, borderThikenes, radius, color, 2.0F);
            drawRoundedCornerOnly(matrices, x + width - delta, y + height - delta, delta, delta, borderThikenes, radius, color, 3.0F);
        }
    }

    public static void drawRoundedCornerOnly(
        MatrixStack matrices,
        float x,
        float y,
        float width,
        float height,
        float borderThickness,
        BorderRadius borderRadius,
        ColorRGBA borderColor,
        float cornerIdex
    ) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        float internalSmoothness = 0.8F;
        float externalSmoothness = 1.0F;
        figmaBorderProgram.use();
        GlUniform sizeUniform = figmaBorderProgram.findUniform("Size");
        GlUniform radiusUniform = figmaBorderProgram.findUniform("Radius");
        GlUniform smoothnessUniform = figmaBorderProgram.findUniform("Smoothness");
        GlUniform thicknessUniform = figmaBorderProgram.findUniform("Thickness");
        GlUniform cornerIndexUniform = figmaBorderProgram.findUniform("CornerIndex");
        if (sizeUniform != null) {
            sizeUniform.set(width, height);
        }

        if (radiusUniform != null) {
            radiusUniform.set(
                borderRadius.topLeftRadius(), borderRadius.bottomLeftRadius(), borderRadius.topRightRadius(), borderRadius.bottomRightRadius()
            );
        }

        if (smoothnessUniform != null) {
            smoothnessUniform.set(internalSmoothness, externalSmoothness);
        }

        if (thicknessUniform != null) {
            thicknessUniform.set(borderThickness);
        }

        if (cornerIndexUniform != null) {
            cornerIndexUniform.set(cornerIdex);
        }

        drawSetup();
        float horizontalPadding = -externalSmoothness / 2.0F + externalSmoothness * 2.0F;
        float verticalPadding = externalSmoothness / 2.0F + externalSmoothness;
        float adjustedX = x - horizontalPadding / 2.0F;
        float adjustedY = y - verticalPadding / 2.0F;
        float adjustedWidth = width + horizontalPadding;
        float adjustedHeight = height + verticalPadding;
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR);
        builder.vertex(matrix4f, adjustedX, adjustedY, 0.0F).color(borderColor.getRGB());
        builder.vertex(matrix4f, adjustedX, adjustedY + adjustedHeight, 0.0F).color(borderColor.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY + adjustedHeight, 0.0F).color(borderColor.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY, 0.0F).color(borderColor.getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        matrices.pop();
    }

    public static void drawTexture(MatrixStack matrices, Identifier identifier, float x, float y, float width, float height, ColorRGBA textureColor) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        RenderSystem.setShader(ShaderProgramKeys.POSITION_TEX_COLOR);
        RenderSystem.setShaderTexture(0, identifier);
        drawSetup();
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
        builder.vertex(matrix4f, x, y, 0.0F).texture(0.0F, 0.0F).color(textureColor.getRGB());
        builder.vertex(matrix4f, x, y + height, 0.0F).texture(0.0F, 1.0F).color(textureColor.getRGB());
        builder.vertex(matrix4f, x + width, y + height, 0.0F).texture(1.0F, 1.0F).color(textureColor.getRGB());
        builder.vertex(matrix4f, x + width, y, 0.0F).texture(1.0F, 0.0F).color(textureColor.getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        RenderSystem.setShaderTexture(0, 0);
        matrices.pop();
    }

    public static void drawTexture(MatrixStack matrices, Identifier identifier, float x, float y, float width, float height, Gradient textureColor) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        RenderSystem.setShader(ShaderProgramKeys.POSITION_TEX_COLOR);
        RenderSystem.setShaderTexture(0, identifier);
        drawSetup();
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
        builder.vertex(matrix4f, x, y, 0.0F).texture(0.0F, 0.0F).color(textureColor.getTopLeftColor().getRGB());
        builder.vertex(matrix4f, x, y + height, 0.0F).texture(0.0F, 1.0F).color(textureColor.getBottomLeftColor().getRGB());
        builder.vertex(matrix4f, x + width, y + height, 0.0F).texture(1.0F, 1.0F).color(textureColor.getBottomRightColor().getRGB());
        builder.vertex(matrix4f, x + width, y, 0.0F).texture(1.0F, 0.0F).color(textureColor.getTopRightColor().getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        RenderSystem.setShaderTexture(0, 0);
        matrices.pop();
    }

    public static void drawTexture(
        MatrixStack matrices, Identifier identifier, float x, float y, float width, float height, float u1, float u2, float v1, float v2, ColorRGBA clor
    ) {
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        matrices.push();
        int color = clor.getRGB();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        float x2 = x + width;
        float y2 = y + height;
        RenderSystem.setShader(ShaderProgramKeys.POSITION_TEX_COLOR);
        RenderSystem.setShaderTexture(0, identifier);
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
        builder.vertex(matrix4f, x, y, 0.0F).texture(u1, v1).color(color);
        builder.vertex(matrix4f, x, y2, 0.0F).texture(u1, v2).color(color);
        builder.vertex(matrix4f, x2, y2, 0.0F).texture(u2, v2).color(color);
        builder.vertex(matrix4f, x2, y, 0.0F).texture(u2, v1).color(color);
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        RenderSystem.setShaderTexture(0, 0);
        matrices.pop();
        RenderSystem.disableBlend();
    }

    public static void drawSprite(MatrixStack matrices, CustomSprite sprite, float x, float y, float width, float height, ColorRGBA color) {
        drawTexture(matrices, sprite.getTexture(), x, y, width, height, 0.0F, 1.0F, 0.0F, 1.0F, color);
    }

    public static void drawRoundedTexture(MatrixStack matrices, Identifier identifier, float x, float y, float width, float height, BorderRadius borderRadius) {
        drawRoundedTexture(matrices, identifier, x, y, width, height, borderRadius, ColorRGBA.WHITE);
    }

    public static void drawRoundedTexture(
        MatrixStack matrices, Identifier identifier, float x, float y, float width, float height, BorderRadius borderRadius, ColorRGBA color
    ) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        float smoothness = 0.8F;
        roundedTextureProgram.use();
        RenderSystem.setShaderTexture(0, identifier);
        GlUniform sizeUniform = roundedTextureProgram.findUniform("Size");
        GlUniform radiusUniform = roundedTextureProgram.findUniform("Radius");
        GlUniform smoothnessUniform = roundedTextureProgram.findUniform("Smoothness");
        if (sizeUniform != null) {
            sizeUniform.set(width, height);
        }

        if (radiusUniform != null) {
            radiusUniform.set(
                borderRadius.topLeftRadius(), borderRadius.bottomLeftRadius(), borderRadius.topRightRadius(), borderRadius.bottomRightRadius()
            );
        }

        if (smoothnessUniform != null) {
            smoothnessUniform.set(smoothness);
        }

        drawSetup();
        float horizontalPadding = -smoothness / 2.0F + smoothness * 2.0F;
        float verticalPadding = smoothness / 2.0F + smoothness;
        float adjustedX = x - horizontalPadding / 2.0F;
        float adjustedY = y - verticalPadding / 2.0F;
        float adjustedWidth = width + horizontalPadding;
        float adjustedHeight = height + verticalPadding;
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
        builder.vertex(matrix4f, adjustedX, adjustedY, 0.0F).texture(0.0F, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX, adjustedY + adjustedHeight, 0.0F).texture(0.0F, 1.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY + adjustedHeight, 0.0F).texture(1.0F, 1.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY, 0.0F).texture(1.0F, 0.0F).color(color.getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        RenderSystem.setShaderTexture(0, 0);
        matrices.pop();
    }

    public static void drawShadow(MatrixStack matrices, float x, float y, float width, float height, float softness, BorderRadius borderRadius, ColorRGBA color) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        rectangleProgram.use();
        GlUniform sizeUniform = rectangleProgram.findUniform("Size");
        GlUniform radiusUniform = rectangleProgram.findUniform("Radius");
        GlUniform smoothnessUniform = rectangleProgram.findUniform("Smoothness");
        if (sizeUniform != null) {
            sizeUniform.set(width, height);
        }

        if (radiusUniform != null) {
            radiusUniform.set(
                borderRadius.topLeftRadius() * 3.0F,
                borderRadius.bottomLeftRadius() * 3.0F,
                borderRadius.topRightRadius() * 3.0F,
                borderRadius.bottomRightRadius() * 3.0F
            );
        }

        if (smoothnessUniform != null) {
            smoothnessUniform.set(softness);
        }

        drawSetup();
        float horizontalPadding = -softness / 2.0F + softness * 2.0F;
        float verticalPadding = softness / 2.0F + softness;
        float adjustedX = x - horizontalPadding / 2.0F;
        float adjustedY = y - verticalPadding / 2.0F;
        float adjustedWidth = width + horizontalPadding;
        float adjustedHeight = height + verticalPadding;
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR);
        builder.vertex(matrix4f, adjustedX, adjustedY, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX, adjustedY + adjustedHeight, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY + adjustedHeight, 0.0F).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY, 0.0F).color(color.getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        matrices.pop();
    }

    public static void drawBlur(
        MatrixStack matrices, float x, float y, float width, float height, float blurRadius, float squirt, BorderRadius borderRadius, ColorRGBA color
    ) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        float smoothness = 0.03F;
        blurRadius /= 22.5F;
        if (!(blurRadius <= 0.0F)) {
            blurProgram.setBlurRadius(2.0F);
            squircleTextureProgram.use();
            RenderSystem.setShaderTexture(0, BlurProgram.getTexture());
            GlUniform sizeUniform = squircleTextureProgram.findUniform("Size");
            GlUniform radiusUniform = squircleTextureProgram.findUniform("Radius");
            GlUniform smoothnessUniform = squircleTextureProgram.findUniform("Smoothness");
            GlUniform cornerSmoothnessUniform = squircleTextureProgram.findUniform("CornerSmoothness");
            if (sizeUniform != null) {
                sizeUniform.set(width, height);
            }

            if (radiusUniform != null) {
                radiusUniform.set(
                    borderRadius.topLeftRadius() * squirt / 2.0F,
                    borderRadius.bottomLeftRadius() * squirt / 2.0F,
                    borderRadius.topRightRadius() * squirt / 2.0F,
                    borderRadius.bottomRightRadius() * squirt / 2.0F
                );
            }

            if (smoothnessUniform != null) {
                smoothnessUniform.set(0.1F);
            }

            if (cornerSmoothnessUniform != null) {
                cornerSmoothnessUniform.set(squirt);
            }

            drawSetup();
            float horizontalPadding = -smoothness / 2.0F + smoothness * 2.0F;
            float verticalPadding = smoothness / 2.0F + smoothness;
            float adjustedX = x - horizontalPadding / 2.0F;
            float adjustedY = y - verticalPadding / 2.0F;
            float adjustedWidth = width + horizontalPadding;
            float adjustedHeight = height + verticalPadding;
            int screenWidth = mc.getWindow().getScaledWidth();
            int screenHeight = mc.getWindow().getScaledHeight();
            float u = adjustedX / (float)screenWidth;
            float v = ((float)screenHeight - adjustedY - adjustedHeight) / (float)screenHeight;
            float texWidth = adjustedWidth / (float)screenWidth;
            float texHeight = adjustedHeight / (float)screenHeight;
            BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
            builder.vertex(matrix4f, adjustedX, adjustedY, 0.0F).texture(u, v + texHeight).color(color.getRGB());
            builder.vertex(matrix4f, adjustedX, adjustedY + adjustedHeight, 0.0F).texture(u, v).color(color.getRGB());
            builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY + adjustedHeight, 0.0F)
                .texture(u + texWidth, v)
                .color(color.getRGB());
            builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY, 0.0F).texture(u + texWidth, v + texHeight).color(color.getRGB());
            BufferRenderer.drawWithGlobalProgram(builder.end());
            drawEnd();
            RenderSystem.setShaderTexture(0, 0);
            matrices.pop();
        }
    }

    public static void drawBlurHud(
        MatrixStack matrices, float x, float y, float width, float height, float blurRadius, BorderRadius borderRadius, ColorRGBA color
    ) {
        drawBlurHudBooleanCheck(matrices, x, y, width, height, blurRadius, borderRadius, color, Interface.INSTANCE.isBlur(), Interface.INSTANCE.isGlow());
    }

    public static void drawBlurHudBooleanCheck(
        MatrixStack matrices,
        float x,
        float y,
        float width,
        float height,
        float blurRadius,
        BorderRadius borderRadius,
        ColorRGBA color,
        boolean blur,
        boolean glow
    ) {
        if (blur) {
            matrices.push();
            Matrix4f matrix4f = matrices.peek().getPositionMatrix();
            blurRadius /= 22.5F;
            if (blurRadius <= 0.0F) {
                return;
            }

            blurProgram.setBlurRadius(2.0F);
            roundedTextureProgram.use();
            RenderSystem.setShaderTexture(0, BlurProgram.getTexture());
            GlUniform sizeUniform = roundedTextureProgram.findUniform("Size");
            GlUniform radiusUniform = roundedTextureProgram.findUniform("Radius");
            GlUniform smoothnessUniform = roundedTextureProgram.findUniform("Smoothness");
            if (sizeUniform != null) {
                sizeUniform.set(width, height);
            }

            if (radiusUniform != null) {
                radiusUniform.set(
                    borderRadius.topLeftRadius(), borderRadius.bottomLeftRadius(), borderRadius.topRightRadius(), borderRadius.bottomRightRadius()
                );
            }

            if (smoothnessUniform != null) {
                smoothnessUniform.set(0.01F);
            }

            drawSetup();
            int screenWidth = mc.getWindow().getScaledWidth();
            int screenHeight = mc.getWindow().getScaledHeight();
            float u = x / (float)screenWidth;
            float v = ((float)screenHeight - y - height) / (float)screenHeight;
            float texWidth = width / (float)screenWidth;
            float texHeight = height / (float)screenHeight;
            BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
            builder.vertex(matrix4f, x, y, 0.0F).texture(u, v + texHeight).color(color.getRGB());
            builder.vertex(matrix4f, x, y + height, 0.0F).texture(u, v).color(color.getRGB());
            builder.vertex(matrix4f, x + width, y + height, 0.0F).texture(u + texWidth, v).color(color.getRGB());
            builder.vertex(matrix4f, x + width, y, 0.0F).texture(u + texWidth, v + texHeight).color(color.getRGB());
            BufferRenderer.drawWithGlobalProgram(builder.end());
            drawEnd();
            RenderSystem.setShaderTexture(0, 0);
            matrices.pop();
        }

        if (glow) {
            drawGlow(matrices, x, y, width, height, Interface.INSTANCE.getGlowRadius());
        }
    }

    public static void drawGlow(MatrixStack matrixStack, float x, float y, float width, float height, int glowRadius) {
        Render2DUtil.drawGradientBlurredShadow(matrixStack, x, y, width, height, glowRadius, Zenith.getInstance().getThemeManager().getClientColor());
    }

    public static void drawBlur(MatrixStack matrices, float x, float y, float width, float height, float blurRadius, BorderRadius borderRadius, ColorRGBA color) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        blurRadius /= 22.5F;
        if (!(blurRadius <= 0.0F)) {
            blurProgram.setBlurRadius(2.0F);
            roundedTextureProgram.use();
            RenderSystem.setShaderTexture(0, BlurProgram.getTexture());
            GlUniform sizeUniform = roundedTextureProgram.findUniform("Size");
            GlUniform radiusUniform = roundedTextureProgram.findUniform("Radius");
            GlUniform smoothnessUniform = roundedTextureProgram.findUniform("Smoothness");
            if (sizeUniform != null) {
                sizeUniform.set(width, height);
            }

            if (radiusUniform != null) {
                radiusUniform.set(
                    borderRadius.topLeftRadius(), borderRadius.bottomLeftRadius(), borderRadius.topRightRadius(), borderRadius.bottomRightRadius()
                );
            }

            if (smoothnessUniform != null) {
                smoothnessUniform.set(0.01F);
            }

            drawSetup();
            int screenWidth = mc.getWindow().getScaledWidth();
            int screenHeight = mc.getWindow().getScaledHeight();
            float u = x / (float)screenWidth;
            float v = ((float)screenHeight - y - height) / (float)screenHeight;
            float texWidth = width / (float)screenWidth;
            float texHeight = height / (float)screenHeight;
            BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
            builder.vertex(matrix4f, x, y, 0.0F).texture(u, v + texHeight).color(color.getRGB());
            builder.vertex(matrix4f, x, y + height, 0.0F).texture(u, v).color(color.getRGB());
            builder.vertex(matrix4f, x + width, y + height, 0.0F).texture(u + texWidth, v).color(color.getRGB());
            builder.vertex(matrix4f, x + width, y, 0.0F).texture(u + texWidth, v + texHeight).color(color.getRGB());
            BufferRenderer.drawWithGlobalProgram(builder.end());
            drawEnd();
            RenderSystem.setShaderTexture(0, 0);
            matrices.pop();
        }
    }

    public static void drawImage(MatrixStack matrices, BufferBuilder builder, double x, double y, double z, double width, double height, ColorRGBA color) {
        Matrix4f matrix = matrices.peek().getPositionMatrix();
        builder.vertex(matrix, (float)x, (float)(y + height), (float)z).texture(0.0F, 1.0F).color(color.getRGB());
        builder.vertex(matrix, (float)(x + width), (float)(y + height), (float)z).texture(1.0F, 1.0F).color(color.getRGB());
        builder.vertex(matrix, (float)(x + width), (float)y, (float)z).texture(1.0F, 0.0F).color(color.getRGB());
        builder.vertex(matrix, (float)x, (float)y, (float)z).texture(0.0F, 0.0F).color(color.getRGB());
    }

    public static void drawImage(MatrixStack matrices, Identifier identifier, double x, double y, double z, double width, double height, ColorRGBA color) {
        RenderSystem.setShaderTexture(0, identifier);
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
        Matrix4f matrix = matrices.peek().getPositionMatrix();
        builder.vertex(matrix, (float)x, (float)(y + height), (float)z).texture(0.0F, 1.0F).color(color.getRGB());
        builder.vertex(matrix, (float)(x + width), (float)(y + height), (float)z).texture(1.0F, 1.0F).color(color.getRGB());
        builder.vertex(matrix, (float)(x + width), (float)y, (float)z).texture(1.0F, 0.0F).color(color.getRGB());
        builder.vertex(matrix, (float)x, (float)y, (float)z).texture(0.0F, 0.0F).color(color.getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
    }

    public static void drawPlayerHeadWithRoundedShader(
        MatrixStack matrices, Identifier skinTexture, float x, float y, float size, BorderRadius borderRadius, ColorRGBA color
    ) {
        drawRoundedTextureWithUV(matrices, skinTexture, x, y, size, size, borderRadius, color, 0.125F, 0.125F, 0.25F, 0.25F);
    }

    private static void drawPlayerHatLayerWithRoundedShader(
        MatrixStack matrices, Identifier skinTexture, float x, float y, float size, BorderRadius borderRadius, ColorRGBA color
    ) {
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        drawRoundedTextureWithUV(matrices, skinTexture, x, y, size, size, borderRadius, color, 0.625F, 0.125F, 0.75F, 0.25F);
        RenderSystem.disableBlend();
    }

    public static void drawRoundedTextureWithUV(
        MatrixStack matrices,
        Identifier identifier,
        float x,
        float y,
        float width,
        float height,
        BorderRadius borderRadius,
        ColorRGBA color,
        float u1,
        float v1,
        float u2,
        float v2
    ) {
        matrices.push();
        Matrix4f matrix4f = matrices.peek().getPositionMatrix();
        float smoothness = 0.8F;
        roundedTextureProgram.use();
        RenderSystem.setShaderTexture(0, identifier);
        GlUniform sizeUniform = roundedTextureProgram.findUniform("Size");
        GlUniform radiusUniform = roundedTextureProgram.findUniform("Radius");
        GlUniform smoothnessUniform = roundedTextureProgram.findUniform("Smoothness");
        if (sizeUniform != null) {
            sizeUniform.set(width, height);
        }

        if (radiusUniform != null) {
            radiusUniform.set(
                borderRadius.topLeftRadius(), borderRadius.bottomLeftRadius(), borderRadius.topRightRadius(), borderRadius.bottomRightRadius()
            );
        }

        if (smoothnessUniform != null) {
            smoothnessUniform.set(smoothness);
        }

        drawSetup();
        float horizontalPadding = -smoothness / 2.0F + smoothness * 2.0F;
        float verticalPadding = smoothness / 2.0F + smoothness;
        float adjustedX = x - horizontalPadding / 2.0F;
        float adjustedY = y - verticalPadding / 2.0F;
        float adjustedWidth = width + horizontalPadding;
        float adjustedHeight = height + verticalPadding;
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
        builder.vertex(matrix4f, adjustedX, adjustedY, 0.0F).texture(u1, v1).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX, adjustedY + adjustedHeight, 0.0F).texture(u1, v2).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY + adjustedHeight, 0.0F).texture(u2, v2).color(color.getRGB());
        builder.vertex(matrix4f, adjustedX + adjustedWidth, adjustedY, 0.0F).texture(u2, v1).color(color.getRGB());
        BufferRenderer.drawWithGlobalProgram(builder.end());
        drawEnd();
        RenderSystem.setShaderTexture(0, 0);
        matrices.pop();
    }

    public static void drawSetup() {
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
    }

    public static void drawEnd() {
        RenderSystem.disableBlend();
    }

    @Generated
    private DrawUtil() {
        throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
    }

    static record HeadUV(float u1, float v1, float uSize, float vSize) {
    }
}
