// 
// Decompiled by Procyon v0.6.0
// 

package org.jline.builtins;

import org.jline.utils.AttributedCharSequence;
import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedString;
import org.jline.utils.StyleResolver;
import java.util.regex.Matcher;
import java.util.Objects;
import java.util.Collections;
import java.util.Collection;
import java.util.Arrays;
import java.util.HashMap;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.function.Function;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

public class Options
{
    public static final String NL;
    private static final String regex = "(?x)\\s*(?:-([^-]))?(?:,?\\s*-(\\w))?(?:,?\\s*--(\\w[\\w-]*)(=\\w+)?)?(?:,?\\s*--(\\w[\\w-]*))?.*?(?:\\(default=(.*)\\))?\\s*";
    private static final int GROUP_SHORT_OPT_1 = 1;
    private static final int GROUP_SHORT_OPT_2 = 2;
    private static final int GROUP_LONG_OPT_1 = 3;
    private static final int GROUP_ARG_1 = 4;
    private static final int GROUP_LONG_OPT_2 = 5;
    private static final int GROUP_DEFAULT = 6;
    private static final Pattern parser;
    private static final Pattern uname;
    private final Map<String, Boolean> unmodifiableOptSet;
    private final Map<String, Object> unmodifiableOptArg;
    private final Map<String, Boolean> optSet;
    private final Map<String, Object> optArg;
    private final Map<String, String> optName;
    private final Map<String, String> optAlias;
    private final List<Object> xargs;
    private List<String> args;
    private static final String UNKNOWN = "unknown";
    private String usageName;
    private int usageIndex;
    private final String[] spec;
    private final String[] gspec;
    private final String defOpts;
    private final String[] defArgs;
    private String error;
    private boolean optionsFirst;
    private boolean stopOnBadOption;
    
    public static Options compile(final String[] optSpec) {
        return new Options(optSpec, null, null, System::getenv);
    }
    
    public static Options compile(final String[] optSpec, final Function<String, String> env) {
        return new Options(optSpec, null, null, env);
    }
    
    public static Options compile(final String optSpec) {
        return compile(optSpec.split("\\n"), System::getenv);
    }
    
    public static Options compile(final String optSpec, final Function<String, String> env) {
        return compile(optSpec.split("\\n"), env);
    }
    
    public static Options compile(final String[] optSpec, final Options gopt) {
        return new Options(optSpec, null, gopt, System::getenv);
    }
    
    public static Options compile(final String[] optSpec, final String[] gspec) {
        return new Options(optSpec, gspec, null, System::getenv);
    }
    
    public Options setStopOnBadOption(final boolean stopOnBadOption) {
        this.stopOnBadOption = stopOnBadOption;
        return this;
    }
    
    public Options setOptionsFirst(final boolean optionsFirst) {
        this.optionsFirst = optionsFirst;
        return this;
    }
    
    public boolean isSet(final String name) {
        final Boolean isSet = this.optSet.get(name);
        if (isSet == null) {
            throw new IllegalArgumentException("option not defined in spec: " + name);
        }
        return isSet;
    }
    
    public Object getObject(final String name) {
        if (!this.optArg.containsKey(name)) {
            throw new IllegalArgumentException("option not defined with argument: " + name);
        }
        final List<Object> list = this.getObjectList(name);
        return list.isEmpty() ? "" : list.get(list.size() - 1);
    }
    
    public List<Object> getObjectList(final String name) {
        final Object arg = this.optArg.get(name);
        if (arg == null) {
            throw new IllegalArgumentException("option not defined with argument: " + name);
        }
        List<Object> list;
        if (arg instanceof String) {
            list = new ArrayList<Object>();
            if (!"".equals(arg)) {
                list.add(arg);
            }
        }
        else {
            list = (List)arg;
        }
        return list;
    }
    
    public List<String> getList(final String name) {
        final ArrayList<String> list = new ArrayList<String>();
        for (final Object o : this.getObjectList(name)) {
            try {
                list.add((String)o);
            }
            catch (final ClassCastException e) {
                throw new IllegalArgumentException("option not String: " + name);
            }
        }
        return list;
    }
    
    private void addArg(final String name, final Object value) {
        final Object arg = this.optArg.get(name);
        List<Object> list;
        if (arg instanceof String) {
            list = new ArrayList<Object>();
            this.optArg.put(name, list);
        }
        else {
            list = (List)arg;
        }
        list.add(value);
    }
    
    public String get(final String name) {
        try {
            return (String)this.getObject(name);
        }
        catch (final ClassCastException e) {
            throw new IllegalArgumentException("option not String: " + name);
        }
    }
    
