// Bounded outbound audio credit buffer.
//
// The capture side may produce a frame every 100ms while a Worker response is
// delayed.  At most one unsent frame is retained; a newer frame replaces it and
// the duration of the replaced frame is accounted as dropped audio.
export class AudioCreditBuffer {
    maxInFlight;
    pendingFrame;
    inFlight = 0;
    droppedMs = 0;
    constructor(maxInFlight = 3) {
        if (!Number.isInteger(maxInFlight) || maxInFlight <= 0)
            throw new Error("invalid_audio_credit_capacity");
        this.maxInFlight = maxInFlight;
    }
    offer(frame) {
        if (!Number.isFinite(frame.durationMs) || frame.durationMs < 0)
            throw new Error("invalid_audio_frame_duration");
        if (this.pendingFrame)
            this.droppedMs += this.pendingFrame.durationMs;
        this.pendingFrame = frame;
    }
    acquire() {
        if (this.inFlight >= this.maxInFlight || !this.pendingFrame)
            return undefined;
        const frame = this.pendingFrame;
        this.pendingFrame = undefined;
        this.inFlight += 1;
        return frame;
    }
    complete() {
        if (this.inFlight > 0)
            this.inFlight -= 1;
    }
    takeDroppedAudioMs() {
        const dropped = this.droppedMs;
        this.droppedMs = 0;
        return dropped;
    }
    clearPending() {
        if (this.pendingFrame)
            this.droppedMs += this.pendingFrame.durationMs;
        this.pendingFrame = undefined;
    }
    clear() {
        this.clearPending();
        this.inFlight = 0;
    }
    get pending() { return this.pendingFrame !== undefined; }
    get inFlightCount() { return this.inFlight; }
    get capacity() { return this.maxInFlight; }
}
