class Optimist::Parser

# Optimist::with_standard_exception_handling.
# and consider calling it from within
# argument-parsing logic), call #parse to actually produce the output hash,
# If you want to instantiate this class yourself (for more complicated
#
# typically be called.
# #opt, #banner and #version, #depends, and #conflicts methods will
# will be handled internally by Optimist::options. In this case, only the
# The commandline parser. In typical usage, the methods in this class

def self.register(lookup, klass)

# The Option subclasses are responsible for registering themselves using this function.
def self.register(lookup, klass)
  @registry[lookup.to_sym] = klass
end

def self.registry_getopttype(type)

# Can be given either a class-name, e.g. Integer, a string, e.g "integer", or a symbol, e.g :integer
# Gets the class from the registry.
def self.registry_getopttype(type)
  return nil unless type
  if type.respond_to?(:name)
    type = type.name
    lookup = type.downcase.to_sym
  else
    lookup = type.to_sym
  end
  raise ArgumentError, "Unsupported argument type '#{type}', registry lookup '#{lookup}'" unless @registry.has_key?(lookup)
  return @registry[lookup].new
end

def banner(s)

# #opt to build a multi-section help page.
# Adds text to the help display. Can be interspersed with calls to
def banner(s)
  @order << [:text, s]
end

def cloaker(&b)

# thanks to _why: http://redhanded.hobix.com/inspect/aBlockCostume.html
# instance_eval but with ability to handle block arguments
def cloaker(&b)
  (class << self; self; end).class_eval do
    define_method :cloaker_, &b
    meth = instance_method :cloaker_
    remove_method :cloaker_
    meth
  end
end

def collect_argument_parameters(args, start_at)

def collect_argument_parameters(args, start_at)
  params = []
  pos = start_at
  while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos]) do
    params << args[pos]
    pos += 1
  end
  params
end

def conflicts(*syms)

# Marks two (or more!) options as conflicting.
def conflicts(*syms)
  syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
  @constraints << [:conflicts, syms]
end

def depends(*syms)

# better modeled with Optimist::die.
# undirected (i.e., mutual) dependencies. Directed dependencies are
# Marks two (or more!) options as requiring each other. Only handles
def depends(*syms)
  syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
  @constraints << [:depends, syms]
end

def die(arg, msg = nil, error_code = nil)

# The per-parser version of Optimist::die (see that for documentation).
def die(arg, msg = nil, error_code = nil)
  msg, error_code = nil, msg if msg.kind_of?(Integer)
  if msg
    $stderr.puts "Error: argument --#{@specs[arg].long} #{msg}."
  else
    $stderr.puts "Error: #{arg}."
  end
  if @educate_on_error
    $stderr.puts
    educate $stderr
  else
    $stderr.puts "Try --help for help."
  end
  exit(error_code || -1)
end

def each_arg(args)

