/**
 * Voice overlay component for the TUI.
 * Shows voice status: enrolling, listening, speech detection, transcription.
 */
import { Container, Spacer, Text } from "@dotkaio/avis-tui";
import { theme } from "../theme/theme.js";
import { DynamicBorder } from "./dynamic-border.js";
export class VoiceOverlayComponent extends Container {
    constructor(config) {
        super();
        this.animFrame = 0;
        this.animInterval = null;
        this.lastEscTime = 0;
        this.config = config;
        this.addChild(new DynamicBorder());
        this.addChild(new Spacer(1));
        // Title
        this.addChild(new Text(theme.bold(theme.fg("accent", "  Voice Mode")), 0, 0));
        this.addChild(new Spacer(1));
        // State indicator
        this.stateText = new Text("", 0, 0);
        this.addChild(this.stateText);
        // Detail line
        this.detailText = new Text("", 0, 0);
        this.addChild(this.detailText);
        // Timing/status line
        this.timingText = new Text("", 0, 0);
        this.addChild(this.timingText);
        this.addChild(new Spacer(1));
        // Hint
        this.hintText = new Text("", 0, 0);
        this.addChild(this.hintText);
        this.addChild(new Spacer(1));
        this.addChild(new DynamicBorder());
        this.updateDisplay();
    }
    updateState(config) {
        Object.assign(this.config, config);
        this.updateDisplay();
    }
    startAnimation(requestRender) {
        if (this.animInterval)
            return;
        this.animInterval = setInterval(() => {
            this.animFrame = (this.animFrame + 1) % 4;
            this.updateDisplay();
            requestRender();
        }, 300);
    }
    stopAnimation() {
        if (this.animInterval) {
            clearInterval(this.animInterval);
            this.animInterval = null;
        }
    }
    getWaveform() {
        const frames = ["  ░▒▓█▓▒░  ", "  ▒▓█▓█▓▒  ", "  ▓█▓▒▓█▓  ", "  █▓▒░▒▓█  "];
        return frames[this.animFrame % frames.length];
    }
    getDots() {
        return ".".repeat((this.animFrame % 3) + 1).padEnd(3);
    }
    updateDisplay() {
        const { state, errorMessage, enrollDuration, statusText } = this.config;
        // Show timing info if available
        this.timingText.setText(statusText ? theme.fg("dim", `  ${statusText}`) : "");
        switch (state) {
            case "installing":
                this.stateText.setText(theme.fg("warning", `  Installing voice dependencies${this.getDots()}`));
                this.detailText.setText(theme.fg("muted", "  This only happens once."));
                this.hintText.setText(theme.fg("dim", "  Esc twice to cancel"));
                break;
            case "initializing":
                this.stateText.setText(theme.fg("muted", `  Starting voice engine${this.getDots()}`));
                this.detailText.setText("");
                this.hintText.setText(theme.fg("dim", "  Esc twice to cancel"));
                break;
            case "enrolling":
                this.stateText.setText(theme.fg("warning", "  Voice Enrollment"));
                this.detailText.setText(theme.fg("accent", `  Speak naturally for ${enrollDuration ?? 10} seconds to create your voice profile.\n` +
                    "  Read something aloud or describe your day."));
                this.hintText.setText(theme.fg("dim", "  Press Enter to start recording · Esc to cancel"));
                break;
            case "recording":
                this.stateText.setText(theme.fg("error", `  ${this.getWaveform()} Recording${this.getDots()}`));
                this.detailText.setText(theme.fg("muted", `  Recording your voice profile (${enrollDuration ?? 10}s)`));
                this.hintText.setText(theme.fg("dim", "  Keep speaking naturally..."));
                break;
            case "idle":
                this.stateText.setText(theme.fg("success", "  Voice profile loaded"));
                this.detailText.setText(theme.fg("muted", "  Ready to listen. Only your voice will be recognized."));
                this.hintText.setText(theme.fg("dim", "  Enter to start listening · Esc twice to exit voice mode"));
                break;
            case "listening":
                this.stateText.setText(theme.fg("accent", `  ${this.getWaveform()} Listening${this.getDots()}`));
                this.detailText.setText(theme.fg("muted", "  Waiting for your voice..."));
                this.hintText.setText(theme.fg("dim", "  Speak to send a message · Esc twice to stop"));
                break;
            case "speech_detected":
                this.stateText.setText(theme.fg("success", `  ${this.getWaveform()} Speech detected`));
                this.detailText.setText(theme.fg("muted", "  Listening..."));
                this.hintText.setText(theme.fg("dim", "  Pause speaking to send · Esc twice to cancel"));
                break;
            case "transcribing":
                this.stateText.setText(theme.fg("warning", `  Transcribing${this.getDots()}`));
                this.detailText.setText("");
                this.hintText.setText("");
                break;
            case "processing":
                this.stateText.setText(theme.fg("accent", `  ${this.getDots()} Thinking${this.getDots()}`));
                this.detailText.setText(theme.fg("muted", "  Avis is working on your request..."));
                this.hintText.setText(theme.fg("dim", "  Esc to interrupt · Esc twice to exit voice mode"));
                break;
            case "speaking":
                this.stateText.setText(theme.fg("accent", `  ${this.getWaveform()} Speaking${this.getDots()}`));
                this.detailText.setText(theme.fg("muted", "  Avis is responding..."));
                this.hintText.setText(theme.fg("dim", "  Esc to stop · Esc twice to exit voice mode"));
                break;
            case "error":
                this.stateText.setText(theme.fg("error", "  Error"));
                this.detailText.setText(theme.fg("error", `  ${errorMessage ?? "Unknown error"}`));
                this.hintText.setText(theme.fg("dim", "  Esc twice to exit voice mode"));
                break;
        }
    }
    handleInput(data) {
        // Esc handling: single Esc stops speaking/interrupts agent, double Esc exits voice mode
        if (data === "\x1b" || data === "\x1b\x1b") {
            const now = Date.now();
            const isDouble = now - this.lastEscTime < 500;
            if (isDouble) {
                // Double Esc: always exit voice mode
                this.lastEscTime = 0;
                this.stopAnimation();
                this.onCancel?.();
            }
            else {
                this.lastEscTime = now;
                // Single Esc: if speaking or processing, stop/interrupt first
                if (this.config.state === "speaking" || this.config.state === "processing") {
                    this.onStopSpeaking?.();
                }
                else {
                    this.onEscHint?.();
                }
            }
            return;
        }
        // Ctrl+C - forward to cancel so the app can handle shutdown
        if (data === "\x03") {
            this.stopAnimation();
            this.onCtrlC?.();
            return;
        }
        // Ctrl+Z - forward to suspend handler
        if (data === "\x1a") {
            this.stopAnimation();
            this.onCtrlZ?.();
            return;
        }
        // Enter triggers action based on state
        if (data === "\r" || data === "\n") {
            this.onEnter?.();
        }
    }
    dispose() {
        this.stopAnimation();
    }
}
//# sourceMappingURL=voice-overlay.js.map