package dev.photosync.platform.impl;

import com.mojang.blaze3d.platform.NativeImage;
import dev.photosync.core.thumbnail.ThumbImage;
import dev.photosync.mcapi.render.TextureHandle;
import dev.photosync.mcapi.render.TextureSink;
import lombok.Getter;
import lombok.experimental.Accessors;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.resources.ResourceLocation;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Turns decoded pixels into something the GPU will draw.
 *
 * <p>Every upload mints its own texture id rather than reusing a slot, because
 * the browser holds many thumbnails alive at once and vanilla's texture manager
 * is the only thing that knows how to free them. The id is opaque; nothing but
 * {@link RenderAdapter} ever looks at it.
 */
public final class TextureAdapter implements TextureSink {

    private final AtomicInteger sequence = new AtomicInteger();

    @Override
    public TextureHandle upload(ThumbImage image) {
        NativeImage pixels = new NativeImage(NativeImage.Format.RGBA, image.width(), image.height(), false);
        int[] argb = image.argb();
        for (int y = 0; y < image.height(); y++) {
            int row = y * image.width();
            for (int x = 0; x < image.width(); x++) {
                pixels.setPixelRGBA(x, y, abgr(argb[row + x]));
            }
        }
        return register(pixels);
    }

    @Override
    public TextureHandle decode(byte[] encoded) throws IOException {
        return register(NativeImage.read(encoded));
    }

    private Handle register(NativeImage pixels) {
        // DynamicTexture takes ownership of the image and closes it with itself,
        // so the only thing left to free is the registration.
        DynamicTexture texture = new DynamicTexture(pixels);
        ResourceLocation id = new ResourceLocation("photosync", "thumb/" + sequence.incrementAndGet());
        Minecraft.getInstance().getTextureManager().register(id, texture);
        return new Handle(id, pixels.getWidth(), pixels.getHeight());
    }

    /**
     * NativeImage stores RGBA in memory order, so the int it wants back is
     * 0xAABBGGRR -- red and blue swapped relative to the ARGB everything else
     * in this mod speaks.
     */
    private static int abgr(int argb) {
        return (argb & 0xFF00FF00) | ((argb >> 16) & 0xFF) | ((argb & 0xFF) << 16);
    }

    /** A registered texture, freed when the browser drops it. */
    public static final class Handle implements TextureHandle {

        @Getter
        @Accessors(fluent = true)
        private final ResourceLocation id;

        private final int width;
        private final int height;
        private boolean released;

        private Handle(ResourceLocation id, int width, int height) {
            this.id = id;
            this.width = width;
            this.height = height;
        }

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

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

        @Override
        public void close() {
            if (released) {
                return;
            }
            released = true;
            Minecraft.getInstance().getTextureManager().release(id);
        }
    }
}