# yield successive arg, parameter pairs
def each_arg(args)
  remains = []
  i = 0
  until i >= args.length
    return remains += args[i..-1] if @stop_words.member? args[i]
    case args[i]
    when /^--$/ # arg terminator
      return remains += args[(i + 1)..-1]
    when /^--(\S+?)=(.*)$/ # long argument with equals
      num_params_taken = yield "--#{$1}", [$2]
      if num_params_taken.nil?
        remains << args[i]
        if @stop_on_unknown
          return remains += args[i + 1..-1]
        end
      end
      i += 1
    when /^--(\S+)$/ # long argument
      params = collect_argument_parameters(args, i + 1)
      num_params_taken = yield args[i], params
      if num_params_taken.nil?
        remains << args[i]
        if @stop_on_unknown
          return remains += args[i + 1..-1]
        end
      else
        i += num_params_taken
      end
      i += 1
    when /^-(\S+)$/ # one or more short arguments
      short_remaining = ""
      shortargs = $1.split(//)
      shortargs.each_with_index do |a, j|
        if j == (shortargs.length - 1)
          params = collect_argument_parameters(args, i + 1)
          num_params_taken = yield "-#{a}", params
          unless num_params_taken
            short_remaining << a
            if @stop_on_unknown
              remains << "-#{short_remaining}"
              return remains += args[i + 1..-1]
            end
          else
            i += num_params_taken
          end
        else
          unless yield "-#{a}", []
            short_remaining << a
            if @stop_on_unknown
              short_remaining += shortargs[j + 1..-1].join
              remains << "-#{short_remaining}"
              return remains += args[i + 1..-1]
            end
          end
        end
      end
      unless short_remaining.empty?
        remains << "-#{short_remaining}"
      end
      i += 1
    else
      if @stop_on_unknown
        return remains += args[i..-1]
      else
        remains << args[i]
        i += 1
      end
    end
  end
  remains
end

def educate(stream = $stdout)

# Print the help message to +stream+.
def educate(stream = $stdout)
  width # hack: calculate it now; otherwise we have to be careful not to
  # call this unless the cursor's at the beginning of a line.
  left = {}
  @specs.each { |name, spec| left[name] = spec.educate }
  leftcol_width = left.values.map(&:length).max || 0
  rightcol_start = leftcol_width + 6 # spaces
  unless @order.size > 0 && @order.first.first == :text
    command_name = File.basename($0).gsub(/\.[^.]+$/, '')
    stream.puts "Usage: #{command_name} #{@usage}\n" if @usage
    stream.puts "#{@synopsis}\n" if @synopsis
    stream.puts if @usage || @synopsis
    stream.puts "#{@version}\n" if @version
    stream.puts "Options:"
  end
  @order.each do |what, opt|
    if what == :text
      stream.puts wrap(opt)
      next
    end
    spec = @specs[opt]
    stream.printf "  %-#{leftcol_width}s    ", left[opt]
    desc = spec.description_with_default
    stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
  end
end

def educate_on_error

# display the usage (via educate)
# Instead of displaying "Try --help for help." on an error
def educate_on_error
  @educate_on_error = true
end

def either(*syms)

# Marks two (or more!) options as required but mutually exclusive.
def either(*syms)
  syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
  @constraints << [:conflicts, syms]
  @constraints << [:either, syms]
end

def initialize(*a, &b)

# Initializes the parser, and instance-evaluates any block given.
def initialize(*a, &b)
  @version = nil
  @leftovers = []
  @specs = {}
  @long = {}
  @short = {}
  @order = []
  @constraints = []
  @stop_words = []
  @stop_on_unknown = false
  @educate_on_error = false
  @synopsis = nil
  @usage = nil
  # instance_eval(&b) if b # can't take arguments
  cloaker(&b).bind(self).call(*a) if b
end

def legacy_width

def legacy_width
  # Support for older Rubies where io/console is not available
  `tput cols`.to_i
rescue Errno::ENOENT
  80
end

def method_missing(m, *_args)

def method_missing(m, *_args)
  self[m] || self[m.to_s]
end

def opt(name, desc = "", opts = {}, &b)

def opt(name, desc = "", opts = {}, &b)
  opts[:callback] ||= b if block_given?
  opts[:desc] ||= desc
  o = Option.create(name, desc, opts)
  raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? o.name
  raise ArgumentError, "long option name #{o.long.inspect} is already taken; please specify a (different) :long" if @long[o.long]
  raise ArgumentError, "short option name #{o.short.inspect} is already taken; please specify a (different) :short" if @short[o.short]
  @long[o.long] = o.name
  @short[o.short] = o.name if o.short?
  @specs[o.name] = o
  @order << [:opt, o.name]
end

def parse(cmdline = ARGV)

# throws CommandlineError, HelpNeeded, and VersionNeeded exceptions.
#
# but you can call it directly if you need more control.
# Parses the commandline. Typically called by Optimist::options,
def parse(cmdline = ARGV)
  vals = {}
  required = {}
  opt :version, "Print version and exit" if @version && ! (@specs[:version] || @long["version"])
  opt :help, "Show this message" unless @specs[:help] || @long["help"]
  @specs.each do |sym, opts|
    required[sym] = true if opts.required?
    vals[sym] = opts.default
    vals[sym] = [] if opts.multi && !opts.default # multi arguments default to [], not nil
  end
  resolve_default_short_options!
  ## resolve symbols
  given_args = {}
  @leftovers = each_arg cmdline do |arg, params|
    ## handle --no- forms
    arg, negative_given = if arg =~ /^--no-([^-]\S*)$/
                            ["--#{$1}", true]
                          else
                            [arg, false]
                          end
    sym = case arg
          when /^-([^-])$/      then @short[$1]
          when /^--([^-]\S*)$/  then @long[$1] || @long["no-#{$1}"]
          else                       raise CommandlineError, "invalid argument syntax: '#{arg}'"
          end
    sym = nil if arg =~ /--no-/ # explicitly invalidate --no-no- arguments
    next nil if ignore_invalid_options && !sym
    raise CommandlineError, "unknown argument '#{arg}'" unless sym
    if given_args.include?(sym) && !@specs[sym].multi?
      raise CommandlineError, "option '#{arg}' specified multiple times"
    end
    given_args[sym] ||= {}
    given_args[sym][:arg] = arg
    given_args[sym][:negative_given] = negative_given
    given_args[sym][:params] ||= []
    # The block returns the number of parameters taken.
    num_params_taken = 0
    unless params.empty?
      if @specs[sym].single_arg?
        given_args[sym][:params] << params[0, 1]  # take the first parameter
        num_params_taken = 1
      elsif @specs[sym].multi_arg?
        given_args[sym][:params] << params        # take all the parameters
        num_params_taken = params.size
      end
    end
    num_params_taken
  end
  ## check for version and help args
  raise VersionNeeded if given_args.include? :version
  raise HelpNeeded if given_args.include? :help
  ## check constraint satisfaction
  @constraints.each do |type, syms|
    constraint_sym = syms.find { |sym| given_args[sym] }
    case type
    when :depends
      next unless constraint_sym
      syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym].long} requires --#{@specs[sym].long}" unless given_args.include? sym }
    when :conflicts
      next unless constraint_sym
      syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym].long} conflicts with --#{@specs[sym].long}" if given_args.include?(sym) && (sym != constraint_sym) }
    when :either
      raise CommandlineError, "one of #{syms.map { |sym| "--#{@specs[sym].long}" }.join(', ') } is required" if (syms & given_args.keys).size != 1
    end
  end
  required.each do |sym, val|
    raise CommandlineError, "option --#{@specs[sym].long} must be specified" unless given_args.include? sym
  end
  ## parse parameters
  given_args.each do |sym, given_data|
    arg, params, negative_given = given_data.values_at :arg, :params, :negative_given
    opts = @specs[sym]
    if params.empty? && !opts.flag?
      raise CommandlineError, "option '#{arg}' needs a parameter" unless opts.default
      params << (opts.array_default? ? opts.default.clone : [opts.default])
    end
    vals["#{sym}_given".intern] = true # mark argument as specified on the commandline
    vals[sym] = opts.parse(params, negative_given)
    if opts.single_arg?
      if opts.multi?        # multiple options, each with a single parameter
        vals[sym] = vals[sym].map { |p| p[0] }
      else                  # single parameter
        vals[sym] = vals[sym][0][0]
      end
    elsif opts.multi_arg? && !opts.multi?
      vals[sym] = vals[sym][0]  # single option, with multiple parameters
    end
    # else: multiple options, with multiple parameters
    opts.callback.call(vals[sym]) if opts.callback
  end
  ## modify input in place with only those
  ## arguments we didn't process
  cmdline.clear
  @leftovers.each { |l| cmdline << l }
  ## allow openstruct-style accessors
  class << vals
    def method_missing(m, *_args)
      self[m] || self[m.to_s]
    end
  end
  vals
