"use strict";
//
//  Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.SshStream = void 0;
const promiseCompletionSource_1 = require("./util/promiseCompletionSource");
const stream_1 = require("stream");
/**
 * Adapts an SshChannel as a Readable+Writable stream.
 */
class SshStream extends stream_1.Duplex {
    constructor(channel) {
        let readPaused = null;
        super({
            async write(chunk, encoding, cb) {
                let error;
                try {
                    let buffer;
                    if (typeof chunk === 'string') {
                        buffer = Buffer.from(chunk, encoding);
                    }
                    else if (chunk instanceof Buffer || chunk instanceof Uint8Array) {
                        buffer = chunk;
                    }
                    else {
                        throw new Error('Unsupported chunk type: ' + typeof chunk);
                    }
                    await channel.send(buffer);
                }
                catch (e) {
                    if (!(e instanceof Error))
                        throw e;
                    error = e;
                }
                if (cb) {
                    cb(error);
                }
            },
            async writev(chunks, cb) {
                let error;
                try {
                    if (chunks.length === 1) {
                        return this.write(chunks[0].chunk, chunks[0].encoding, cb);
                    }
                    else {
                        function BufferReduce(accumulator, chunk) {
                            if (chunk.chunk instanceof Buffer || chunk.chunk instanceof Uint8Array) {
                                return accumulator + chunk.chunk.length;
                            }
                            else {
                                throw new Error('Unsupported chunk type: ' + typeof chunk.chunk);
                            }
                        }
                        const totalLength = chunks.reduce(BufferReduce, 0);
                        const singleBuffer = Buffer.alloc(totalLength);
                        let singleBufferIndex = 0;
                        for (let i = 0; i < chunks.length; i++) {
                            chunks[i].chunk.copy(singleBuffer, singleBufferIndex);
                            singleBufferIndex += chunks[i].chunk.length;
                        }
                        await channel.send(singleBuffer);
                    }
                }
                catch (e) {
                    if (!(e instanceof Error))
                        throw e;
                    error = e;
                }
                if (cb) {
                    cb(error);
                }
            },
            async final(cb) {
                let error;
                try {
                    await channel.close();
                }
                catch (e) {
                    if (!(e instanceof Error))
                        throw e;
                    error = e;
                }
                if (cb) {
                    cb(error);
                }
            },
            read() {
                if (readPaused) {
                    readPaused.resolve();
                    readPaused = null;
                }
            },
        });
        channel.onDataReceived(async (data) => {
            const buffer = Buffer.alloc(data.length);
            data.copy(buffer);
            const result = this.push(buffer);
            // Our flow control isn't great. Once we hit the highWaterMark,
            // we stop adjusting the SSH window until our own reader has caught up,
            // and then *all* the data received and buffered in the interim suddenly
            // gets 'adjusted' so that we tend to be somewhat choppy about adjusting the window.
            // Improving this would require that we know when the data we push gets passed to
            // the Duplex reader, and I don't think there's a way to get that notification.
            // So I suspect we'd have to dump Duplex and implement the stream ourselves.
            if (!result) {
                if (!readPaused) {
                    readPaused = new promiseCompletionSource_1.PromiseCompletionSource();
                }
                await readPaused.promise;
            }
            // Notify the channel that the data has been consumed and more data may be sent.
            channel.adjustWindow(buffer.length);
        });
        channel.onClosed(() => {
            this.push(null);
        });
        this.channel = channel;
    }
    /**
     * Destroys the stream and closes the underlying SSH channel.
     */
    destroy(error) {
        void this.channel.close().catch();
        super.destroy(error);
        return this;
    }
    toString() {
        return `SshStream(Channel Type: ${this.channel.channelType}, Id: ${this.channel.channelId}, RemoteId: ${this.channel.remoteChannelId})`;
    }
}
exports.SshStream = SshStream;
//# sourceMappingURL=https://github.com/VSCodium/sourcemaps/releases/download/stable-824c4c46a288b839f13b24022655329c2aeb9f81/node_modules-@microsoft-dev-tunnels-ssh-sshStream.js.map