"use strict";
const helpers = require("./helpers");
const yargs = require("yargs");
class OptionType {
}
OptionType.String = "string";
OptionType.Boolean = "boolean";
OptionType.Number = "number";
OptionType.Array = "array";
OptionType.Object = "object";
exports.OptionType = OptionType;
class OptionsBase {
    constructor(options, defaultProfileDir, $errors, $staticConfig) {
        this.options = options;
        this.defaultProfileDir = defaultProfileDir;
        this.$errors = $errors;
        this.$staticConfig = $staticConfig;
        this.optionsWhiteList = ["ui", "recursive", "reporter", "require", "timeout", "_", "$0"];
        this.globalOptions = {
            log: { type: OptionType.String },
            verbose: { type: OptionType.Boolean, alias: "v" },
            version: { type: OptionType.Boolean },
            help: { type: OptionType.Boolean, alias: "h" },
            profileDir: { type: OptionType.String, default: this.defaultProfileDir },
            analyticsClient: { type: OptionType.String },
            path: { type: OptionType.String, alias: "p" },
            _: { type: OptionType.String }
        };
        this.options = _.extend({}, this.commonOptions, this.options, this.globalOptions);
        this.setArgv();
    }
    get shorthands() {
        let result = [];
        _.each(_.keys(this.options), optionName => {
            if (this.options[optionName].alias) {
                result.push(this.options[optionName].alias);
            }
        });
        return result;
    }
    get commonOptions() {
        return {
            all: { type: OptionType.Boolean },
            json: { type: OptionType.Boolean },
            watch: { type: OptionType.Boolean },
            avd: { type: OptionType.String },
            config: { type: OptionType.Array },
            debug: { type: OptionType.Boolean, alias: "d" },
            timeout: { type: OptionType.String },
            device: { type: OptionType.String },
            availableDevices: { type: OptionType.Boolean },
            appid: { type: OptionType.String },
            geny: { type: OptionType.String },
            debugBrk: { type: OptionType.Boolean },
            debugPort: { type: OptionType.Number },
            getPort: { type: OptionType.Boolean },
            start: { type: OptionType.Boolean },
            stop: { type: OptionType.Boolean },
            ddi: { type: OptionType.String },
            justlaunch: { type: OptionType.Boolean },
            file: { type: OptionType.String },
            force: { type: OptionType.Boolean, alias: "f" },
            companion: { type: OptionType.Boolean },
            emulator: { type: OptionType.Boolean },
            sdk: { type: OptionType.String },
            template: { type: OptionType.String },
            release: { type: OptionType.Boolean, alias: "r" },
            var: { type: OptionType.Object },
            default: { type: OptionType.Boolean },
            count: { type: OptionType.Number },
            hooks: { type: OptionType.Boolean, default: true }
        };
    }
    get optionNames() {
        return _.keys(this.options);
    }
    getOptionValue(optionName) {
        optionName = this.getCorrectOptionName(optionName);
        return this.argv[optionName];
    }
    validateOptions(commandSpecificDashedOptions) {
        if (commandSpecificDashedOptions) {
            this.options = this.globalOptions;
            _.extend(this.options, commandSpecificDashedOptions);
            this.setArgv();
        }
        let parsed = Object.create(null);
        _.each(_.keys(this.argv), optionName => {
            parsed[optionName] = this.getOptionValue(optionName);
        });
        _.each(parsed, (value, originalOptionName) => {
            if (value === undefined) {
                return;
            }
            let optionName = this.getCorrectOptionName(originalOptionName);
            if (!_.includes(this.optionsWhiteList, optionName)) {
                if (!this.isOptionSupported(optionName)) {
                    this.$errors.failWithoutHelp(`The option '${originalOptionName}' is not supported. To see command's options, use '$ ${this.$staticConfig.CLIENT_NAME.toLowerCase()} help ${process.argv[2]}'. To see all commands use '$ ${this.$staticConfig.CLIENT_NAME.toLowerCase()} help'.`);
                }
                let optionType = this.getOptionType(optionName), optionValue = parsed[optionName];
                if (_.isArray(optionValue) && optionType !== OptionType.Array) {
                    this.$errors.fail("You have set the %s option multiple times. Check the correct command syntax below and try again.", originalOptionName);
                }
                else if (optionType === OptionType.String && helpers.isNullOrWhitespace(optionValue)) {
                    this.$errors.failWithoutHelp("The option '%s' requires non-empty value.", originalOptionName);
                }
                else if (optionType === OptionType.Array && optionValue.length === 0) {
                    this.$errors.failWithoutHelp(`The option '${originalOptionName}' requires one or more values, separated by a space.`);
                }
            }
        });
    }
    getCorrectOptionName(optionName) {
        let secondaryOptionName = this.getNonDashedOptionName(optionName);
        return _.includes(this.optionNames, secondaryOptionName) ? secondaryOptionName : optionName;
    }
    getOptionType(optionName) {
        let option = this.options[optionName] || this.tryGetOptionByAliasName(optionName);
        return option ? option.type : "";
    }
    tryGetOptionByAliasName(aliasName) {
        let option = _.find(this.options, opt => opt.alias === aliasName);
        return option;
    }
    isOptionSupported(option) {
        if (!this.options[option]) {
            let opt = this.tryGetOptionByAliasName(option);
            return !!opt;
        }
        return true;
    }
    getNonDashedOptionName(optionName) {
        let matchUpperCaseLetters = optionName.match(OptionsBase.NONDASHED_OPTION_REGEX);
        if (matchUpperCaseLetters) {
            let secondaryOptionName = matchUpperCaseLetters[1] + matchUpperCaseLetters[2].toUpperCase() + matchUpperCaseLetters[3] || '';
            return this.getNonDashedOptionName(secondaryOptionName);
        }
        return optionName;
    }
    getDashedOptionName(optionName) {
        let matchUpperCaseLetters = optionName.match(OptionsBase.DASHED_OPTION_REGEX);
        if (matchUpperCaseLetters) {
            let secondaryOptionName = `${matchUpperCaseLetters[1]}-${matchUpperCaseLetters[2].toLowerCase()}${matchUpperCaseLetters[3] || ''}`;
            return this.getDashedOptionName(secondaryOptionName);
        }
        return optionName;
    }
    setArgv() {
        let opts = {};
        _.each(this.options, (value, key) => {
            opts[this.getDashedOptionName(key)] = value;
        });
        this.argv = yargs(process.argv.slice(2)).options(opts).argv;
        this.adjustDashedOptions();
    }
    adjustDashedOptions() {
        _.each(this.optionNames, optionName => {
            Object.defineProperty(OptionsBase.prototype, optionName, {
                configurable: true,
                get: function () {
                    return this.getOptionValue(optionName);
                },
                set: function (value) {
                    this.argv[optionName] = value;
                }
            });
        });
    }
}
OptionsBase.DASHED_OPTION_REGEX = /(.+?)([A-Z])(.*)/;
OptionsBase.NONDASHED_OPTION_REGEX = /(.+?)[-]([a-zA-Z])(.*)/;
exports.OptionsBase = OptionsBase;
