export class LineTimeSliceBuffer {
    static DEFAULT_MIN_TIMED_FLUSH_CHARS = 24;
    static DEFAULT_MAX_TIMED_BUFFER_MS = 1500;
    flushIntervalMs;
    onTimedFlush;
    minTimedFlushChars;
    maxTimedBufferMs;
    buffers = new Map();
    timers = new Map();
    pendingSince = new Map();
    constructor(options) {
        this.flushIntervalMs = options.flushIntervalMs;
        this.onTimedFlush = options.onTimedFlush;
        this.minTimedFlushChars = options.minTimedFlushChars ?? LineTimeSliceBuffer.DEFAULT_MIN_TIMED_FLUSH_CHARS;
        this.maxTimedBufferMs = options.maxTimedBufferMs ?? LineTimeSliceBuffer.DEFAULT_MAX_TIMED_BUFFER_MS;
    }
    addKey(key) {
        if (!this.buffers.has(key)) {
            this.buffers.set(key, '');
        }
    }
    push(key, text) {
        this.addKey(key);
        const buffer = this.buffers.get(key) ?? '';
        const combined = buffer + text;
        const parts = combined.split('\n');
        const remainder = parts.pop() ?? '';
        this.buffers.set(key, remainder);
        if (remainder === '') {
            this.pendingSince.delete(key);
            this.clearTimer(key);
        }
        else {
            if (!this.pendingSince.has(key)) {
                this.pendingSince.set(key, Date.now());
            }
            this.scheduleTimer(key);
        }
        return parts;
    }
    flushKey(key) {
        this.clearTimer(key);
        this.pendingSince.delete(key);
        const buffer = this.buffers.get(key) ?? '';
        if (buffer === '') {
            return undefined;
        }
        this.buffers.set(key, '');
        return buffer;
    }
    flushAll() {
        const result = [];
        for (const key of this.buffers.keys()) {
            const text = this.flushKey(key);
            if (text !== undefined) {
                result.push({ key, text });
            }
        }
        return result;
    }
    scheduleTimer(key) {
        this.clearTimer(key);
        const timer = setTimeout(() => {
            this.timers.delete(key);
            const text = this.flushTimedKey(key);
            if (text !== undefined) {
                this.onTimedFlush(key, text);
            }
        }, this.flushIntervalMs);
        this.timers.set(key, timer);
    }
    flushTimedKey(key) {
        const buffer = this.buffers.get(key) ?? '';
        if (buffer === '') {
            this.pendingSince.delete(key);
            return undefined;
        }
        const startedAt = this.pendingSince.get(key) ?? Date.now();
        const elapsed = Date.now() - startedAt;
        const canForceFlush = elapsed >= this.maxTimedBufferMs;
        if (!canForceFlush && buffer.length < this.minTimedFlushChars) {
            this.scheduleTimer(key);
            return undefined;
        }
        const minBoundaryIndex = Math.max(this.minTimedFlushChars - 1, 1);
        const boundaryIndex = this.findBoundaryIndex(buffer, minBoundaryIndex);
        const flushIndex = boundaryIndex >= 0
            ? boundaryIndex + 1
            : buffer.length;
        if (!canForceFlush && buffer.length > 0 && this.isBoundary(buffer.charAt(0)) && boundaryIndex < 0) {
            this.scheduleTimer(key);
            return undefined;
        }
        const flushText = buffer.slice(0, flushIndex);
        const remainder = buffer.slice(flushIndex);
        this.buffers.set(key, remainder);
        if (remainder === '') {
            this.pendingSince.delete(key);
        }
        else {
            this.scheduleTimer(key);
        }
        return flushText;
    }
    findBoundaryIndex(text, minIndex) {
        let lastIndex = -1;
        const start = Math.min(Math.max(minIndex, 0), text.length - 1);
        for (let i = start; i < text.length; i += 1) {
            const ch = text.charAt(i);
            if (this.isBoundary(ch)) {
                lastIndex = i;
            }
        }
        return lastIndex;
    }
    isBoundary(ch) {
        const boundaryChars = new Set([
            ' ',
            '\n',
            '\t',
            ',',
            '.',
            '!',
            '?',
            ';',
            ':',
            '、',
            '。',
            '！',
            '？',
            '；',
            '：',
            '（',
            '）',
            '[',
            ']',
            '{',
            '}',
        ]);
        return boundaryChars.has(ch);
    }
    clearTimer(key) {
        const timer = this.timers.get(key);
        if (!timer) {
            return;
        }
        clearTimeout(timer);
        this.timers.delete(key);
    }
}
//# sourceMappingURL=stream-buffer.js.map