package zenith.zov.utility.render;

import com.mojang.blaze3d.systems.RenderSystem;
import java.awt.Color;
import java.util.function.Consumer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.ShaderProgramKey;
import net.minecraft.client.gl.ShaderProgramKeys;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.BufferRenderer;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.render.VertexFormat.DrawMode;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis;
import net.minecraft.util.math.Vec3d;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL11;
import zenith.zov.utility.interfaces.IMinecraft;

public final class RenderUtils implements IMinecraft {
    public static Vec3d getCameraPos() {
        return mc.getBlockEntityRenderDispatcher().camera.getPos();
    }

    public static double deltaTime() {
        return mc.getCurrentFps() > 0 ? 1.0 / (double)mc.getCurrentFps() : 1.0;
    }

    public static float fast(float end, float start, float multiple) {
        return (1.0F - MathHelper.clamp((float)(deltaTime() * (double)multiple), 0.0F, 1.0F)) * end
            + MathHelper.clamp((float)(deltaTime() * (double)multiple), 0.0F, 1.0F) * start;
    }

    public static Vec3d getPlayerLookVec(PlayerEntity player) {
        float f = (float) (Math.PI / 180.0);
        float pi = (float) Math.PI;
        float f1 = MathHelper.cos(-player.getYaw() * f - pi);
        float f2 = MathHelper.sin(-player.getYaw() * f - pi);
        float f3 = -MathHelper.cos(-player.getPitch() * f);
        float f4 = MathHelper.sin(-player.getPitch() * f);
        return new Vec3d((double)(f2 * f3), (double)f4, (double)(f1 * f3)).normalize();
    }

    public static void renderRoundedQuad(
        MatrixStack matrices, Color c, double x, double y, double x2, double y2, double corner1, double corner2, double corner3, double corner4, double samples
    ) {
        int color = c.getRGB();
        Matrix4f matrix = matrices.peek().getPositionMatrix();
        float f = (float)(color >> 24 & 0xFF) / 255.0F;
        float g = (float)(color >> 16 & 0xFF) / 255.0F;
        float h = (float)(color >> 8 & 0xFF) / 255.0F;
        float k = (float)(color & 0xFF) / 255.0F;
        RenderSystem.enableBlend();
        RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
        RenderSystem.setShader(ShaderProgramKeys.POSITION_COLOR);
        renderRoundedQuadInternal(matrix, g, h, k, f, x, y, x2, y2, corner1, corner2, corner3, corner4, samples);
        RenderSystem.enableCull();
        RenderSystem.disableBlend();
    }

    private static void setup() {
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
    }

    private static void cleanup() {
        RenderSystem.enableCull();
        RenderSystem.disableBlend();
    }

    public static void renderRoundedQuad(MatrixStack matrices, Color c, double x, double y, double x1, double y1, double rad, double samples) {
        renderRoundedQuad(matrices, c, x, y, x1, y1, rad, rad, rad, rad, samples);
    }

