"use strict";
/*
 * @adonisjs/ace
 *
 * (c) Harminder Virk <virk@adonisjs.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.printHelpFor = exports.printHelp = void 0;
const cliui_1 = require("@poppinss/cliui");
const term_size_1 = __importDefault(require("term-size"));
const sortAndGroupCommands_1 = require("./sortAndGroupCommands");
/**
 * Converts a line to rows at a specific width
 */
function lineToRows(text, width) {
    const rows = [];
    let row = [];
    let wordsCount = 0;
    text.split(' ').forEach((word) => {
        if (wordsCount + (word.length + 1) > width) {
            /**
             * Push the number of whitespace left after the existing current
             * and the terminal space. We need to do this, coz we at times
             * have whitespace when the upcoming word may break into next
             * lines
             */
            row.push(new Array(width - wordsCount + 1).join(' '));
            /**
             * Push the existing row to the rows
             */
            rows.push(row.join(' '));
            /**
             * Row is empty now
             */
            row = [];
            /**
             * Row has zero words
             */
            wordsCount = 0;
        }
        /**
         * Increase the words count + 1. The extra one is for the
         * whitspace between the words
         */
        wordsCount += word.length + 1;
        /**
         * Collect word inside the row
         */
        row.push(word);
    });
    /**
     * Handle the orphan row
     */
    if (row.length) {
        rows.push(row.join(' '));
    }
    return rows;
}
/**
 * Converts the description to multiple lines fitting into
 * a given column size
 */
function descriptionToRows(description, options) {
    return lineToRows(description, options.descriptionColumnsSize)
        .map((column, index) => {
        return index > 0 ? `${new Array(options.nameColumnSize + 1).join(' ')}${column}` : column;
    })
        .join('');
}
/**
 * Wraps the command arg inside `<>` or `[]` brackets based upon if it's
 * required or not.
 */
function wrapArg(arg) {
    const displayName = arg.type === 'spread' ? `...${arg.name}` : arg.name;
    return arg.required ? `<${displayName}>` : `[${displayName}]`;
}
/**
 * Returns an array of flags for displaying the help screen
 */
function getFlagsForDisplay(flags) {
    return flags.map(({ name, type, alias, description }) => {
        /**
         * Display name is the way we want to display a single flag in the
         * list of flags
         */
        const displayName = alias ? `-${alias}, --${name}` : `--${name}`;
        /**
         * The type hints the user about the expectation on the flag type. We only
         * print the type, when flag is not a boolean.
         */
        let displayType = '';
        switch (type) {
            case 'array':
                displayType = 'string[]';
                break;
            case 'numArray':
                displayType = 'number[]';
                break;
            case 'string':
                displayType = 'string';
                break;
            case 'boolean':
                displayType = 'boolean';
                break;
            case 'number':
                displayType = 'number';
                break;
        }
        return {
            displayName,
            displayType,
            description,
            width: displayName.length + displayType.length,
        };
    });
}
/**
 * Returns an array of args for displaying the help screen
 */
function getArgsForDisplay(args) {
    return args.map(({ name, description }) => {
        return {
            displayName: name,
            description: description,
            width: name.length,
        };
    });
}
/**
 * Returns an array of commands for display
 */
function getCommandsForDisplay(commands, aliases) {
    return commands.map(({ commandName, description }) => {
        const commandAliases = getCommandAliases(commandName, aliases);
        const aliasesString = commandAliases.length ? ` [${commandAliases.join(', ')}]` : '';
        return {
            displayName: `${commandName}${aliasesString}`,
            description,
            width: commandName.length + aliasesString.length,
        };
    });
}
/**
 * Returns the aliases for a given command
 */
function getCommandAliases(commandName, aliases) {
    return Object.keys(aliases).reduce((commandAliases, alias) => {
        if (aliases[alias] === commandName) {
            commandAliases.push(alias);
        }
        return commandAliases;
    }, []);
}
/**
 * Prints help for all the commands by sorting them in alphabetical order
 * and grouping them as per their namespace.
 */
