// sprites-loader.js

// Chemin relatif du fichier JSON d'index contenant tous les sprites à charger
const INDEX_PATH = './library/loader/tiny_swords.json';

// Objet global qui contiendra toute l'API de sprites (et le statut de chargement)
const Sprites = {
    isLoaded: false,   // Indique si toutes les images/sprites sont chargées
    preloadAll,        // Fonction principale pour tout charger et initialiser
};

// Fonction asynchrone pour charger tous les sprites et images
async function preloadAll() {
    // 1. Charge le fichier JSON d'index (un unique tableau de tous les sprites)
    const res = await fetch(INDEX_PATH);       // Charge le fichier JSON depuis le serveur
    const data = await res.json();             // Parse le contenu JSON en objet JS
    const allSprites = Array.isArray(data.sprites) ? data.sprites : []; // Récupère le tableau de sprites

    // 2. Précharge toutes les images utilisées par les sprites
    const images = {};         // Dictionnaire : chemin image → objet Image HTML
    const imagePromises = [];  // Tableau de promesses pour attendre le chargement de chaque image

    for (const sprite of allSprites) {
        // On charge chaque image seulement une fois (même si utilisée par plusieurs sprites)
        if (!images[sprite.image]) {
            images[sprite.image] = new window.Image();                  // Crée un nouvel objet Image HTML
            images[sprite.image].src = "library/" + sprite.image;       // Définit le chemin source de l'image
            // On crée une promesse qui sera résolue quand l'image sera chargée
            imagePromises.push(new Promise(resolve => {
                images[sprite.image].onload = () => resolve();          // Résout la promesse à la fin du chargement
            }));
        }
    }
    // Attend que toutes les images soient chargées avant de continuer
    await Promise.all(imagePromises);

    // 3. Génère dynamiquement l'arborescence d'accès aux sprites via l'objet Sprites
    for (const sprite of allSprites) {
        const category = sprite.category || 'uncategorized';      // Catégorie du sprite (ex : 'faction', 'terrain')
        if (!Sprites[category]) Sprites[category] = {};           // Crée la catégorie si elle n'existe pas déjà

        // Clé unique du sprite dans sa catégorie (par défaut, on prend sprite.name)
        const key = sprite.name || (sprite.class ? `${sprite.class}_${sprite.color || ''}` : 'unknown');
        if (!Sprites[category][key]) Sprites[category][key] = {};

        // === Si c'est un sprite animé (plusieurs frames pour chaque animation) ===
        if (sprite.type === 'animated') {
            for (const anim of sprite.animations) {
                // Pour chaque animation, on génère une fonction qui gère l'affichage et l'animation
                Sprites[category][key][anim.name] = makeAnimationHelper(
                    images[sprite.image],
                    sprite,
                    anim,
                    sprite.size
                );
            }
            // Pour permettre l'accès direct aux frames brutes si besoin
            Sprites[category][key]._frames = sprite.frames;
        }
        // === Si c'est un sprite statique (une ou plusieurs frames fixes, sans animation) ===
        else if (sprite.type === 'static') {
            for (const frame of sprite.frames) {
                // Pour chaque frame nommée, on génère une fonction d'affichage dédiée
                Sprites[category][key][frame.name] = makeStaticHelper(
                    images[sprite.image],
                    frame,
                    sprite.size
                );
            }
            // Fonction drawDefault pour dessiner la ou les frames par défaut (par ex pour composer un objet à partir de plusieurs frames)
            Sprites[category][key].drawDefault = (ctx, x, y, opts = {}) => {
                // frames = liste des frames à afficher pour ce sprite
                const frames = sprite.default.frames || [sprite.default.frame];
                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;
        }
    }
    // On indique que tout est chargé et prêt
    Sprites.isLoaded = true;
}

// Helper pour créer une fonction d'affichage/animation pour un sprite animé
function makeAnimationHelper(img, sprite, anim, size) {
    // Sélectionne les objets frame correspondant à cette animation
    const frameObjs = anim.frames.map(fname => sprite.frames.find(f => f.name === fname));
    let frameIdx = 0, lastTime = 0;    // Index de la frame courante et timestamp du dernier changement de frame
    let lastDrawAnimName = '';         // Utilisé pour réinitialiser l'animation si on change d'animation

    // Fonction retournée (sera appelée à chaque frame par l'utilisateur)
    return function draw(ctx, x, y, opts = {}) {
        // Si on change d'animation, on repart de la première frame
        if (draw !== lastDrawAnimName) {
            frameIdx = 0;
            lastTime = performance.now();
            lastDrawAnimName = draw;
        }
        // Gère la cadence d'animation (par défaut 12 images/seconde)
        const fps = opts.fps || 12;
        const now = performance.now();
        if (now - lastTime > 1000 / fps) {
            frameIdx = (frameIdx + 1) % frameObjs.length;    // Passe à la frame suivante
            lastTime = now;
        }
        const f = frameObjs[frameIdx];                       // Récupère la frame à afficher
        ctx.save();                                          // Sauve l'état du contexte
        // Gère les flips horizontaux/verticaux si demandé (flipX/flipY)
        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,              // Source dans l'image
                0, 0, opts.w || size.w, opts.h || size.h   // Destination sur le canvas
            );
        } else {
            // Affiche normalement la frame à la position demandée
            ctx.drawImage(
                img,
                f.x, f.y, size.w, size.h,
                x, y, opts.w || size.w, opts.h || size.h
            );
        }
        ctx.restore();    // Restaure l'état du contexte
    };
}

// Helper pour créer une fonction d'affichage pour une frame statique (non animée)
function makeStaticHelper(img, frame, size) {
    // Fonction retournée, à appeler pour afficher la frame
    return function draw(ctx, x, y, opts = {}) {
        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();
    };
}

// Export en module ES ou global window (permet d'utiliser Sprites dans tous les contextes)
if (typeof window !== "undefined") window.Sprites = Sprites;
export default Sprites;
