"use strict";
//
//  Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.SessionMetrics = void 0;
const vscode_jsonrpc_1 = require("vscode-jsonrpc");
const trace_1 = require("../trace");
/**
 * Collects current and cumulative measurements about a session.
 */
class SessionMetrics {
    /* @internal */
    constructor() {
        this.startTime = 0;
        this.messagesSentCount = 0;
        this.messagesReceivedCount = 0;
        this.bytesSentSum = 0;
        this.bytesReceivedSum = 0;
        this.reconnectionsCount = 0;
        this.currentLatency = 0;
        this.minLatency = 0;
        this.maxLatency = 0;
        this.latencySum = 0;
        this.latencyCount = 0;
        this.messageSentEmitter = new vscode_jsonrpc_1.Emitter();
        this.onMessageSent = this.messageSentEmitter.event;
        this.messageReceivedEmitter = new vscode_jsonrpc_1.Emitter();
        this.onMessageReceived = this.messageReceivedEmitter.event;
        this.latencyUpdatedEmitter = new vscode_jsonrpc_1.Emitter();
        this.onLatencyUpdated = this.latencyUpdatedEmitter.event;
        this.sessionClosedEmitter = new vscode_jsonrpc_1.Emitter();
        this.onSessionClosed = this.sessionClosedEmitter.event;
        if (typeof performance === 'object' && typeof performance.now === 'function') {
            Object.defineProperty(this, 'time', { get: this.browserTime });
        }
        else if (typeof process === 'object' && typeof process.hrtime === 'function') {
            Object.defineProperty(this, 'time', { get: this.nodejsTime });
        }
        this.startTime = this.time;
    }
    /**
     * Gets the current stopwatch value in milliseconds (possibly including fractional milliseconds),
     * used for measuring latency.
     */
    /* @internal */
    get time() {
        // The SessionMetrics constructor may replace this with either of the below
        // high-precision implementations, depending on availability of platform APIs.
        return Date.now() - this.startTime;
    }
    browserTime() {
        // Use the browser high-resolution time API.
        // Note the precision may be reduced for pricacy depending on browser and page policy.
        return performance.now() - this.startTime;
    }
    nodejsTime() {
        // Use Node.js high-resolution time API.
        const [s, ns] = process.hrtime();
        return s * 1000 + ns / 1000000 - this.startTime;
    }
    /**
     * Gets the total cumulative number of messages sent for the duration of the session,
     * including all channels and non-channel protocol messages.
     */
    get messagesSent() {
        return this.messagesSentCount;
    }
    /**
     * Gets the total cumulative number of messages received for the duration of the session,
     * including all channels and non-channel protocol messages.
     */
    get messagesReceived() {
        return this.messagesReceivedCount;
    }
    /**
     * Gets the total cumulative number of bytes sent for the duration of the session,
     * including all channels and non-channel protocol messages, and including message
     * framing, padding, and MAC bytes.
     */
    get bytesSent() {
        return this.bytesSentSum;
    }
    /**
     * Gets the total cumulative number of bytes received for the duration of the session,
     * including all channels and non-channel protocol messages, and including message
     * framing, padding, and MAC bytes.
     */
    get bytesReceived() {
        return this.bytesReceivedSum;
    }
    /**
     * Gets the number of times the session has reconnected.
     * </summary>
     * <remarks>
     * Reconnection requires both sides to support the
     * <see cref="SshProtocolExtensionNames.SessionReconnect" /> protocol extension.
     */
    get reconnections() {
        return this.reconnectionsCount;
    }
    /**
     * Gets the average measured round-trip connection latency between client and server
     * over the duration of the session, in milliseconds.
     * </summary>
     * <remarks>
     * Latency measurement requires both sides to support the
     * <see cref="SshProtocolExtensionNames.SessionLatency" /> protocol extension.
     * If not supported, this Sum will be 0.
     */
    get latencyAverageMs() {
        return this.latencyCount === 0 ? 0 : this.latencySum / this.latencyCount;
    }
    /**
     * Gets the minimum measured round-trip connection latency between client and server
     * over the duration of the session, in milliseconds.
     * </summary>
     * <remarks>
     * Latency measurement requires both sides to support the
     * <see cref="SshProtocolExtensionNames.SessionLatency" /> protocol extension.
     * If not supported, this Sum will be 0.
     */
    get latencyMinMs() {
        return this.minLatency;
    }
    /**
     * Gets the maximum measured round-trip connection latency between client and server
     * over the duration of the session, in milliseconds.
     * </summary>
     * <remarks>
     * Latency measurement requires both sides to support the
     * <see cref="SshProtocolExtensionNames.SessionLatency" /> protocol extension.
     * If not supported, this Sum will be 0.
     */
    get latencyMaxMs() {
        return this.maxLatency;
    }
    /**
     * Gets the most recent measurement of round-trip connection latency between client and
     * server, in milliseconds.
     * </summary>
     * <remarks>
     * Latency measurement requires both sides to support the
     * <see cref="SshProtocolExtensionNames.SessionLatency" /> protocol extension.
     * If not supported or the session is not currently connected, this Sum will be 0.
     */
    get latencyCurrentMs() {
        return this.currentLatency;
    }
    /* @internal */
    addMessageSent(size) {
        this.messagesSentCount++;
        this.bytesSentSum += size;
        this.messageSentEmitter.fire({ time: this.time, size });
    }
    /* @internal */
    addMessageReceived(size) {
        this.messagesReceivedCount++;
        this.bytesReceivedSum += size;
        this.messageReceivedEmitter.fire({ time: this.time, size });
    }
    /* @internal */
    addReconnection() {
        this.reconnectionsCount++;
    }
    /* @internal */
    updateLatency(latencyMs, trace) {
        if (latencyMs < 0) {
            if (trace) {
                trace(trace_1.TraceLevel.Warning, trace_1.SshTraceEventIds.metricsError, `Measured latency was negative: ${latencyMs} us`);
            }
            return;
        }
        this.currentLatency = latencyMs;
        if (latencyMs === 0) {
            // Disconnected.
            return;
        }
        if (this.minLatency === 0 || latencyMs < this.minLatency) {
            this.minLatency = latencyMs;
        }
        if (this.maxLatency === 0 || latencyMs > this.maxLatency) {
            this.maxLatency = latencyMs;
        }
        // Enable computing the average.
        this.latencySum += latencyMs;
        this.latencyCount++;
        this.latencyUpdatedEmitter.fire({ time: this.time, latency: latencyMs });
    }
    /* @internal */
    close() {
        this.currentLatency = 0;
        this.sessionClosedEmitter.fire();
    }
    toString() {
        let s = `Messages S/R: ${this.messagesSent} / ${this.messagesReceived}; ` +
            `Bytes S/R: ${this.bytesSent} / ${this.bytesReceived}; ` +
            `Reconnections: ${this.reconnections}; `;
        // Show extra precision for a low-latency connection.
        const precision = this.minLatency >= 10 ? 1 : this.minLatency >= 1 ? 10 : 100;
        if (this.maxLatency > 0) {
            const min = Math.round(this.minLatency * precision) / precision;
            const avg = Math.round((this.latencySum / this.latencyCount) * precision) / precision;
            const max = Math.round(this.maxLatency * precision) / precision;
            s += `Latency Min-Avg-Max: ${min} - ${avg} - ${max} ms; `;
        }
        if (this.currentLatency > 0) {
            const current = Math.round(this.currentLatency * precision) / precision;
            s += `Current Latency: ${current} ms; `;
        }
        return s;
    }
}
exports.SessionMetrics = SessionMetrics;
//# sourceMappingURL=https://main.vscode-cdn.net/sourcemaps/6a44c352bd24569c417e530095901b649960f9f8/node_modules/@microsoft/dev-tunnels-ssh/metrics/sessionMetrics.js.map