    public int getNumber(final String name) {
        final String number = this.get(name);
        try {
            if (number != null) {
                return Integer.parseInt(number);
            }
            return 0;
        }
        catch (final NumberFormatException e) {
            throw new IllegalArgumentException("option '" + name + "' not Number: " + number);
        }
    }
    
    public List<Object> argObjects() {
        return this.xargs;
    }
    
    public List<String> args() {
        if (this.args == null) {
            this.args = new ArrayList<String>();
            for (final Object arg : this.xargs) {
                this.args.add((arg == null) ? "null" : arg.toString());
            }
        }
        return this.args;
    }
    
    public void usage(final PrintStream err) {
        err.print(this.usage());
    }
    
    public String usage() {
        final StringBuilder buf = new StringBuilder();
        int index = 0;
        if (this.error != null) {
            buf.append(this.error);
            buf.append(Options.NL);
            index = this.usageIndex;
        }
        for (int i = index; i < this.spec.length; ++i) {
            buf.append(this.spec[i]);
            buf.append(Options.NL);
        }
        return buf.toString();
    }
    
    public IllegalArgumentException usageError(final String s) {
        this.error = this.usageName + ": " + s;
        return new IllegalArgumentException(this.error);
    }
    
    private Options(final String[] spec, final String[] gspec, final Options opt, final Function<String, String> env) {
        this.optSet = new HashMap<String, Boolean>();
        this.optArg = new HashMap<String, Object>();
        this.optName = new HashMap<String, String>();
        this.optAlias = new HashMap<String, String>();
        this.xargs = new ArrayList<Object>();
        this.args = null;
        this.usageName = "unknown";
        this.usageIndex = 0;
        this.error = null;
        this.optionsFirst = false;
        this.stopOnBadOption = false;
        this.gspec = gspec;
        if (gspec == null && opt == null) {
            this.spec = spec;
        }
        else {
            final ArrayList<String> list = new ArrayList<String>();
            list.addAll(Arrays.asList(spec));
            list.addAll(Arrays.asList((gspec != null) ? gspec : opt.gspec));
            this.spec = list.toArray(new String[list.size()]);
        }
        final Map<String, Boolean> myOptSet = new HashMap<String, Boolean>();
        final Map<String, Object> myOptArg = new HashMap<String, Object>();
        this.parseSpec(myOptSet, myOptArg);
        if (opt != null) {
            for (final Map.Entry<String, Boolean> e : opt.optSet.entrySet()) {
                if (e.getValue()) {
                    myOptSet.put(e.getKey(), true);
                }
            }
            for (final Map.Entry<String, Object> e2 : opt.optArg.entrySet()) {
                if (!e2.getValue().equals("")) {
                    myOptArg.put(e2.getKey(), e2.getValue());
                }
            }
            opt.reset();
        }
        this.unmodifiableOptSet = Collections.unmodifiableMap((Map<? extends String, ? extends Boolean>)myOptSet);
        this.unmodifiableOptArg = Collections.unmodifiableMap((Map<? extends String, ?>)myOptArg);
        this.defOpts = ((env != null) ? env.apply(this.usageName.toUpperCase() + "_OPTS") : null);
        this.defArgs = ((this.defOpts != null) ? this.defOpts.split("\\s+") : new String[0]);
    }
    
    private void parseSpec(final Map<String, Boolean> myOptSet, final Map<String, Object> myOptArg) {
        int index = 0;
        for (final String line : this.spec) {
            final Matcher m = Options.parser.matcher(line);
            if (m.matches()) {
                final String opt = m.group(3);
                final String name = (opt != null) ? opt : m.group(1);
                if (name != null && myOptSet.putIfAbsent(name, false) != null) {
                    throw new IllegalArgumentException("duplicate option in spec: --" + name);
                }
                final String dflt = (m.group(6) != null) ? m.group(6) : "";
                if (m.group(4) != null) {
                    myOptArg.put(opt, dflt);
                }
                final String opt2 = m.group(5);
                if (opt2 != null) {
                    this.optAlias.put(opt2, opt);
                    myOptSet.put(opt2, false);
                    if (m.group(4) != null) {
                        myOptArg.put(opt2, "");
                    }
                }
                for (int i = 0; i < 2; ++i) {
                    final String sopt = m.group((i == 0) ? 1 : 2);
                    if (sopt != null && this.optName.putIfAbsent(sopt, name) != null) {
                        throw new IllegalArgumentException("duplicate option in spec: -" + sopt);
                    }
                }
            }
            if (Objects.equals(this.usageName, "unknown")) {
                final Matcher u = Options.uname.matcher(line);
                if (u.find()) {
                    this.usageName = u.group(1);
                    this.usageIndex = index;
                }
            }
            ++index;
        }
    }
    