end

def resolve_default_short_options!

def resolve_default_short_options!
  @order.each do |type, name|
    opts = @specs[name]
    next if type != :opt || opts.short
    c = opts.long.split(//).find { |d| d !~ INVALID_SHORT_ARG_REGEX && !@short.member?(d) }
    if c # found a character to use
      opts.short = c
      @short[c] = name
    end
  end
end

def stop_on(*words)

# shifting the subcommand off of ARGV.
# invocation would then be used to parse subcommand options, after
# would be set to the list of subcommands. A subsequent Optimist
# A typical use case would be for subcommand support, where these
#
# intact.
# parsed as usual, and options to the right of the word are left
# encountered, such that any options to the left of the word are
# Defines a set of words which cause parsing to terminate when
def stop_on(*words)
  @stop_words = [*words].flatten
end

def stop_on_unknown

# i.e., without first parsing the global options.
# cases where you don't know the set of subcommands ahead of time,
# (unless it is a parameter for an argument). This is useful for
# Similar to #stop_on, but stops on any unknown word when encountered
def stop_on_unknown
  @stop_on_unknown = true
end

def synopsis(s = nil)

# usage line, or as the first line if usage isn't specified.
# Adds a synopsis (command summary description) right below the
def synopsis(s = nil)
  s ? @synopsis = s : @synopsis
end

def usage(s = nil)

# lines.
# first line in the help (educate) output and ending in two new
# Sets the usage string. If set the message will be printed as the
def usage(s = nil)
  s ? @usage = s : @usage
end

def version(s = nil)

# ".
# on the commandline. Should probably be of the form "
# Sets the version string. If set, the user can request the version
def version(s = nil)
  s ? @version = s : @version
end

def width #:nodoc:

:nodoc:
def width #:nodoc:
  @width ||= if $stdout.tty?
               begin
                 require 'io/console'
                 w = IO.console.winsize.last
                 w.to_i > 0 ? w : 80
               rescue LoadError, NoMethodError, Errno::ENOTTY, Errno::EBADF, Errno::EINVAL
                 legacy_width
               end
             else
               80
             end
end

def wrap(str, opts = {}) # :nodoc:

:nodoc:
def wrap(str, opts = {}) # :nodoc:
  if str == ""
    [""]
  else
    inner = false
    str.split("\n").map do |s|
      line = wrap_line s, opts.merge(:inner => inner)
      inner = true
      line
    end.flatten
  end
end

def wrap_line(str, opts = {})

def wrap_line(str, opts = {})
  prefix = opts[:prefix] || 0
  width = opts[:width] || (self.width - 1)
  start = 0
  ret = []
  until start > str.length
    nextt =
    if start + width >= str.length
      str.length
    else
      x = str.rindex(/\s/, start + width)
      x = str.index(/\s/, start) if x && x < start
      x || str.length
    end
    ret << ((ret.empty? && !opts[:inner]) ? "" : " " * prefix) + str[start...nextt]
    start = nextt + 1
  end
  ret
end