package dev.photosync.platform.impl;

import dev.photosync.mcapi.render.RenderBridge;
import dev.photosync.mcapi.render.TextureHandle;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.renderer.RenderPipelines;

/**
 * The eleven drawing primitives, on top of {@link GuiGraphicsExtractor}.
 *
 * <p>One of these is built per frame and thrown away; it holds the frame's
 * extractor, which is not valid outside the call that produced it. Coordinates
 * are GUI-space, matching vanilla's, so nothing here scales.
 *
 * <p>26.1 split GUI rendering in two: the old {@code GuiGraphics} calls now
 * record draw commands into a render state that the GPU consumes later. The
 * class was renamed to match, and {@code drawString} to {@code text}, but every
 * primitive the bridge needs survived the split with its arguments intact --
 * which is the whole reason the bridge was drawn at this level.
 */
public final class RenderAdapter implements RenderBridge {

    private final GuiGraphicsExtractor graphics;
    private final Font font;
    private final int width;
    private final int height;
    private final float tickDelta;

    public RenderAdapter(GuiGraphicsExtractor graphics, int width, int height, float tickDelta) {
        this.graphics = graphics;
        this.font = Minecraft.getInstance().font;
        this.width = width;
        this.height = height;
        this.tickDelta = tickDelta;
    }

    @Override
    public int width() {
        return width;
    }

    @Override
    public int height() {
        return height;
    }

    @Override
    public float tickDelta() {
        return tickDelta;
    }

    @Override
    public void fill(int x, int y, int width, int height, int argb) {
        graphics.fill(x, y, x + width, y + height, argb);
    }

    @Override
    public void gradient(int x, int y, int width, int height, int topArgb, int bottomArgb) {
        graphics.fillGradient(x, y, x + width, y + height, topArgb, bottomArgb);
    }

    @Override
    public void border(int x, int y, int width, int height, int argb) {
        graphics.fill(x, y, x + width, y + 1, argb);
        graphics.fill(x, y + height - 1, x + width, y + height, argb);
        graphics.fill(x, y + 1, x + 1, y + height - 1, argb);
        graphics.fill(x + width - 1, y + 1, x + width, y + height - 1, argb);
    }

    @Override
    public void text(String text, int x, int y, int argb, boolean shadow) {
        graphics.text(font, text, x, y, argb, shadow);
    }

    @Override
    public int textWidth(String text) {
        return font.width(text);
    }

    @Override
    public int lineHeight() {
        return font.lineHeight;
    }

    @Override
    public void image(TextureHandle texture, int x, int y, int width, int height) {
        image(texture, x, y, width, height, 0f, 0f, 1f, 1f);
    }

    /**
     * 1.21.2 moved the destination size ahead of the source rectangle, and 1.21.6
     * swapped the render-type lookup in front of it for a baked pipeline. The UVs
     * are still texels, so the handle's own dimensions still do the conversion.
     *
     * <p>There is a normalised-UV overload as of 1.21.6 that would suit the
     * bridge better on paper, but its inner argument order differs from the
     * texel form's in ways that are easy to get subtly wrong; staying on the
     * explicit form keeps this method identical to the four buckets below.
     */
    @Override
    public void image(TextureHandle texture, int x, int y, int width, int height,
                      float u0, float v0, float u1, float v1) {
        TextureAdapter.Handle handle = (TextureAdapter.Handle) texture;
        int textureWidth = handle.width();
        int textureHeight = handle.height();
        graphics.blit(
                RenderPipelines.GUI_TEXTURED,
                handle.id(),
                x, y,
                u0 * textureWidth, v0 * textureHeight,
                width, height,
                Math.max(1, Math.round((u1 - u0) * textureWidth)),
                Math.max(1, Math.round((v1 - v0) * textureHeight)),
                textureWidth, textureHeight);
    }

    @Override
    public void pushClip(int x, int y, int width, int height) {
        graphics.enableScissor(x, y, x + width, y + height);
    }

    @Override
    public void popClip() {
        graphics.disableScissor();
    }
}
