"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.listTags = void 0;
/*
 * This is a TypeScript port of the original Java version, which was written by
 * Gil Tene as described in
 * https://github.com/HdrHistogram/HdrHistogram
 * and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */
const Histogram_1 = require("./Histogram");
const encoding_1 = require("./encoding");
const TAG_PREFIX = "Tag=";
const TAG_PREFIX_LENGTH = "Tag=".length;
/**
 * A histogram log reader.
 * <p>
 * Histogram logs are used to capture full fidelity, per-time-interval
 * histograms of a recorded value.
 * <p>
 * For example, a histogram log can be used to capture high fidelity
 * reaction-time logs for some measured system or subsystem component.
 * Such a log would capture a full reaction time histogram for each
 * logged interval, and could be used to later reconstruct a full
 * HdrHistogram of the measured reaction time behavior for any arbitrary
 * time range within the log, by adding [only] the relevant interval
 * histograms.
 * <h3>Histogram log format:</h3>
 * A histogram log file consists of text lines. Lines beginning with
 * the "#" character are optional and treated as comments. Lines
 * containing the legend (starting with "Timestamp") are also optional
 * and ignored in parsing the histogram log. All other lines must
 * be valid interval description lines. Text fields are delimited by
 * commas, spaces.
 * <p>
 * A valid interval description line contains an optional Tag=tagString
 * text field, followed by an interval description.
 * <p>
 * A valid interval description must contain exactly four text fields:
 * <ul>
 * <li>StartTimestamp: The first field must contain a number parse-able as a Double value,
 * representing the start timestamp of the interval in seconds.</li>
 * <li>intervalLength: The second field must contain a number parse-able as a Double value,
 * representing the length of the interval in seconds.</li>
 * <li>Interval_Max: The third field must contain a number parse-able as a Double value,
 * which generally represents the maximum value of the interval histogram.</li>
 * <li>Interval_Compressed_Histogram: The fourth field must contain a text field
 * parse-able as a Base64 text representation of a compressed HdrHistogram.</li>
 * </ul>
 * The log file may contain an optional indication of a starting time. Starting time
 * is indicated using a special comments starting with "#[StartTime: " and followed
 * by a number parse-able as a double, representing the start time (in seconds)
 * that may be added to timestamps in the file to determine an absolute
 * timestamp (e.g. since the epoch) for each interval.
 */
class HistogramLogReader {
    constructor(logContent, bitBucketSize = 32, useWebAssembly = false) {
        this.lines = splitLines(logContent);
        this.currentLineIndex = 0;
        this.bitBucketSize = bitBucketSize;
        this.useWebAssembly = useWebAssembly;
    }
    /**
     * Read the next interval histogram from the log. Returns a Histogram object if
     * an interval line was found, or null if not.
     * <p>Upon encountering any unexpected format errors in reading the next interval
     * from the file, this method will return a null.
     * @return a DecodedInterval, or a null if no appropriate interval found
     */
    nextIntervalHistogram(rangeStartTimeSec = 0, rangeEndTimeSec = Number.MAX_VALUE) {
        while (this.currentLineIndex < this.lines.length) {
            const currentLine = this.lines[this.currentLineIndex];
            this.currentLineIndex++;
            if (currentLine.startsWith("#[StartTime:")) {
                this.parseStartTimeFromLine(currentLine);
            }
            else if (currentLine.startsWith("#[BaseTime:")) {
                this.parseBaseTimeFromLine(currentLine);
            }
            else if (currentLine.startsWith("#") ||
                currentLine.startsWith('"StartTimestamp"')) {
                // skip legend & meta data for now
            }
            else if (currentLine.includes(",")) {
                const tokens = currentLine.split(",");
                const [firstToken] = tokens;
                let tag;
                if (firstToken.startsWith(TAG_PREFIX)) {
                    tag = firstToken.substring(TAG_PREFIX_LENGTH);
                    tokens.shift();
                }
                else {
                    tag = Histogram_1.NO_TAG;
                }
                const [rawLogTimeStampInSec, rawIntervalLengthSec, , base64Histogram,] = tokens;
                const logTimeStampInSec = Number.parseFloat(rawLogTimeStampInSec);
                if (!this.baseTimeSec) {
                    // No explicit base time noted. Deduce from 1st observed time (compared to start time):
                    if (logTimeStampInSec < this.startTimeSec - 365 * 24 * 3600.0) {
                        // Criteria Note: if log timestamp is more than a year in the past (compared to
                        // StartTime), we assume that timestamps in the log are not absolute
                        this.baseTimeSec = this.startTimeSec;
                    }
                    else {
                        // Timestamps are absolute
                        this.baseTimeSec = 0.0;
                    }
                }
                if (rangeEndTimeSec < logTimeStampInSec) {
                    return null;
                }
                if (logTimeStampInSec < rangeStartTimeSec) {
                    continue;
                }
                const histogram = encoding_1.decodeFromCompressedBase64(base64Histogram, this.bitBucketSize, this.useWebAssembly);
                histogram.startTimeStampMsec =
                    (this.baseTimeSec + logTimeStampInSec) * 1000;
                const intervalLengthSec = Number.parseFloat(rawIntervalLengthSec);
                histogram.endTimeStampMsec =
                    (this.baseTimeSec + logTimeStampInSec + intervalLengthSec) * 1000;
                histogram.tag = tag;
                return histogram;
            }
        }
        return null;
    }
    parseStartTimeFromLine(line) {
        this.startTimeSec = Number.parseFloat(line.split(" ")[1]);
    }
    parseBaseTimeFromLine(line) {
        this.baseTimeSec = Number.parseFloat(line.split(" ")[1]);
    }
}
const splitLines = (logContent) => logContent.split(/\r\n|\r|\n/g);
const shouldIncludeNoTag = (lines) => lines.find((line) => !line.startsWith("#") &&
    !line.startsWith('"') &&
    !line.startsWith(TAG_PREFIX) &&
    line.includes(","));
exports.listTags = (content) => {
    const lines = splitLines(content);
    const tags = lines
        .filter((line) => line.includes(",") && line.startsWith(TAG_PREFIX))
        .map((line) => line.substring(TAG_PREFIX_LENGTH, line.indexOf(",")));
    const tagsWithoutDuplicates = new Set(tags);
    const result = Array.from(tagsWithoutDuplicates);
    if (shouldIncludeNoTag(lines)) {
        result.unshift("NO TAG");
    }
    return result;
};
exports.default = HistogramLogReader;
//# sourceMappingURL=HistogramLogReader.js.map