"use strict";
/* eslint-disable */
/*
 * Patch #1 for node-progress to fix display issue in certain terminals.
 * Issue related: https://github.com/MarkBind/markbind/issues/416
 * The **only** changes are based on the following PR and its comments:
 * https://github.com/visionmedia/node-progress/pull/168
 * As the above PR is not merged since 2017, this is a temporary patch
 * to fix the issue for markbind's usecase.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProgressBar = void 0;
/*
 * Patch #2 for node-progress to allow logging without disturbing the progress bar.
 * The changes are based on the following PR:
 * https://github.com/visionmedia/node-progress/pull/155
 */
/*
 * Patch #3 Rewrite the progress bar into TypeScript and make it ESM compatible.
 */
/*!
 * node-progress
 * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */
const tty_1 = require("tty");
/**
 * ProgressBar class for displaying progress in terminal
 *
 * Tokens:
 *   - `:bar` the progress bar itself
 *   - `:current` current tick number
 *   - `:total` total ticks
 *   - `:elapsed` time elapsed in seconds
 *   - `:percent` completion percentage
 *   - `:eta` eta in seconds
 *   - `:rate` rate of ticks per second
 */
class ProgressBar {
    /**
     * Initialize a ProgressBar with the given format string and options
     * @param fmt Format string for the progress bar
     * @param options Configuration options or total number
     */
    constructor(fmt, options) {
        // Handle options parameter - can be number (total) or options object
        let opts;
        if (typeof options === 'number') {
            opts = { total: options };
        }
        else {
            opts = options || {};
            if (typeof fmt !== 'string')
                throw new Error('format required');
            if (typeof opts.total !== 'number')
                throw new Error('total required');
        }
        this.stream = opts.stream || process.stderr;
        // patch #1
        // options.forceTTY is undefined in git-bash on mintty Windows
        if (!process.stderr.isTTY) {
            const tty = tty_1.WriteStream.prototype;
            Object.getOwnPropertyNames(tty).forEach((key) => {
                process.stderr[key] = tty[key];
            });
            process.stderr.columns = 80;
        } // end patch
        this.fmt = fmt;
        this.curr = opts.curr || 0;
        this.total = opts.total;
        this.width = opts.width || this.total;
        this.clear = opts.clear || false;
        this.chars = {
            complete: opts.complete || '=',
            incomplete: opts.incomplete || '-',
            head: opts.head || (opts.complete || '=')
        };
        this.renderThrottle = opts.renderThrottle !== 0 ? (opts.renderThrottle || 16) : 0;
        this.lastRender = -Infinity;
        this.callback = opts.callback || (() => { });
        this.tokens = {};
        this.lastDraw = '';
    }
    /**
     * "tick" the progress bar with optional `len` and optional `tokens`.
     * @param len Length to increment or tokens object
     * @param tokens Optional tokens for template replacement
     */
    tick(len, tokens) {
        let increment = 1;
        if (len !== 0) {
            increment = (typeof len === 'number' ? len : 1) || 1;
        }
        // swap tokens
        if (typeof len === 'object') {
            tokens = len;
            increment = 1;
        }
        if (tokens)
            this.tokens = tokens;
        // start time for eta
        if (this.curr === 0)
            this.start = new Date();
        this.curr += increment;
        // try to render
        this.render();
        // progress complete
        if (this.curr >= this.total) {
            this.render(undefined, true);
            this.complete = true;
            this.terminate();
            this.callback(this);
            return;
        }
    }
    /**
     * Method to render the progress bar with optional `tokens` to place in the
     * progress bar's `fmt` field.
     * @param tokens Optional tokens for template replacement
     * @param force Force rendering even if throttled
     */
    render(tokens, force) {
        const forceRender = force !== undefined ? force : false;
        if (tokens)
            this.tokens = tokens;
        if (!this.stream.isTTY)
            return;
        const now = Date.now();
        const delta = now - this.lastRender;
        if (!forceRender && (delta < this.renderThrottle)) {
            return;
        }
        else {
            this.lastRender = now;
        }
        let ratio = this.curr / this.total;
        ratio = Math.min(Math.max(ratio, 0), 1);
        const percent = Math.floor(ratio * 100);
        let incomplete, complete, completeLength;
        const elapsed = this.start ? new Date().getTime() - this.start.getTime() : 0;
        const eta = (percent === 100) ? 0 : elapsed * (this.total / this.curr - 1);
        const rate = this.curr / (elapsed / 1000);
        /* populate the bar template with percentages and timestamps */
        let str = this.fmt
            .replace(':current', this.curr.toString())
            .replace(':total', this.total.toString())
            .replace(':elapsed', isNaN(elapsed) ? '0.0' : (elapsed / 1000).toFixed(1))
            .replace(':eta', (isNaN(eta) || !isFinite(eta)) ? '0.0' : (eta / 1000).toFixed(1))
            .replace(':percent', percent.toFixed(0) + '%')
            .replace(':rate', Math.round(rate).toString());
        /* compute the available space (non-zero) for the bar */
        let availableSpace = Math.max(0, this.stream.columns - str.replace(':bar', '').length);
        if (availableSpace && process.platform === 'win32') {
            availableSpace = availableSpace - 1;
        }
        const width = Math.min(this.width, availableSpace);
        /* TODO: the following assumes the user has one ':bar' token */
        completeLength = Math.round(width * ratio);
        complete = Array(Math.max(0, completeLength + 1)).join(this.chars.complete);
        incomplete = Array(Math.max(0, width - completeLength + 1)).join(this.chars.incomplete);
        /* add head to the complete string */
        if (completeLength > 0) {
            complete = complete.slice(0, -1) + this.chars.head;
        }
        /* fill in the actual progress bar */
        str = str.replace(':bar', complete + incomplete);
        /* replace the extra tokens */
        if (this.tokens) {
            for (const key in this.tokens) {
                str = str.replace(':' + key, this.tokens[key].toString());
            }
        }
        if (this.lastDraw !== str) {
            this.stream.cursorTo(0);
            this.stream.write(str);
            this.stream.clearLine(1);
            this.lastDraw = str;
        }
    }
    /**
     * "update" the progress bar to represent an exact percentage.
     * The ratio (between 0 and 1) specified will be multiplied by `total` and
     * floored, representing the closest available "tick." For example, if a
     * progress bar has a length of 3 and `update(0.5)` is called, the progress
     * will be set to 1.
     *
     * A ratio of 0.5 will attempt to set the progress to halfway.
     * @param ratio The ratio (between 0 and 1 inclusive) to set the overall completion to
     * @param tokens Optional tokens for template replacement
     */
    update(ratio, tokens) {
        const goal = Math.floor(ratio * this.total);
        const delta = goal - this.curr;
        this.tick(delta, tokens);
    }
    /**
     * "interrupt" the progress bar and write a message above it.
     * @param message The message to write
     */
    interrupt(message) {
        // clear the current line
        this.stream.clearLine();
        // move the cursor to the start of the line
        this.stream.cursorTo(0);
        // write the message text
        this.stream.write(message);
        // terminate the line after writing the message
        this.stream.write('\n');
        // re-display the progress bar with its lastDraw
        this.stream.write(this.lastDraw);
    }
    /**
     * Terminates a progress bar.
     */
    terminate() {
        if (this.clear) {
            if (this.stream.clearLine) {
                this.stream.clearLine();
                this.stream.cursorTo(0);
            }
        }
        else {
            this.stream.write('\n');
        }
    }
    // patch #2 Add interruptBegin & interruptEnd
    /**
     * Begin a interrupt so the user can manually write messages above the bar.
     */
    interruptBegin() {
        this.stream.clearLine();
        this.stream.cursorTo(0);
    }
    /**
     * End a interrupt, rendering the last draw again.
     */
    interruptEnd() {
        this.stream.write(this.lastDraw);
    } // end patch
}
exports.ProgressBar = ProgressBar;
//# sourceMappingURL=node-progress.js.map