import { HandLandmarker, FilesetResolver } from "@mediapipe/tasks-vision";

type Callback = (isLoaded: boolean) => void;

class HandLandmarkerSingleton {
    private handLandmarker: HandLandmarker | null = null;
    private isLoading = false;
    private isLoaded = false;
    private callbacks: Callback[] = [];
    private loadPromise: Promise<void> | null = null;

    preload = () => {
        if (this.isLoaded || this.isLoading) return this.loadPromise!;
        this.isLoading = true;
        this.loadPromise = (async () => {
            const vision = await FilesetResolver.forVisionTasks("https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision/wasm");
            this.handLandmarker = await HandLandmarker.createFromOptions(vision, {
                baseOptions: {
                    modelAssetPath: `https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task`,
                    delegate: "GPU"
                },
                runningMode: "VIDEO",
                numHands: 1
            });

            this.warmUpHandLandmarker(this.handLandmarker);

            this.isLoaded = true;
            this.isLoading = false;
            this.callbacks.forEach(cb => cb(true));
        })();
        this.callbacks.forEach(cb => cb(false));
        return this.loadPromise;
    };

    getHandLandmarker = () => this.handLandmarker;

    subscribe = (cb: Callback) => {
        this.callbacks.push(cb);
        cb(this.isLoaded);
        return () => {
            this.callbacks = this.callbacks.filter(f => f !== cb);
        };
    };

    private warmUpHandLandmarker(handLandmarker: HandLandmarker) {
        try {
            const video = document.createElement('video');
            Object.defineProperty(video, 'videoWidth', { value: 1280 });
            Object.defineProperty(video, 'videoHeight', { value: 720 });

            handLandmarker.detectForVideo(video, performance.now());
        } catch (e) {
            console.error(e);
        }
    }
}

export const handLandmarkerSingleton = new HandLandmarkerSingleton();