    private void reset() {
        this.optSet.clear();
        this.optSet.putAll(this.unmodifiableOptSet);
        this.optArg.clear();
        this.optArg.putAll(this.unmodifiableOptArg);
        this.xargs.clear();
        this.args = null;
        this.error = null;
    }
    
    public Options parse(final Object[] argv) {
        return this.parse(argv, false);
    }
    
    public Options parse(final List<?> argv) {
        return this.parse(argv, false);
    }
    
    public Options parse(final Object[] argv, final boolean skipArg0) {
        if (null == argv) {
            throw new IllegalArgumentException("argv is null");
        }
        return this.parse(Arrays.asList(argv), skipArg0);
    }
    
    public Options parse(final List<?> argv, boolean skipArg0) {
        this.reset();
        final List<Object> args = new ArrayList<Object>();
        args.addAll(Arrays.asList(this.defArgs));
        for (final Object arg : argv) {
            if (skipArg0) {
                skipArg0 = false;
                this.usageName = arg.toString();
            }
            else {
                args.add(arg);
            }
        }
        String needArg = null;
        String needOpt = null;
        boolean endOpt = false;
        for (final Object oarg : args) {
            final String arg2 = (oarg == null) ? "null" : oarg.toString();
            if (endOpt) {
                this.xargs.add(oarg);
            }
            else if (needArg != null) {
                this.addArg(needArg, oarg);
                needArg = null;
                needOpt = null;
            }
            else if (!arg2.startsWith("-") || (arg2.length() > 1 && Character.isDigit(arg2.charAt(1))) || "-".equals(oarg)) {
                if (this.optionsFirst) {
                    endOpt = true;
                }
                this.xargs.add(oarg);
            }
            else if (arg2.equals("--")) {
                endOpt = true;
            }
            else if (arg2.startsWith("--")) {
                final int eq = arg2.indexOf("=");
                final String value = (eq == -1) ? null : arg2.substring(eq + 1);
                String name = arg2.substring(2, (eq == -1) ? arg2.length() : eq);
                final List<String> names = new ArrayList<String>();
                if (this.optSet.containsKey(name)) {
                    names.add(name);
                }
                else {
                    for (final String k : this.optSet.keySet()) {
                        if (k.startsWith(name)) {
                            names.add(k);
                        }
                    }
                }
                switch (names.size()) {
                    case 1: {
                        name = names.get(0);
                        this.optSet.put(name, true);
                        if (this.optArg.containsKey(name)) {
                            if (value != null) {
                                this.addArg(name, value);
                                continue;
                            }
                            needArg = name;
                            continue;
                        }
                        else {
                            if (value != null) {
                                throw this.usageError("option '--" + name + "' doesn't allow an argument");
                            }
                            continue;
                        }
                        break;
                    }
                    case 0: {
                        if (this.stopOnBadOption) {
                            endOpt = true;
                            this.xargs.add(oarg);
                            continue;
                        }
                        throw this.usageError("invalid option '--" + name + "'");
                    }
                    default: {
                        throw this.usageError("option '--" + name + "' is ambiguous: " + names);
                    }
                }
            }
            else {
                for (int i = 1; i < arg2.length(); ++i) {
                    final String c = String.valueOf(arg2.charAt(i));
                    if (this.optName.containsKey(c)) {
                        final String name = this.optName.get(c);
                        this.optSet.put(name, true);
                        if (this.optArg.containsKey(name)) {
                            final int j = i + 1;
                            if (j < arg2.length()) {
                                this.addArg(name, arg2.substring(j));
                                break;
                            }
                            needOpt = c;
                            needArg = name;
                            break;
                        }
                    }
                    else {
                        if (!this.stopOnBadOption) {
                            throw this.usageError("invalid option '" + c + "'");
                        }
                        this.xargs.add("-" + c);
                        endOpt = true;
                    }
                }
            }
        }
        if (needArg != null) {
            final String name2 = (needOpt != null) ? needOpt : ("--" + needArg);
            throw this.usageError("option '" + name2 + "' requires an argument");
        }
        for (final Map.Entry<String, String> alias : this.optAlias.entrySet()) {
            if (this.optSet.get(alias.getKey())) {
                this.optSet.put(alias.getValue(), true);
                if (this.optArg.containsKey(alias.getKey())) {
                    this.optArg.put(alias.getValue(), this.optArg.get(alias.getKey()));
                }
            }
            this.optSet.remove(alias.getKey());
            this.optArg.remove(alias.getKey());
        }
        return this;
    }
    
