"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SshShell = void 0;
const defer_1 = require("@f2/defer");
const event_1 = require("@f2/event");
const task_1 = require("@f2/task");
const EndRegex = /[a-z0-9_-]+@[\.a-z0-9_-]+\:.*[\$\#] $/gi;
class SshShell {
    constructor(client, options) {
        this._deferredReady = (0, defer_1.getDeferred)();
        this._readyToRunCmd = false;
        this._cmdQueue = [];
        (0, event_1.beginEventEmitter)(this);
        client.shell((err, channel) => {
            if (err) {
                return this.emit('error', err);
            }
            this._channel = channel;
            const onData = (chunk) => {
                //console.log('recv - ' + chunk.toString());
                if (this._inProgressCmd) {
                    this._onCmdData(this._inProgressCmd, chunk);
                    return;
                }
                if (this._checkDefaultTermReturn(chunk)) {
                    this._next();
                }
            };
            channel.stdout.on('data', (chunk) => {
                //console.log('recv start - ' + chunk.toString());
                if (!this._checkDefaultTermReturn(chunk)) {
                    return;
                }
                const deferred = this._deferredReady;
                if (deferred) {
                    this._deferredReady = undefined;
                    deferred.resolve();
                }
                channel.stdout.removeAllListeners('data');
                channel.stdout.on('data', onData);
                this._next();
            });
        });
        if (options) {
            this._checkEndCmdFn = options.checkEndCmdFn;
            if (options.firstCmd) {
                this.runTaskCmd(options.firstCmd);
            }
        }
    }
    _checkDefaultTermReturn(data) {
        const dataStr = data.toString();
        const lines = dataStr.split('\r');
        const lastLine = lines[lines.length - 1];
        return lastLine.match(EndRegex) !== null;
    }
    _next() {
        const cmd = this._inProgressCmd;
        if (cmd) {
            if (cmd.data) {
                cmd.deferred.resolve(Buffer.concat(cmd.data).toString());
            }
            else {
                cmd.deferred.resolve(undefined);
            }
            this._inProgressCmd = undefined;
        }
        this._readyToRunCmd = true;
        this._runNext();
    }
    _onCmdData(cmd, data) {
        if (cmd.observer) {
            cmd.observer.progress('data', data);
        }
        else {
            cmd.data.push(data);
        }
        if (cmd.checkEndFn) {
            if (cmd.checkEndFn(data)) {
                this._next();
            }
        }
        else if (this._checkDefaultTermReturn(data)) {
            this._next();
        }
        else if (this._checkEndCmdFn) {
            if (this._checkEndCmdFn(data)) {
                this._next();
            }
        }
    }
    _runNext() {
        if (!this._readyToRunCmd || this._inProgressCmd) {
            return;
        }
        if (this._cmdQueue.length === 0) {
            return;
        }
        const cmd = this._cmdQueue.shift();
        this._inProgressCmd = cmd;
        this.write(cmd.cmd);
    }
    runCmd(cmd) {
        const deferred = (0, defer_1.getDeferred)();
        this._cmdQueue.push({
            cmd,
            deferred,
            data: []
        });
        this._runNext();
        return deferred.promise;
    }
    runTaskCmd(cmd) {
        const observer = new task_1.TaskObserver();
        const deferred = (0, defer_1.getDeferred)();
        const task = task_1.Task.from(deferred.promise, observer);
        this._cmdQueue.push({
            cmd,
            observer,
            deferred
        });
        this._runNext();
        return task;
    }
    waitReady() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this._deferredReady) {
                return this._deferredReady.promise;
            }
        });
    }
    write(cmd) {
        var _a;
        //console.log('send - ' + cmd);
        (_a = this._channel) === null || _a === void 0 ? void 0 : _a.write(cmd + '\n');
    }
}
exports.SshShell = SshShell;
