class Thor

def self.convert_task_options(opts)

def self.convert_task_options(opts)
  opts.map do |key, value|
    if value == true
      "--#{key}"
    elsif value.is_a?(Array)
      value.map {|v| "--#{key} #{v.inspect}"}.join(" ")
    else
      "--#{key} #{value.inspect}"
    end
  end.join(" ")    
end  

def self.desc(usage, description)

def self.desc(usage, description)
  @usage, @desc = usage, description
end

def self.format_opts(opts)

def self.format_opts(opts)
  return "" if !opts
  opts.map do |opt, val|
    if val == true || val == "BOOLEAN"
      "[#{opt}]"
    elsif val == "REQUIRED"
      opt + "=" + opt.gsub(/\-/, "").upcase
    elsif val == "OPTIONAL"
      "[" + opt + "=" + opt.gsub(/\-/, "").upcase + "]"
    end
  end.join(" ")
end

def self.help_list

def self.help_list
  return nil unless @usages
  @help_list ||= begin
    max_usage = @usages.max {|x,y| x.last.to_s.size <=> y.last.to_s.size}.last.size
    max_opts  = @opts.empty? ? 0 : format_opts(@opts.max {|x,y| x.last.to_s.size <=> y.last.to_s.size}.last).size 
    max_desc  = @descriptions.max {|x,y| x.last.to_s.size <=> y.last.to_s.size}.last.size
    Struct.new(:klass, :usages, :opts, :descriptions, :max).new(
      self, @usages, @opts, @descriptions, Struct.new(:usage, :opt, :desc).new(max_usage, max_opts, max_desc)
    )
  end
end

def self.inherited(klass)

def self.inherited(klass)
  subclass_files[File.expand_path(caller[0].split(":")[0])] << klass
  subclasses << klass unless subclasses.include?(klass)
end

def self.install_task

def self.install_task
  package_task
  
  self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
    desc "install", "install the gem"
    def install
      old_stderr, $stderr = $stderr.dup, File.open("/dev/null", "w")
      package
      $stderr = old_stderr
      system %{sudo gem install pkg/#{GEM}-#{GEM_VERSION} --no-rdoc --no-ri --no-update-sources}
    end
  RUBY
end

def self.map(map)

def self.map(map)
  @map ||= superclass.instance_variable_get("@map") || {}
  @map.merge! map
end

def self.method_added(meth)

def self.method_added(meth)
  meth = meth.to_s
  return if !public_instance_methods.include?(meth) || !@usage
  @descriptions ||= []
  @usages ||= []
  @opts ||= []
  @descriptions.delete(@descriptions.assoc(meth))
  @descriptions << [meth, @desc]
  @usages.delete(@usages.assoc(meth))
  @usages << [meth, @usage]
  
  @opts.delete(@opts.assoc(meth))    
  @opts << [meth, @method_options] if @method_options
  @usage, @desc, @method_options = nil
end

def self.method_options(opts)

def self.method_options(opts)
  @method_options = opts.inject({}) do |accum, (k,v)|
    accum.merge("--" + k.to_s => v.to_s.upcase)
  end
end

def self.package_task

def self.package_task
  self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
    desc "package", "package up the gem"
    def package
      FileUtils.mkdir_p(File.join(Dir.pwd, "pkg"))
      Gem::Builder.new(SPEC).build
      FileUtils.mv(SPEC.file_name, File.join(Dir.pwd, "pkg", SPEC.file_name))
    end    
  RUBY
end

def self.spec_task(file_list, opts = {})

def self.spec_task(file_list, opts = {})
  name = opts.delete(:name) || "spec"
  rcov_dir = opts.delete(:rcov_dir) || "coverage"
  file_list = file_list.map {|f| %["#{f}"]}.join(" ")
  verbose = opts.delete(:verbose)
  opts = {:format => "specdoc", :color => true}.merge(opts)
  
  rcov_opts = convert_task_options(opts.delete(:rcov) || {})
  rcov = !rcov_opts.empty?
  options = convert_task_options(opts)
  
  if rcov
    FileUtils.rm_rf(File.join(Dir.pwd, rcov_dir))
  end
  
  self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
    desc("#{name}", "spec task")
    def #{name}
      cmd = "ruby "
      if #{rcov.inspect}
        cmd << "-S rcov -o #{rcov_dir} #{rcov_opts.inspect[1...-1]} "
      end
      cmd << `which spec`.chomp
      cmd << " -- " if #{rcov.inspect}
      cmd << " "
      cmd << #{file_list.inspect}
      cmd << " "
      cmd << #{options.inspect}
      puts cmd if #{verbose.inspect}
      system(cmd)
    end
  RUBY
end

def self.start

def self.start
  meth = ARGV.shift
  params = []
  while !ARGV.empty?
    break if ARGV.first =~ /^\-/
    params << ARGV.shift
  end
  if defined?(@map) && @map[meth]
    meth = @map[meth].to_s
  end
  
  args = ARGV.dup
  
  if @opts.assoc(meth)
    opts = @opts.assoc(meth).last.map {|opt, val| [opt, val == true ? Getopt::BOOLEAN : Getopt.const_get(val)].flatten}
    options = Getopt::Long.getopts(*opts)
    params << options
  end
  
  ARGV.replace args
  
  new(meth, params).instance_variable_get("@results")
end

def self.subclass_files

def self.subclass_files
  @subclass_files ||= Hash.new {|h,k| h[k] = []}
end

def self.subclasses

def self.subclasses
  @subclasses ||= []
end

def help

def help
  list = self.class.help_list
  puts "Options"
  puts "-------"
  list.usages.each do |meth, use|
    format = "%-" + (list.max.usage + list.max.opt + 4).to_s + "s"
    print format % ("#{usage(meth)}")
    puts  list.descriptions.assoc(meth)[1]
  end
end

def initialize(op, params)

def initialize(op, params)
  begin
    op ||= "help"
    @results = send(op.to_sym, *params) if public_methods.include?(op) || !methods.include?(op)
  rescue ArgumentError
    puts "`#{op}' was called incorrectly. Call as `#{usage(op)}'"
  end
end

def usage(meth)

def usage(meth)
  list = self.class.help_list
  list.usages.assoc(meth)[1] + (list.opts.assoc(meth) ? " " + self.class.format_opts(list.opts.assoc(meth)[1]) : "")
end