function printHelp(commands, flags, aliases) {
    const flagsList = getFlagsForDisplay(flags);
    const commandsList = getCommandsForDisplay(commands, aliases);
    /**
     * Get width of longest command name.
     */
    const maxWidth = Math.max.apply(Math, flagsList.concat(commandsList).map(({ width }) => width));
    /**
     * Size of the terminal columns. Max width is the width of the command
     * name and the extra four is whitespace around the command name.
     *
     * This gives the columns size for the description section
     */
    const descriptionColumnsSize = (0, term_size_1.default)().columns - (maxWidth + 4);
    /**
     * Sort commands and group them, so that we can print them as per
     * the namespace they belongs to
     */
    (0, sortAndGroupCommands_1.sortAndGroupCommands)(commands).forEach(({ group, commands: groupCommands }) => {
        console.log('');
        if (group === 'root') {
            console.log(cliui_1.logger.colors.bold(cliui_1.logger.colors.yellow('Available commands')));
        }
        else {
            console.log(cliui_1.logger.colors.bold(cliui_1.logger.colors.yellow(group)));
        }
        groupCommands.forEach(({ commandName, description }) => {
            const commandAliases = getCommandAliases(commandName, aliases);
            const aliasesString = commandAliases.length ? ` [${commandAliases.join(', ')}]` : '';
            const displayName = `${commandName}${aliasesString}`;
            const whiteSpace = ''.padEnd(maxWidth - displayName.length, ' ');
            const descriptionRows = descriptionToRows(description, {
                nameColumnSize: maxWidth + 4,
                descriptionColumnsSize,
            });
            console.log(`  ${cliui_1.logger.colors.green(displayName)} ${whiteSpace}  ${cliui_1.logger.colors.dim(descriptionRows)}`);
        });
    });
    if (flagsList.length) {
        console.log('');
        console.log(cliui_1.logger.colors.bold(cliui_1.logger.colors.yellow('Global Flags')));
        flagsList.forEach(({ displayName, displayType, description = '', width }) => {
            const whiteSpace = ''.padEnd(maxWidth - width, ' ');
            const descriptionRows = descriptionToRows(description, {
                nameColumnSize: maxWidth + 4,
                descriptionColumnsSize,
            });
            console.log(`  ${cliui_1.logger.colors.green(displayName)} ${cliui_1.logger.colors.dim(displayType)}${whiteSpace}  ${cliui_1.logger.colors.dim(descriptionRows)}`);
        });
    }
}
exports.printHelp = printHelp;
/**
 * Prints help for a single command
 */
function printHelpFor(command, aliases) {
    if (command.description) {
        console.log('');
        console.log(command.description);
    }
    console.log('');
    console.log(`${cliui_1.logger.colors.yellow('Usage:')} ${command.commandName} ${cliui_1.logger.colors.dim(command.args.map(wrapArg).join(' '))}`);
    const flags = getFlagsForDisplay(command.flags);
    const args = getArgsForDisplay(command.args);
    /**
     * Getting max width to keep flags and args symmetric
     */
    const maxWidth = Math.max.apply(Math, flags.concat(args).map(({ width }) => width));
    /**
     * Size of the terminal columns. Max width is the width of the command
     * name and the extra four is whitespace around the command name.
     *
     * This gives the columns size for the description section
     */
    const descriptionColumnsSize = (0, term_size_1.default)().columns - (maxWidth + 5);
    const commandAliases = getCommandAliases(command.commandName, aliases);
    if (commandAliases.length) {
        console.log('');
        console.log(`${cliui_1.logger.colors.yellow('Aliases:')} ${cliui_1.logger.colors.green(commandAliases.join(', '))}`);
    }
    if (args.length) {
        console.log('');
        console.log(cliui_1.logger.colors.bold(cliui_1.logger.colors.yellow('Arguments')));
        args.forEach(({ displayName, description = '', width }) => {
            const whiteSpace = ''.padEnd(maxWidth - width, ' ');
            const descriptionRow = descriptionToRows(description, {
                nameColumnSize: maxWidth + 5,
                descriptionColumnsSize,
            });
            console.log(`  ${cliui_1.logger.colors.green(displayName)} ${whiteSpace}  ${cliui_1.logger.colors.dim(descriptionRow)}`);
        });
    }
    if (flags.length) {
        console.log('');
        console.log(cliui_1.logger.colors.bold(cliui_1.logger.colors.yellow('Flags')));
        flags.forEach(({ displayName, displayType, description = '', width }) => {
            const whiteSpace = ''.padEnd(maxWidth - width, ' ');
            const descriptionRow = descriptionToRows(description, {
                nameColumnSize: maxWidth + 5,
                descriptionColumnsSize,
            });
            console.log(`  ${cliui_1.logger.colors.green(displayName)} ${cliui_1.logger.colors.dim(displayType)}${whiteSpace}  ${cliui_1.logger.colors.dim(descriptionRow)}`);
        });
    }
}
exports.printHelpFor = printHelpFor;
