// sprites-loader.ts

// Type pour une frame individuelle (valide pour static et animated)
import {SpriteFrame} from "../_types/SpriteFrame";
// Type pour une animation (animated uniquement)
import {SpriteAnimation} from "../_types/SpriteAnimation";
// Sprite animé
import {AnimatedSprite} from "../_types/AnimatedSprite";
// Sprite statique
import {StaticSprite} from "../_types/StaticSprite";
// Le type général pour un sprite
import {SpritesAPI} from "../_types/SpritesAPI";

export type Sprite = AnimatedSprite | StaticSprite;

// Chemin du fichier index (à adapter à ton arborescence)
const INDEX_PATH = './library/loader/tiny_swords.json';

// L’objet global exporté
const Sprites: SpritesAPI = {
    isLoaded: false,
    preloadAll,
};

async function preloadAll() {
    // 1. Charge le JSON contenant { sprites: [...] }
    const res = await fetch(INDEX_PATH);
    const data = await res.json();
    const allSprites: Sprite[] = Array.isArray(data.sprites) ? data.sprites : [];

    // 2. Précharge toutes les images nécessaires
    const images: Record<string, HTMLImageElement> = {};
    const imagePromises: Promise<void>[] = [];
    for (const sprite of allSprites) {
        if (!images[sprite.image]) {
            images[sprite.image] = new window.Image();
            images[sprite.image].src = "library/" + sprite.image;
            imagePromises.push(new Promise(resolve => {
                images[sprite.image].onload = () => resolve();
            }));
        }
    }
    await Promise.all(imagePromises);

    // 3. Génère l’arborescence Sprites[catégorie][nom][animation ou frame]
    for (const sprite of allSprites) {
        const category = sprite.category || 'uncategorized';
        if (!Sprites[category]) Sprites[category] = {};
        const key = sprite.name || (sprite.class ? `${sprite.class}_${sprite.color || ''}` : 'unknown');
        if (!Sprites[category][key]) Sprites[category][key] = {};

        if (sprite.type === 'animated') {
            for (const anim of (sprite as AnimatedSprite).animations) {
                Sprites[category][key][anim.name] = makeAnimationHelper(
                    images[sprite.image],
                    sprite as AnimatedSprite,
                    anim,
                    sprite.size
                );
            }
            Sprites[category][key]._frames = sprite.frames;
        } else if (sprite.type === 'static') {
            for (const frame of sprite.frames) {
                Sprites[category][key][frame.name] = makeStaticHelper(
                    images[sprite.image],
                    frame,
                    sprite.size
                );
            }
            Sprites[category][key].drawDefault = (ctx: CanvasRenderingContext2D, x: number, y: number, opts: any = {}) => {
                const frames = (sprite.default.frames || [sprite.default.frame]) as string[];
                for (const fname of frames) {
                    const f = sprite.frames.find(f => f.name === fname);
                    if (f) makeStaticHelper(images[sprite.image], f, sprite.size)(ctx, x, y, opts);
                }
            };
            Sprites[category][key]._frames = sprite.frames;
        }
    }
    Sprites.isLoaded = true;
}

// Fabrique un helper d’animation qui retient l’index de frame, etc.
function makeAnimationHelper(
    img: HTMLImageElement,
    sprite: AnimatedSprite,
    anim: SpriteAnimation,
    size: { w: number; h: number }
) {
    const frameObjs = anim.frames.map(fname => sprite.frames.find(f => f.name === fname)!);
    let frameIdx = 0, lastTime = 0;
    let lastDrawAnimName: any = '';
    return function draw(
        ctx: CanvasRenderingContext2D,
        x: number,
        y: number,
        opts: { w?: number; h?: number; fps?: number; flipX?: boolean; flipY?: boolean } = {}
    ) {
        if (draw !== lastDrawAnimName) {
            frameIdx = 0;
            lastTime = performance.now();
            lastDrawAnimName = draw;
        }
        const fps = opts.fps || 12;
        const now = performance.now();
        if (now - lastTime > 1000 / fps) {
            frameIdx = (frameIdx + 1) % frameObjs.length;
            lastTime = now;
        }
        const f = frameObjs[frameIdx];
        ctx.save();
        if (opts.flipX || opts.flipY) {
            ctx.translate(
                x + (opts.flipX ? (opts.w || size.w) : 0),
                y + (opts.flipY ? (opts.h || size.h) : 0)
            );
            ctx.scale(opts.flipX ? -1 : 1, opts.flipY ? -1 : 1);
            ctx.drawImage(
                img,
                f.x, f.y, size.w, size.h,
                0, 0, opts.w || size.w, opts.h || size.h
            );
        } else {
            ctx.drawImage(
                img,
                f.x, f.y, size.w, size.h,
                x, y, opts.w || size.w, opts.h || size.h
            );
        }
        ctx.restore();
    };
}

// Helper statique pour une frame "fixe"
function makeStaticHelper(
    img: HTMLImageElement,
    frame: SpriteFrame,
    size: { w: number; h: number }
) {
    return function draw(
        ctx: CanvasRenderingContext2D,
        x: number,
        y: number,
        opts: { w?: number; h?: number; flipX?: boolean; flipY?: boolean } = {}
    ) {
        ctx.save();
        if (opts.flipX || opts.flipY) {
            ctx.translate(
                x + (opts.flipX ? (opts.w || size.w) : 0),
                y + (opts.flipY ? (opts.h || size.h) : 0)
            );
            ctx.scale(opts.flipX ? -1 : 1, opts.flipY ? -1 : 1);
            ctx.drawImage(
                img,
                frame.x, frame.y, size.w, size.h,
                0, 0, opts.w || size.w, opts.h || size.h
            );
        } else {
            ctx.drawImage(
                img,
                frame.x, frame.y, size.w, size.h,
                x, y, opts.w || size.w, opts.h || size.h
            );
        }
        ctx.restore();
    };
}

// Pour accès direct dans le navigateur
if (typeof window !== "undefined") (window as any).Sprites = Sprites;
export default Sprites;