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

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.BufferRenderer;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.render.VertexFormat.DrawMode;
import zenith.zov.Zenith;
import zenith.zov.utility.interfaces.IWindow;
import zenith.zov.utility.math.Timer;
import zenith.zov.utility.render.display.shader.impl.KawaseBlurProgram;

public class BlurProgram implements IWindow {
    public static final Supplier<CustomRenderTarget> CACHE = Suppliers.memoize(() -> new CustomRenderTarget(false).setLinear());
    public static final Supplier<CustomRenderTarget> BUFFER = Suppliers.memoize(() -> new CustomRenderTarget(false).setLinear());
    private static KawaseBlurProgram kawaseDownProgram;
    private static KawaseBlurProgram kawaseUpProgram;
    private final Timer timer = new Timer();

    public void initShaders() {
        kawaseDownProgram = new KawaseBlurProgram(Zenith.id("kawase_down/data"));
        kawaseUpProgram = new KawaseBlurProgram(Zenith.id("kawase_up/data"));
    }

    public void draw() {
        if (this.timer.finished(25L)) {
            CustomRenderTarget cache = (CustomRenderTarget)CACHE.get();
            CustomRenderTarget buffer = (CustomRenderTarget)BUFFER.get();
            RenderSystem.enableBlend();
            RenderSystem.defaultBlendFunc();
            kawaseDownProgram.use();
            cache.setup();
            mc.getFramebuffer().beginRead();
            RenderSystem.setShaderTexture(0, mc.getFramebuffer().getColorAttachment());
            this.drawQuad(0.0F, 0.0F, (float)mw.getScaledWidth(), (float)mw.getScaledHeight());
            cache.stop();
            CustomRenderTarget[] buffers = new CustomRenderTarget[]{cache, buffer};
            int steps = 3;

            for (int i = 1; i < 3; i++) {
                int step = i % 2;
                buffers[step].setup();
                buffers[(step + 1) % 2].beginRead();
                RenderSystem.setShaderTexture(0, buffers[(step + 1) % 2].getColorAttachment());
                this.drawQuad(0.0F, 0.0F, (float)mw.getScaledWidth(), (float)mw.getScaledHeight());
                buffers[(step + 1) % 2].endRead();
                buffers[step].stop();
            }

            kawaseUpProgram.use();

            for (int i = 0; i < 3; i++) {
                int step = i % 2;
                buffers[(step + 1) % 2].setup();
                buffers[step].beginRead();
                RenderSystem.setShaderTexture(0, buffers[step].getColorAttachment());
                this.drawQuad(0.0F, 0.0F, (float)mw.getScaledWidth(), (float)mw.getScaledHeight());
                buffers[step].endRead();
                buffers[step].stop();
            }

            mc.getFramebuffer().endRead();
            mc.getFramebuffer().beginWrite(false);
            RenderSystem.setShaderTexture(0, 0);
            RenderSystem.disableBlend();
            this.timer.reset();
        }
    }

    private void drawQuad(float x, float y, float width, float height) {
        int color = -1;
        BufferBuilder builder = RenderSystem.renderThreadTesselator().begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
        builder.vertex(x, y, 0.0F).texture(0.0F, 1.0F).color(color);
        builder.vertex(x, y + height, 0.0F).texture(0.0F, 0.0F).color(color);
        builder.vertex(x + width, y + height, 0.0F).texture(1.0F, 0.0F).color(color);
        builder.vertex(x + width, y, 0.0F).texture(1.0F, 1.0F).color(color);
        BufferRenderer.drawWithGlobalProgram(builder.end());
    }

    public static int getTexture() {
        return ((CustomRenderTarget)BUFFER.get()).getColorAttachment();
    }

    public void setBlurRadius(float blurRadius) {
        kawaseDownProgram.updateUniforms(blurRadius);
        kawaseUpProgram.updateUniforms(blurRadius);
    }
}
