// Rôle unique de ce fichier : ouvrir la caméra et fournir des pixels à C#.
// AUCUN décodage ici — il est fait par ZXing.Net côté C# (décision actée dans CLAUDE.md).
// getUserMedia et canvas/getImageData n'ont pas d'équivalent C#, d'où ces quelques lignes.

let flux = null;
let video = null;
let canvas = null;
let ctx = null;
let derniersPixels = null;

// getUserMedia exige un contexte sécurisé : HTTPS, ou localhost.
// Une IP de réseau local (192.168.x.x) en http NE l'est PAS — piège classique en dev.
export function diagnostic() {
    if (!window.isSecureContext) return 'contexte-non-securise';
    if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) return 'api-absente';
    return 'ok';
}

// Renvoie un code de statut plutôt que de laisser remonter l'exception : une DOMException
// traversée par le pont JS→C# perd son `name` (mesuré : C# ne reçoit que « Permission denied
// undefined »), or c'est précisément ce nom qui dit à l'utilisateur ce qui lui manque.
export async function demarrer(element) {
    video = element;
    try {
        // facingMode "environment" = caméra arrière : on scanne un livre tenu devant soi.
        // "ideal" et non "exact" pour ne pas échouer sur un PC qui n'a qu'une webcam frontale.
        flux = await navigator.mediaDevices.getUserMedia({
            video: { facingMode: { ideal: 'environment' }, width: { ideal: 1280 }, height: { ideal: 720 } },
            audio: false,
        });
    } catch (e) {
        switch (e.name) {
            case 'NotAllowedError':
            case 'SecurityError':
                return 'permission-refusee';
            case 'NotFoundError':
            case 'DevicesNotFoundError':
            case 'OverconstrainedError':
                return 'aucune-camera';
            case 'NotReadableError':
            case 'TrackStartError':
                return 'camera-occupee';
            default:
                return 'erreur:' + (e.name || 'Error') + ' — ' + (e.message || '');
        }
    }
    video.srcObject = flux;
    video.setAttribute('playsinline', '');
    await video.play();
    canvas = document.createElement('canvas');
    ctx = canvas.getContext('2d', { willReadFrequently: true });
    return 'ok';
}

// Capture une frame et la garde en mémoire. Renvoie [largeur, hauteur], ou null si
// la vidéo n'est pas encore prête. Les pixels se récupèrent ensuite via pixels().
export function capturer(largeurMax, partHauteur) {
    if (!video || !ctx || video.readyState < 2 || !video.videoWidth) return null;
    const echelle = Math.min(1, largeurMax / video.videoWidth);
    const l = Math.round(video.videoWidth * echelle);
    const h = Math.round(video.videoHeight * echelle);
    if (canvas.width !== l || canvas.height !== h) { canvas.width = l; canvas.height = h; }
    ctx.drawImage(video, 0, 0, l, h);
    // On ne renvoie que la bande centrale, celle que l'utilisateur vise : moins de pixels
    // à transférer et à décoder, donc moins de travail par frame côté C#.
    const hb = Math.max(1, Math.round(h * partHauteur));
    const y = Math.round((h - hb) / 2);
    derniersPixels = new Uint8Array(ctx.getImageData(0, y, l, hb).data.buffer);
    return [l, hb];
}

export function pixels() {
    return derniersPixels;
}

// Libérer la caméra : sinon le voyant reste allumé et la batterie se vide,
// ce que l'utilisateur perçoit — à raison — comme un dysfonctionnement.
export function arreter() {
    if (flux) { flux.getTracks().forEach(t => t.stop()); flux = null; }
    if (video) { video.srcObject = null; video = null; }
    canvas = null; ctx = null; derniersPixels = null;
}