    public static void renderRoundedOutlineInternal(
        Matrix4f matrix,
        float cr,
        float cg,
        float cb,
        float ca,
        double fromX,
        double fromY,
        double toX,
        double toY,
        double radC1,
        double radC2,
        double radC3,
        double radC4,
        double width,
        double samples
    ) {
        BufferBuilder bufferBuilder = Tessellator.getInstance().begin(DrawMode.TRIANGLE_STRIP, VertexFormats.POSITION_COLOR);
        double[][] map = new double[][]{
            {toX - radC4, toY - radC4, radC4}, {toX - radC2, fromY + radC2, radC2}, {fromX + radC1, fromY + radC1, radC1}, {fromX + radC3, toY - radC3, radC3}
        };

        for (int i = 0; i < 4; i++) {
            double[] current = map[i];
            double rad = current[2];
            double r = (double)i * 90.0;

            while (r < 90.0 + (double)i * 90.0) {
                float rad1 = (float)Math.toRadians(r);
                double sin1 = Math.sin((double)rad1);
                float sin = (float)(sin1 * rad);
                double cos1 = Math.cos((double)rad1);
                float cos = (float)(cos1 * rad);
                bufferBuilder.vertex(matrix, (float)current[0] + sin, (float)current[1] + cos, 0.0F).color(cr, cg, cb, ca);
                bufferBuilder.vertex(matrix, (float)(current[0] + (double)sin + sin1 * width), (float)(current[1] + (double)cos + cos1 * width), 0.0F)
                    .color(cr, cg, cb, ca);
                r += 90.0 / samples;
            }

            float rad1 = (float)Math.toRadians(90.0 + (double)i * 90.0);
            double sin1 = Math.sin((double)rad1);
            float sin = (float)(sin1 * rad);
            double cos1 = Math.cos((double)rad1);
            float cos = (float)(cos1 * rad);
            bufferBuilder.vertex(matrix, (float)current[0] + sin, (float)current[1] + cos, 0.0F).color(cr, cg, cb, ca);
            bufferBuilder.vertex(matrix, (float)(current[0] + (double)sin + sin1 * width), (float)(current[1] + (double)cos + cos1 * width), 0.0F)
                .color(cr, cg, cb, ca);
        }

        int i = 0;
        double[] current = map[i];
        double rad = current[2];
        float cos = (float)rad;
        bufferBuilder.vertex(matrix, (float)current[0], (float)current[1] + cos, 0.0F).color(cr, cg, cb, ca);
        bufferBuilder.vertex(matrix, (float)current[0], (float)(current[1] + (double)cos + width), 0.0F).color(cr, cg, cb, ca);
        BufferRenderer.drawWithGlobalProgram(bufferBuilder.end());
    }

    public static void setScissorRegion(int x, int y, int width, int height) {
        Screen currentScreen = MinecraftClient.getInstance().currentScreen;
        int screenHeight;
        if (currentScreen == null) {
            screenHeight = 0;
        } else {
            screenHeight = currentScreen.height - height;
        }

        double scaleFactor = MinecraftClient.getInstance().getWindow().getScaleFactor();
        GL11.glScissor(
            (int)((double)x * scaleFactor),
            (int)((double)screenHeight * scaleFactor),
            (int)((double)(width - x) * scaleFactor),
            (int)((double)(height - y) * scaleFactor)
        );
        GL11.glEnable(3089);
    }

    public static void renderCircle(MatrixStack matrices, Color c, double originX, double originY, double rad, int segments) {
        int segments1 = MathHelper.clamp(segments, 4, 360);
        int color = c.getRGB();
        Matrix4f matrix = matrices.peek().getPositionMatrix();
        float f = (float)(color >> 24 & 0xFF) / 255.0F;
        float g = (float)(color >> 16 & 0xFF) / 255.0F;
        float h = (float)(color >> 8 & 0xFF) / 255.0F;
        float k = (float)(color & 0xFF) / 255.0F;
        setup();
        RenderSystem.setShader(ShaderProgramKeys.POSITION_COLOR);
        BufferBuilder bufferBuilder = Tessellator.getInstance().begin(DrawMode.TRIANGLE_FAN, VertexFormats.POSITION_COLOR);

        for (int i = 0; i < 360; i += Math.min(360 / segments1, 360 - i)) {
            double radians = Math.toRadians((double)i);
            double sin = Math.sin(radians) * rad;
            double cos = Math.cos(radians) * rad;
            bufferBuilder.vertex(matrix, (float)(originX + sin), (float)(originY + cos), 0.0F).color(g, h, k, f);
        }

        BufferRenderer.drawWithGlobalProgram(bufferBuilder.end());
        cleanup();
    }

    public static void renderShaderRect(
        MatrixStack matrixStack, Color color, Color color2, Color color3, Color color4, float f, float f2, float f3, float f4, float f5, float f6
    ) {
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        Tessellator tessellator = Tessellator.getInstance();
        BufferBuilder bufferBuilder = tessellator.begin(DrawMode.QUADS, VertexFormats.POSITION);
        bufferBuilder.vertex(matrixStack.peek().getPositionMatrix(), f - 10.0F, f2 - 10.0F, 0.0F);
        bufferBuilder.vertex(matrixStack.peek().getPositionMatrix(), f - 10.0F, f2 + f4 + 20.0F, 0.0F);
        bufferBuilder.vertex(matrixStack.peek().getPositionMatrix(), f + f3 + 20.0F, f2 + f4 + 20.0F, 0.0F);
        bufferBuilder.vertex(matrixStack.peek().getPositionMatrix(), f + f3 + 20.0F, f2 - 10.0F, 0.0F);
        BufferRenderer.drawWithGlobalProgram(bufferBuilder.end());
        RenderSystem.disableBlend();
    }

