package dev.photosync.mcapi.capture;

import dev.photosync.core.capture.CaptureOrigin;
import dev.photosync.core.capture.CapturedScreenshot;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;

/**
 * Where a screenshot the player took is announced.
 *
 * <p>This is the mod's one singleton, and it exists for a specific reason: the
 * thing that knows a screenshot was saved is a mixin, and a mixin is woven into
 * a Minecraft class that nobody constructs, so it has nowhere to be handed a
 * collaborator. A static rendezvous point is the only option, and confining it
 * to this one class is what stops that fact from spreading.
 *
 * <p>Every platform module's {@code Screenshot} mixin calls {@link #published}
 * with the same two arguments, which is why the mixins stay a handful of lines
 * each no matter how the surrounding Minecraft code is reshaped.
 *
 * <p>Automatic captures do not come through here -- they are published by
 * whoever called {@link ScreenshotService#capture}, which already knows their
 * origin.
 */
@Slf4j
public final class ScreenshotBus {

    private static final ScreenshotBus INSTANCE = new ScreenshotBus();

    private final List<Consumer<CapturedScreenshot>> listeners = new CopyOnWriteArrayList<>();

    private ScreenshotBus() {
    }

    public static ScreenshotBus get() {
        return INSTANCE;
    }

    public void subscribe(Consumer<CapturedScreenshot> listener) {
        listeners.add(listener);
    }

    /**
     * Announces a saved screenshot. Called from the IO thread that wrote it, so
     * subscribers must be thread-safe.
     *
     * <p>Swallows failures on purpose: a screenshot the mod could not stat is
     * still a screenshot the player successfully took, and throwing back into a
     * mixin would turn a sync problem into a vanilla one.
     */
    public void published(Path file, CaptureOrigin origin) {
        CapturedScreenshot shot;
        try {
            shot = CapturedScreenshot.of(file, origin);
        } catch (IOException e) {
            log.warn("Ignoring a screenshot that could not be read back: {}", file, e);
            return;
        }
        for (Consumer<CapturedScreenshot> listener : listeners) {
            try {
                listener.accept(shot);
            } catch (RuntimeException e) {
                log.error("A screenshot listener failed for {}", file, e);
            }
        }
    }
}
