
import { ShellIntegrationStatus } from '../terminal.js';
import { Disposable, toDisposable, dispose } from '../../../../base/common/lifecycle.js';
import { TerminalCapabilityStore } from '../capabilities/terminalCapabilityStore.js';
import { CommandDetectionCapability } from '../capabilities/commandDetectionCapability.js';
import { CwdDetectionCapability } from '../capabilities/cwdDetectionCapability.js';
import { TerminalCapability } from '../capabilities/capabilities.js';
import { PartialCommandDetectionCapability } from '../capabilities/partialCommandDetectionCapability.js';
import { Emitter } from '../../../../base/common/event.js';
import { BufferMarkCapability } from '../capabilities/bufferMarkCapability.js';
import { URI } from '../../../../base/common/uri.js';
import { sanitizeCwd } from '../terminalEnvironment.js';
import { removeAnsiEscapeCodesFromPrompt } from '../../../../base/common/strings.js';
import { ShellEnvDetectionCapability } from '../capabilities/shellEnvDetectionCapability.js';
import { PromptTypeDetectionCapability } from '../capabilities/promptTypeDetectionCapability.js';

var ShellIntegrationOscPs;
(function(ShellIntegrationOscPs) {
    ShellIntegrationOscPs[ShellIntegrationOscPs["FinalTerm"] = 133] = "FinalTerm";
    ShellIntegrationOscPs[ShellIntegrationOscPs["VSCode"] = 633] = "VSCode";
    ShellIntegrationOscPs[ShellIntegrationOscPs["ITerm"] = 1337] = "ITerm";
    ShellIntegrationOscPs[ShellIntegrationOscPs["SetCwd"] = 7] = "SetCwd";
    ShellIntegrationOscPs[ShellIntegrationOscPs["SetWindowsFriendlyCwd"] = 9] = "SetWindowsFriendlyCwd";
})(ShellIntegrationOscPs || (ShellIntegrationOscPs = {}));
var FinalTermOscPt;
(function(FinalTermOscPt) {
    FinalTermOscPt["PromptStart"] = "A";
    FinalTermOscPt["CommandStart"] = "B";
    FinalTermOscPt["CommandExecuted"] = "C";
    FinalTermOscPt["CommandFinished"] = "D";
})(FinalTermOscPt || (FinalTermOscPt = {}));
var VSCodeOscPt;
(function(VSCodeOscPt) {
    VSCodeOscPt["PromptStart"] = "A";
    VSCodeOscPt["CommandStart"] = "B";
    VSCodeOscPt["CommandExecuted"] = "C";
    VSCodeOscPt["CommandFinished"] = "D";
    VSCodeOscPt["CommandLine"] = "E";
    VSCodeOscPt["ContinuationStart"] = "F";
    VSCodeOscPt["ContinuationEnd"] = "G";
    VSCodeOscPt["RightPromptStart"] = "H";
    VSCodeOscPt["RightPromptEnd"] = "I";
    VSCodeOscPt["Property"] = "P";
    VSCodeOscPt["SetMark"] = "SetMark";
    VSCodeOscPt["EnvJson"] = "EnvJson";
    VSCodeOscPt["EnvSingleDelete"] = "EnvSingleDelete";
    VSCodeOscPt["EnvSingleStart"] = "EnvSingleStart";
    VSCodeOscPt["EnvSingleEntry"] = "EnvSingleEntry";
    VSCodeOscPt["EnvSingleEnd"] = "EnvSingleEnd";
})(VSCodeOscPt || (VSCodeOscPt = {}));
var ITermOscPt;
(function(ITermOscPt) {
    ITermOscPt["SetMark"] = "SetMark";
    ITermOscPt["CurrentDir"] = "CurrentDir";
})(ITermOscPt || (ITermOscPt = {}));
class ShellIntegrationAddon extends Disposable {
    get seenSequences() {
        return this._seenSequences;
    }
    get status() {
        return this._status;
    }
    constructor(
        _nonce,
        _disableTelemetry,
        _onDidExecuteText,
        _telemetryService,
        _logService
    ) {
        super();
        this._nonce = _nonce;
        this._disableTelemetry = _disableTelemetry;
        this._onDidExecuteText = _onDidExecuteText;
        this._telemetryService = _telemetryService;
        this._logService = _logService;
        this.capabilities = this._register(( new TerminalCapabilityStore()));
        this._hasUpdatedTelemetry = false;
        this._commonProtocolDisposables = [];
        this._seenSequences = ( new Set());
        this._status = ShellIntegrationStatus.Off;
        this._onDidChangeStatus = ( new Emitter());
        this.onDidChangeStatus = this._onDidChangeStatus.event;
        this._onDidChangeSeenSequences = ( new Emitter());
        this.onDidChangeSeenSequences = this._onDidChangeSeenSequences.event;
        this._register(toDisposable(() => {
            this._clearActivationTimeout();
            this._disposeCommonProtocol();
        }));
    }
    _disposeCommonProtocol() {
        dispose(this._commonProtocolDisposables);
        this._commonProtocolDisposables.length = 0;
    }
    activate(xterm) {
        this._terminal = xterm;
        this.capabilities.add(TerminalCapability.PartialCommandDetection, this._register(( new PartialCommandDetectionCapability(this._terminal, this._onDidExecuteText))));
        this._register(
            xterm.parser.registerOscHandler(ShellIntegrationOscPs.VSCode, data => this._handleVSCodeSequence(data))
        );
        this._register(
            xterm.parser.registerOscHandler(ShellIntegrationOscPs.ITerm, data => this._doHandleITermSequence(data))
        );
        this._commonProtocolDisposables.push(xterm.parser.registerOscHandler(
            ShellIntegrationOscPs.FinalTerm,
            data => this._handleFinalTermSequence(data)
        ));
        this._register(
            xterm.parser.registerOscHandler(ShellIntegrationOscPs.SetCwd, data => this._doHandleSetCwd(data))
        );
        this._register(xterm.parser.registerOscHandler(
            ShellIntegrationOscPs.SetWindowsFriendlyCwd,
            data => this._doHandleSetWindowsFriendlyCwd(data)
        ));
        this._ensureCapabilitiesOrAddFailureTelemetry();
    }
    getMarkerId(terminal, vscodeMarkerId) {
        this._createOrGetBufferMarkDetection(terminal).getMark(vscodeMarkerId);
    }
    setNextCommandId(command, commandId) {
        if (this._terminal) {
            this._createOrGetCommandDetection(this._terminal).setNextCommandId(command, commandId);
        }
    }
    _markSequenceSeen(sequence) {
        if (!( this._seenSequences.has(sequence))) {
            this._seenSequences.add(sequence);
            this._onDidChangeSeenSequences.fire(this._seenSequences);
        }
    }
    _handleFinalTermSequence(data) {
        const didHandle = this._doHandleFinalTermSequence(data);
        if (this._status === ShellIntegrationStatus.Off) {
            this._status = ShellIntegrationStatus.FinalTerm;
            this._onDidChangeStatus.fire(this._status);
        }
        return didHandle;
    }
    _doHandleFinalTermSequence(data) {
        if (!this._terminal) {
            return false;
        }
        const [command, ...args] = data.split(";");
        this._markSequenceSeen(command);
        switch (command) {
        case FinalTermOscPt.PromptStart:
            this._createOrGetCommandDetection(this._terminal).handlePromptStart();
            return true;
        case FinalTermOscPt.CommandStart:
            this._createOrGetCommandDetection(this._terminal).handleCommandStart({
                ignoreCommandLine: true
            });
            return true;
        case FinalTermOscPt.CommandExecuted:
            this._createOrGetCommandDetection(this._terminal).handleCommandExecuted();
            return true;
        case FinalTermOscPt.CommandFinished:
            {
                const exitCode = args.length === 1 ? parseInt(args[0]) : undefined;
                this._createOrGetCommandDetection(this._terminal).handleCommandFinished(exitCode);
                return true;
            }
        }
        return false;
    }
    _handleVSCodeSequence(data) {
        const didHandle = this._doHandleVSCodeSequence(data);
        if (!this._hasUpdatedTelemetry && didHandle) {
            this._telemetryService?.publicLog2("terminal/shellIntegrationActivationSucceeded");
            this._hasUpdatedTelemetry = true;
            this._clearActivationTimeout();
        }
        if (this._status !== ShellIntegrationStatus.VSCode) {
            this._status = ShellIntegrationStatus.VSCode;
            this._onDidChangeStatus.fire(this._status);
        }
        return didHandle;
    }
    async _ensureCapabilitiesOrAddFailureTelemetry() {
        if (!this._telemetryService || this._disableTelemetry) {
            return;
        }
        this._activationTimeout = setTimeout(() => {
            if (!this.capabilities.get(TerminalCapability.CommandDetection) && !this.capabilities.get(TerminalCapability.CwdDetection)) {
                this._telemetryService?.publicLog2("terminal/shellIntegrationActivationTimeout");
                this._logService.warn("Shell integration failed to add capabilities within 10 seconds");
            }
            this._hasUpdatedTelemetry = true;
        }, 10000);
    }
    _clearActivationTimeout() {
        if (this._activationTimeout !== undefined) {
            clearTimeout(this._activationTimeout);
            this._activationTimeout = undefined;
        }
    }
    _doHandleVSCodeSequence(data) {
        if (!this._terminal) {
            return false;
        }
        const argsIndex = data.indexOf(";");
        const command = argsIndex === -1 ? data : data.substring(0, argsIndex);
        this._markSequenceSeen(command);
        const args = argsIndex === -1 ? [] : data.substring(argsIndex + 1).split(";");
        switch (command) {
        case VSCodeOscPt.PromptStart:
            this._createOrGetCommandDetection(this._terminal).handlePromptStart();
            return true;
        case VSCodeOscPt.CommandStart:
            this._createOrGetCommandDetection(this._terminal).handleCommandStart();
            return true;
        case VSCodeOscPt.CommandExecuted:
            this._createOrGetCommandDetection(this._terminal).handleCommandExecuted();
            return true;
        case VSCodeOscPt.CommandFinished:
            {
                const arg0 = args[0];
                const exitCode = arg0 !== undefined ? parseInt(arg0) : undefined;
                this._createOrGetCommandDetection(this._terminal).handleCommandFinished(exitCode);
                return true;
            }
        case VSCodeOscPt.CommandLine:
            {
                const arg0 = args[0];
                const arg1 = args[1];
                let commandLine;
                if (arg0 !== undefined) {
                    commandLine = deserializeVSCodeOscMessage(arg0);
                } else {
                    commandLine = "";
                }
                this._createOrGetCommandDetection(this._terminal).setCommandLine(commandLine, arg1 === this._nonce);
                return true;
            }
        case VSCodeOscPt.ContinuationStart:
            {
                this._createOrGetCommandDetection(this._terminal).handleContinuationStart();
                return true;
            }
        case VSCodeOscPt.ContinuationEnd:
            {
                this._createOrGetCommandDetection(this._terminal).handleContinuationEnd();
                return true;
            }
        case VSCodeOscPt.EnvJson:
            {
                const arg0 = args[0];
                const arg1 = args[1];
                if (arg0 !== undefined) {
                    try {
                        const env = JSON.parse(deserializeVSCodeOscMessage(arg0));
                        this._createOrGetShellEnvDetection().setEnvironment(env, arg1 === this._nonce);
                    } catch (e) {
                        this._logService.warn("Failed to parse environment from shell integration sequence", arg0);
                    }
                }
                return true;
            }
        case VSCodeOscPt.EnvSingleStart:
            {
                this._createOrGetShellEnvDetection().startEnvironmentSingleVar(args[0] === "1", args[1] === this._nonce);
                return true;
            }
        case VSCodeOscPt.EnvSingleDelete:
            {
                const arg0 = args[0];
                const arg1 = args[1];
                const arg2 = args[2];
                if (arg0 !== undefined && arg1 !== undefined) {
                    const env = deserializeVSCodeOscMessage(arg1);
                    this._createOrGetShellEnvDetection().deleteEnvironmentSingleVar(arg0, env, arg2 === this._nonce);
                }
                return true;
            }
        case VSCodeOscPt.EnvSingleEntry:
            {
                const arg0 = args[0];
                const arg1 = args[1];
                const arg2 = args[2];
                if (arg0 !== undefined && arg1 !== undefined) {
                    const env = deserializeVSCodeOscMessage(arg1);
                    this._createOrGetShellEnvDetection().setEnvironmentSingleVar(arg0, env, arg2 === this._nonce);
                }
                return true;
            }
        case VSCodeOscPt.EnvSingleEnd:
            {
                this._createOrGetShellEnvDetection().endEnvironmentSingleVar(args[0] === this._nonce);
                return true;
            }
        case VSCodeOscPt.RightPromptStart:
            {
                this._createOrGetCommandDetection(this._terminal).handleRightPromptStart();
                return true;
            }
        case VSCodeOscPt.RightPromptEnd:
            {
                this._createOrGetCommandDetection(this._terminal).handleRightPromptEnd();
                return true;
            }
        case VSCodeOscPt.Property:
            {
                const arg0 = args[0];
                const deserialized = arg0 !== undefined ? deserializeVSCodeOscMessage(arg0) : "";
                const {
                    key,
                    value
                } = parseKeyValueAssignment(deserialized);
                if (value === undefined) {
                    return true;
                }
                switch (key) {
                case "ContinuationPrompt":
                    {
                        this._updateContinuationPrompt(removeAnsiEscapeCodesFromPrompt(value));
                        return true;
                    }
                case "Cwd":
                    {
                        this._updateCwd(value);
                        return true;
                    }
                case "IsWindows":
                    {
                        this._createOrGetCommandDetection(this._terminal).setIsWindowsPty(value === "True" ? true : false);
                        return true;
                    }
                case "HasRichCommandDetection":
                    {
                        this._createOrGetCommandDetection(this._terminal).setHasRichCommandDetection(value === "True" ? true : false);
                        return true;
                    }
                case "Prompt":
                    {
                        const sanitizedValue = value.replace(/\x1b\[[0-9;]*m/g, "");
                        this._updatePromptTerminator(sanitizedValue);
                        return true;
                    }
                case "PromptType":
                    {
                        this._createOrGetPromptTypeDetection().setPromptType(value);
                        return true;
                    }
                case "Task":
                    {
                        this._createOrGetBufferMarkDetection(this._terminal);
                        this.capabilities.get(TerminalCapability.CommandDetection)?.setIsCommandStorageDisabled();
                        return true;
                    }
                }
            }
        case VSCodeOscPt.SetMark:
            {
                this._createOrGetBufferMarkDetection(this._terminal).addMark(parseMarkSequence(args));
                return true;
            }
        }
        return false;
    }
    _updateContinuationPrompt(value) {
        if (!this._terminal) {
            return;
        }
        this._createOrGetCommandDetection(this._terminal).setContinuationPrompt(value);
    }
    _updatePromptTerminator(prompt) {
        if (!this._terminal) {
            return;
        }
        const lastPromptLine = prompt.substring(prompt.lastIndexOf("\n") + 1);
        const lastPromptLineTrimmed = lastPromptLine.trim();
        const promptTerminator = (
        lastPromptLineTrimmed.length === 1 ? lastPromptLine : lastPromptLine.substring(lastPromptLine.lastIndexOf(" ")));
        if (promptTerminator) {
            this._createOrGetCommandDetection(this._terminal).setPromptTerminator(promptTerminator, lastPromptLine);
        }
    }
    _updateCwd(value) {
        value = sanitizeCwd(value);
        this._createOrGetCwdDetection().updateCwd(value);
        const commandDetection = this.capabilities.get(TerminalCapability.CommandDetection);
        commandDetection?.setCwd(value);
    }
    _doHandleITermSequence(data) {
        if (!this._terminal) {
            return false;
        }
        const [command] = data.split(";");
        this._markSequenceSeen(`${ShellIntegrationOscPs.ITerm};${command}`);
        switch (command) {
        case ITermOscPt.SetMark:
            {
                this._createOrGetBufferMarkDetection(this._terminal).addMark();
            }
        default:
            {
                const {
                    key,
                    value
                } = parseKeyValueAssignment(command);
                if (value === undefined) {
                    return true;
                }
                switch (key) {
                case ITermOscPt.CurrentDir:
                    this._updateCwd(value);
                    return true;
                }
            }
        }
        return false;
    }
    _doHandleSetWindowsFriendlyCwd(data) {
        if (!this._terminal) {
            return false;
        }
        const [command, ...args] = data.split(";");
        this._markSequenceSeen(`${ShellIntegrationOscPs.SetWindowsFriendlyCwd};${command}`);
        switch (command) {
        case "9":
            if (args.length) {
                this._updateCwd(args[0]);
            }
            return true;
        }
        return false;
    }
    _doHandleSetCwd(data) {
        if (!this._terminal) {
            return false;
        }
        const [command] = data.split(";");
        this._markSequenceSeen(`${ShellIntegrationOscPs.SetCwd};${command}`);
        if (command.match(/^file:\/\/.*\//)) {
            const uri = ( URI.parse(command));
            if (uri.path && uri.path.length > 0) {
                this._updateCwd(uri.path);
                return true;
            }
        }
        return false;
    }
    serialize() {
        if (!this._terminal || !( this.capabilities.has(TerminalCapability.CommandDetection))) {
            return {
                isWindowsPty: false,
                hasRichCommandDetection: false,
                commands: [],
                promptInputModel: undefined
            };
        }
        const result = this._createOrGetCommandDetection(this._terminal).serialize();
        return result;
    }
    deserialize(serialized) {
        if (!this._terminal) {
            throw ( new Error("Cannot restore commands before addon is activated"));
        }
        const commandDetection = this._createOrGetCommandDetection(this._terminal);
        commandDetection.deserialize(serialized);
        if (commandDetection.cwd) {
            this._updateCwd(commandDetection.cwd);
        }
    }
    _createOrGetCwdDetection() {
        let cwdDetection = this.capabilities.get(TerminalCapability.CwdDetection);
        if (!cwdDetection) {
            cwdDetection = this._register(( new CwdDetectionCapability()));
            this.capabilities.add(TerminalCapability.CwdDetection, cwdDetection);
        }
        return cwdDetection;
    }
    _createOrGetCommandDetection(terminal) {
        let commandDetection = this.capabilities.get(TerminalCapability.CommandDetection);
        if (!commandDetection) {
            commandDetection = this._register(( new CommandDetectionCapability(terminal, this._logService)));
            this.capabilities.add(TerminalCapability.CommandDetection, commandDetection);
        }
        return commandDetection;
    }
    _createOrGetBufferMarkDetection(terminal) {
        let bufferMarkDetection = this.capabilities.get(TerminalCapability.BufferMarkDetection);
        if (!bufferMarkDetection) {
            bufferMarkDetection = this._register(( new BufferMarkCapability(terminal)));
            this.capabilities.add(TerminalCapability.BufferMarkDetection, bufferMarkDetection);
        }
        return bufferMarkDetection;
    }
    _createOrGetShellEnvDetection() {
        let shellEnvDetection = this.capabilities.get(TerminalCapability.ShellEnvDetection);
        if (!shellEnvDetection) {
            shellEnvDetection = this._register(( new ShellEnvDetectionCapability()));
            this.capabilities.add(TerminalCapability.ShellEnvDetection, shellEnvDetection);
        }
        return shellEnvDetection;
    }
    _createOrGetPromptTypeDetection() {
        let promptTypeDetection = this.capabilities.get(TerminalCapability.PromptTypeDetection);
        if (!promptTypeDetection) {
            promptTypeDetection = this._register(( new PromptTypeDetectionCapability()));
            this.capabilities.add(TerminalCapability.PromptTypeDetection, promptTypeDetection);
        }
        return promptTypeDetection;
    }
}
function deserializeVSCodeOscMessage(message) {
    return message.replaceAll(
        /\\(\\|x([0-9a-f]{2}))/gi,
        (_match, op, hex) => hex ? String.fromCharCode(parseInt(hex, 16)) : op
    );
}
function serializeVSCodeOscMessage(message) {
    return message.replace(
    /[\\;\x00-\x20]/g, char => {
        if (char === "\\") {
            return "\\\\";
        }
        const charCode = char.charCodeAt(0);
        return `\\x${( charCode.toString(16)).padStart(2, "0")}`;
    });
}
function parseKeyValueAssignment(message) {
    const separatorIndex = message.indexOf("=");
    if (separatorIndex === -1) {
        return {
            key: message,
            value: undefined
        };
    }
    return {
        key: message.substring(0, separatorIndex),
        value: message.substring(1 + separatorIndex)
    };
}
function parseMarkSequence(sequence) {
    let id = undefined;
    let hidden = false;
    for (const property of sequence) {
        if (property === undefined) {
            continue;
        }
        if (property === "Hidden") {
            hidden = true;
        }
        if (property.startsWith("Id=")) {
            id = property.substring(3);
        }
    }
    return {
        id,
        hidden
    };
}

export { ShellIntegrationAddon, ShellIntegrationOscPs, deserializeVSCodeOscMessage, parseKeyValueAssignment, parseMarkSequence, serializeVSCodeOscMessage };