    @Override
    public String toString() {
        return "isSet" + this.optSet + "\nArg" + this.optArg + "\nargs" + this.xargs;
    }
    
    static {
        NL = System.getProperty("line.separator", "\n");
        parser = Pattern.compile("(?x)\\s*(?:-([^-]))?(?:,?\\s*-(\\w))?(?:,?\\s*--(\\w[\\w-]*)(=\\w+)?)?(?:,?\\s*--(\\w[\\w-]*))?.*?(?:\\(default=(.*)\\))?\\s*");
        uname = Pattern.compile("^Usage:\\s+(\\w+)");
    }
    
    public static class HelpException extends Exception
    {
        public HelpException(final String message) {
            super(message);
        }
        
        public static StyleResolver defaultStyle() {
            return Styles.helpStyle();
        }
        
        public static AttributedString highlight(final String msg, final StyleResolver resolver) {
            final Matcher tm = Pattern.compile("(^|\\n)(Usage|Summary)(:)").matcher(msg);
            if (tm.find()) {
                final boolean subcommand = tm.group(2).equals("Summary");
                final AttributedStringBuilder asb = new AttributedStringBuilder(msg.length());
                final AttributedStringBuilder acommand = new AttributedStringBuilder().append(msg.substring(0, tm.start(2))).styleMatches(Pattern.compile("(?:^\\s*)([a-z]+[a-zA-Z0-9-]*)\\b"), Collections.singletonList(resolver.resolve(".co")));
                asb.append(acommand);
                asb.styled(resolver.resolve(".ti"), tm.group(2)).append(":");
                for (final String line : msg.substring(tm.end(3)).split("\n")) {
                    final int ind = line.lastIndexOf("  ");
                    String syntax;
                    String comment;
                    if (ind > 20) {
                        syntax = line.substring(0, ind);
                        comment = line.substring(ind + 1);
                    }
                    else {
                        syntax = line;
                        comment = "";
                    }
                    asb.append(_highlightSyntax(syntax, resolver, subcommand));
                    asb.append(_highlightComment(comment, resolver));
                    asb.append("\n");
                }
                return asb.toAttributedString();
            }
            return AttributedString.fromAnsi(msg);
        }
        
        public static AttributedString highlightSyntax(final String syntax, final StyleResolver resolver, final boolean subcommands) {
            return _highlightSyntax(syntax, resolver, subcommands).toAttributedString();
        }
        
        public static AttributedString highlightSyntax(final String syntax, final StyleResolver resolver) {
            return _highlightSyntax(syntax, resolver, false).toAttributedString();
        }
        
        public static AttributedString highlightComment(final String comment, final StyleResolver resolver) {
            return _highlightComment(comment, resolver).toAttributedString();
        }
        
        private static AttributedStringBuilder _highlightSyntax(final String syntax, final StyleResolver resolver, final boolean subcommand) {
            final StringBuilder indent = new StringBuilder();
            for (final char c : syntax.toCharArray()) {
                if (c != ' ') {
                    break;
                }
                indent.append(c);
            }
            final AttributedStringBuilder asyntax = new AttributedStringBuilder().append(syntax.substring(indent.length()));
            asyntax.styleMatches(Pattern.compile("(?:^)([a-z]+[a-zA-Z0-9-]*)\\b"), Collections.singletonList(resolver.resolve(".co")));
            if (!subcommand) {
                asyntax.styleMatches(Pattern.compile("(?:<|\\[|\\s|=)([A-Za-z]+[A-Za-z_-]*)\\b"), Collections.singletonList(resolver.resolve(".ar")));
                asyntax.styleMatches(Pattern.compile("(?:^|\\s|\\[)(-\\$|-\\?|[-]{1,2}[A-Za-z-]+\\b)"), Collections.singletonList(resolver.resolve(".op")));
            }
            return new AttributedStringBuilder().append(indent).append(asyntax);
        }
        
        private static AttributedStringBuilder _highlightComment(final String comment, final StyleResolver resolver) {
            final AttributedStringBuilder acomment = new AttributedStringBuilder().append(comment);
            acomment.styleMatches(Pattern.compile("(?:\\s|\\[)(-\\$|-\\?|[-]{1,2}[A-Za-z-]+\\b)"), Collections.singletonList(resolver.resolve(".op")));
            acomment.styleMatches(Pattern.compile("(?:\\s)([a-z]+[-]+[a-z]+|[A-Z_]{2,})(?:\\s)"), Collections.singletonList(resolver.resolve(".ar")));
            return acomment;
        }
    }
}
