/**
 * Incremental parser for the binary output stream of screen-streamer.swift.
 *
 * Frame format:
 *   1. Header: magic bytes "RDSM" + a JSON line (\n) with ScreenInfo[].
 *   2. Video frames: [4B length BE] [1B flags] [H.264 NAL data]
 */
import { logger } from "../../logger.js";
import { STREAMER_MAGIC } from "./protocol.js";
export class StreamerParser {
    onScreenInfo;
    onVideoFrame;
    buf = Buffer.alloc(0);
    headerReceived = false;
    constructor(onScreenInfo, onVideoFrame) {
        this.onScreenInfo = onScreenInfo;
        this.onVideoFrame = onVideoFrame;
    }
    /** Reset parser state (e.g. when the streamer process restarts). */
    reset() {
        this.buf = Buffer.alloc(0);
        this.headerReceived = false;
    }
    /** Feed a raw chunk from the streamer's stdout. */
    feed(chunk) {
        this.buf = Buffer.concat([this.buf, chunk]);
        this.parse();
    }
    parse() {
        // Phase 1: parse the RDSM magic + JSON header.
        if (!this.headerReceived) {
            const magic = STREAMER_MAGIC;
            if (this.buf.length < magic.length)
                return;
            const header = this.buf.subarray(0, magic.length).toString();
            if (header !== magic) {
                logger.err(`[streamer-parser] Invalid magic: "${header}"`);
                return;
            }
            const nlIdx = this.buf.indexOf(0x0a, magic.length);
            if (nlIdx === -1)
                return; // wait for full header line
            const jsonStr = this.buf.subarray(magic.length, nlIdx).toString();
            this.buf = this.buf.subarray(nlIdx + 1);
            this.headerReceived = true;
            try {
                const screens = JSON.parse(jsonStr);
                logger.debug(`[streamer-parser] Screen info: ${screens.length} display(s)`);
                this.onScreenInfo(screens);
            }
            catch (err) {
                logger.err(`[streamer-parser] Failed to parse screen info: ${err instanceof Error ? err.message : String(err)}`);
            }
        }
        // Phase 2: parse length-prefixed video frames.
        while (this.buf.length >= 4) {
            const frameLen = this.buf.readUInt32BE(0);
            if (this.buf.length < 4 + frameLen)
                break;
            const frameData = this.buf.subarray(4, 4 + frameLen);
            this.buf = this.buf.subarray(4 + frameLen);
            this.onVideoFrame(frameData);
        }
    }
}
//# sourceMappingURL=streamer-parser.js.map