    public static void renderRoundedOutline(
        DrawContext poses,
        Color c,
        double fromX,
        double fromY,
        double toX,
        double toY,
        double rad1,
        double rad2,
        double rad3,
        double rad4,
        double width,
        double samples
    ) {
        int color = c.getRGB();
        Matrix4f matrix = poses.getMatrices().peek().getPositionMatrix();
        float f = (float)(color >> 24 & 0xFF) / 255.0F;
        float g = (float)(color >> 16 & 0xFF) / 255.0F;
        float h = (float)(color >> 8 & 0xFF) / 255.0F;
        float k = (float)(color & 0xFF) / 255.0F;
        setup();
        RenderSystem.setShader(ShaderProgramKeys.POSITION_COLOR);
        renderRoundedOutlineInternal(matrix, g, h, k, f, fromX, fromY, toX, toY, rad1, rad2, rad3, rad4, width, samples);
        cleanup();
    }

    public static MatrixStack matrixFrom(double x, double y, double z) {
        MatrixStack matrices = new MatrixStack();
        Camera camera = MinecraftClient.getInstance().gameRenderer.getCamera();
        matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(camera.getPitch()));
        matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(camera.getYaw() + 180.0F));
        matrices.translate(x - camera.getPos().x, y - camera.getPos().y, z - camera.getPos().z);
        return matrices;
    }

    public static void renderQuad(MatrixStack matrices, float x, float y, float width, float height, int color) {
        float alpha = (float)(color >> 24 & 0xFF) / 255.0F;
        float red = (float)(color >> 16 & 0xFF) / 255.0F;
        float green = (float)(color >> 8 & 0xFF) / 255.0F;
        float blue = (float)(color & 0xFF) / 255.0F;
        matrices.push();
        matrices.scale(0.5F, 0.5F, 0.5F);
        matrices.translate((double)x, (double)y, 0.0);
        Tessellator tessellator = Tessellator.getInstance();
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        BufferBuilder bufferBuilder = tessellator.begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR);
        bufferBuilder.vertex(0.0F, 0.0F, 0.0F).color(red, green, blue, alpha);
        bufferBuilder.vertex(0.0F, height, 0.0F).color(red, green, blue, alpha);
        bufferBuilder.vertex(width, height, 0.0F).color(red, green, blue, alpha);
        bufferBuilder.vertex(width, 0.0F, 0.0F).color(red, green, blue, alpha);
        BufferRenderer.drawWithGlobalProgram(bufferBuilder.end());
        RenderSystem.disableBlend();
        matrices.pop();
    }

    public static void renderRoundedQuadInternal(
        Matrix4f matrix,
        float cr,
        float cg,
        float cb,
        float ca,
        double fromX,
        double fromY,
        double toX,
        double toY,
        double corner1,
        double corner2,
        double corner3,
        double corner4,
        double samples
    ) {
        BufferBuilder bufferBuilder = Tessellator.getInstance().begin(DrawMode.TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
        double[][] map = new double[][]{
            {toX - corner4, toY - corner4, corner4},
            {toX - corner2, fromY + corner2, corner2},
            {fromX + corner1, fromY + corner1, corner1},
            {fromX + corner3, toY - corner3, corner3}
        };

        for (int i = 0; i < 4; i++) {
            double[] current = map[i];
            double rad = current[2];
            double r = (double)i * 90.0;

            while (r < 90.0 + (double)i * 90.0) {
                float rad1 = (float)Math.toRadians(r);
                float sin = (float)(Math.sin((double)rad1) * rad);
                float cos = (float)(Math.cos((double)rad1) * rad);
                bufferBuilder.vertex(matrix, (float)current[0] + sin, (float)current[1] + cos, 0.0F).color(cr, cg, cb, ca);
                r += 90.0 / samples;
            }

            float rad1 = (float)Math.toRadians(90.0 + (double)i * 90.0);
            float sin = (float)(Math.sin((double)rad1) * rad);
            float cos = (float)(Math.cos((double)rad1) * rad);
            bufferBuilder.vertex(matrix, (float)current[0] + sin, (float)current[1] + cos, 0.0F).color(cr, cg, cb, ca);
        }

        BufferRenderer.drawWithGlobalProgram(bufferBuilder.end());
    }

    public static void renderFilledBox(MatrixStack matrices, float f, float f2, float f3, float f4, float f5, float f6, Color color) {
        RenderSystem.enableBlend();
        RenderSystem.disableDepthTest();
        RenderSystem.setShaderColor(
            (float)color.getRed() / 255.0F, (float)color.getGreen() / 255.0F, (float)color.getBlue() / 255.0F, (float)color.getAlpha() / 255.0F
        );
        RenderSystem.setShader(ShaderProgramKeys.POSITION);
        Tessellator tessellator = Tessellator.getInstance();
        BufferBuilder bufferBuilder = tessellator.begin(DrawMode.TRIANGLE_STRIP, VertexFormats.POSITION);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f2, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f2, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f2, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f2, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f5, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f5, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f5, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f2, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f5, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f2, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f2, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f2, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f5, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f5, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f5, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f2, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f5, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f2, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f2, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f2, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f2, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f2, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f2, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f5, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f5, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f, f5, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f5, f3);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f5, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f5, f6);
        bufferBuilder.vertex(matrices.peek().getPositionMatrix(), f4, f5, f6);
        BufferRenderer.drawWithGlobalProgram(bufferBuilder.end());
        RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
        RenderSystem.enableDepthTest();
        RenderSystem.disableBlend();
    }

    public static void renderLine(MatrixStack matrices, Color color, Vec3d start, Vec3d end) {
        matrices.push();
        Matrix4f s = matrices.peek().getPositionMatrix();
        GL11.glEnable(32925);
        GL11.glEnable(2848);
        GL11.glHint(3154, 4354);
        GL11.glDepthFunc(519);
        RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
        RenderSystem.defaultBlendFunc();
        RenderSystem.enableBlend();
        genericAABBRender(
            DrawMode.DEBUG_LINES,
            VertexFormats.POSITION_COLOR,
            ShaderProgramKeys.POSITION_COLOR,
            s,
            start,
            end.subtract(start),
            color,
            (buffer, x, y, z, x1, y1, z1, red, green, blue, alpha, matrix) -> {
                buffer.vertex(matrix, x, y, z).color(red, green, blue, alpha);
                buffer.vertex(matrix, x1, y1, z1).color(red, green, blue, alpha);
            }
        );
        GL11.glDepthFunc(515);
        RenderSystem.disableBlend();
        GL11.glDisable(2848);
        GL11.glDisable(32925);
        matrices.pop();
    }

    private static void genericAABBRender(
        DrawMode mode,
        VertexFormat format,
        ShaderProgramKey shader,
        Matrix4f stack,
        Vec3d start,
        Vec3d dimensions,
        Color color,
        RenderUtils.RenderAction action
    ) {
        float red = (float)color.getRed() / 255.0F;
        float green = (float)color.getGreen() / 255.0F;
        float blue = (float)color.getBlue() / 255.0F;
        float alpha = (float)color.getAlpha() / 255.0F;
        Vec3d end = start.add(dimensions);
        float x1 = (float)start.x;
        float y1 = (float)start.y;
        float z1 = (float)start.z;
        float x2 = (float)end.x;
        float y2 = (float)end.y;
        float z2 = (float)end.z;
        useBuffer(mode, format, shader, bufferBuilder -> action.run(bufferBuilder, x1, y1, z1, x2, y2, z2, red, green, blue, alpha, stack));
    }

    private static void useBuffer(DrawMode mode, VertexFormat format, ShaderProgramKey shader, Consumer<BufferBuilder> runner) {
        Tessellator t = Tessellator.getInstance();
        BufferBuilder bb = t.begin(mode, format);
        runner.accept(bb);
        setup();
        RenderSystem.setShader(shader);
        BufferRenderer.drawWithGlobalProgram(bb.end());
        cleanup();
    }

    interface RenderAction {
        void run(
            BufferBuilder var1,
            float var2,
            float var3,
            float var4,
            float var5,
            float var6,
            float var7,
            float var8,
            float var9,
            float var10,
            float var11,
            Matrix4f var12
        );
    }
}
