# We handle the parsing of options, and subsequently as a singleton
# object to be queried for option values
class Options

  require 'singleton'
  require 'getoptlong'

  include Singleton

  # the name of the output directory
  attr_accessor :op_dir
  
  # include private and protected methods in the
  # output
  attr_accessor :show_all
  
  # name of the file, class or module to display in
  # the initial index page (if not specified
  # the first file we encounter is used)
  attr_accessor :main_page

  # Don't display progress as we process the files
  attr_reader :quiet

  # description of the output generator (set with the <tt>-fmt</tt>
  # option
  attr_accessor :generator

  # and the list of files to be processed
  attr_reader :files

  # array of directories to search for files to satisfy an :include:
  attr_reader :rdoc_include

  # title to be used out the output
  attr_reader :title

  # template to be used when generating output
  attr_reader :template

  # should diagrams be drawn
  attr_reader :diagram

  # should we draw fileboxes in diagrams
  attr_reader :fileboxes

  # include the '#' at the front of hyperlinked instance method names
  attr_reader :show_hash

  module OptionList
    OPTION_LIST = [
      [ "--all",       "-a",   GetoptLong::NO_ARGUMENT ],
      [ "--debug",     "-D",   GetoptLong::NO_ARGUMENT ],
      [ "--diagram",   "-d",   GetoptLong::NO_ARGUMENT ],
      [ "--fileboxes", "-F",   GetoptLong::NO_ARGUMENT ],
      [ "--fmt",       "-f",   GetoptLong::REQUIRED_ARGUMENT ],
      [ "--help",      "-h",   GetoptLong::NO_ARGUMENT ],
      [ "--include",   "-i",   GetoptLong::REQUIRED_ARGUMENT ],
      [ "--main",      "-m",   GetoptLong::REQUIRED_ARGUMENT ],
      [ "--op",        "-o",   GetoptLong::REQUIRED_ARGUMENT ],
      [ "--quiet",     "-q",   GetoptLong::NO_ARGUMENT ],
      [ "--show_hash", "-H",   GetoptLong::NO_ARGUMENT ],
      [ "--template",  "-T",   GetoptLong::REQUIRED_ARGUMENT ],
      [ "--title",     "-t",   GetoptLong::REQUIRED_ARGUMENT ],
      [ "--version",   "-v",   GetoptLong::NO_ARGUMENT ],
    ]

    def OptionList.options
      OPTION_LIST
    end

    # Show usage and exit
    
    def OptionList.usage(generator_names, msg = nil)
      
      $stderr.puts
      $stderr.puts(msg) if msg
      
      name = File.basename($0)
      $stderr.puts "
usage:

  #{name} [options]  [names...]

options:

  --all        include all methods (not just public) in the output
  --quiet      don't show progress as we parse
  --main name  make 'name' the page displayed in the index page
  --op dir     set the output directory
  --version    display  RDoc's version
  --fileboxes  classes are put in boxes which represents files, where these
               classes resides. Classes shared between more than one file are
               shown with list of files that sharing them. Needs --diagram
               to be turned on. If not, this option is silently discarded.
               Experimental.
  --fmt fmt    set the output formatter (#{generator_names.join(', ')})
  --include dir[,dir...]
               set (or add to) the list of directories to be searched
               when satisfying :include: requests. Can be used more
               than once.
  --show_hash  A name of the form #name in a comment is a possible hyperlink
               to an instance method name. When displayed, the '#' is
               removed unless this option is specified
  --title txt  Set 'txt' as the title for the output
  --template name
               Set the template used when generating output
  --diagram    Generate diagrams showing modules and classes
"
    exit 1
    end
  end

  # Parse command line options. We're passed a hash containing
  # output generators, keyed by the generator name

  def parse(argv, generators)
    old_argv = ARGV.dup
    begin
      ARGV.replace(argv)
      @op_dir = "doc"
      @show_all = false
      @main_page = nil
      @quiet = false
      @generator = generators["html"]
      @rdoc_include = []
      @title = "RDoc Documentation"
      @template = 'standard'
      @diagram = false
      @fileboxes = false
      @show_hash = false

      go = GetoptLong.new(*OptionList.options)
      go.quiet = true
      go.each do |opt, arg|
	case opt
	when "--all"       then @show_all  = true
	when "--debug"     then $DEBUG     = true
	when "--main"      then @main_page = arg
	when "--op"        then @op_dir    = arg
	when "--quiet"     then @quiet     = true
        when "--help"      then OptionList.usage(generators.keys, VERSION_STRING)
        when "--include"   then @rdoc_include.concat arg.split(/\s*,\s*/)
        when "--show_hash" then @show_hash = true
        when "--template"  then @template  = arg
        when "--title"     then @title     = arg

        when "--diagram"
          check_diagram
          @diagram = true
        when "--fileboxes"
          @fileboxes = true if @diagram
          

	when "--fmt"
	  @generator = generators[arg.downcase]
	  OptionList.usage(generators.keys, "Invalid output formatter") unless @generator

	when "--version"
	  $stderr.puts VERSION_STRING
	  exit
	end
      end

      @files = ARGV.dup

      @rdoc_include << "." if @rdoc_include.empty?

      check_files

    rescue GetoptLong::InvalidOption => error
      OptionList.usage(generators.keys, error.message)
    ensure
      ARGV.replace(old_argv)
    end
  end

  private

  # Check that the right version of 'dot' is available
  def check_diagram
    ok = false
    IO.popen("dot -V 2>&1") do |io|
      ver = io.read
      if ver =~ /dot\s+version(?:\s+gviz)?\s+(\d+)\.(\d+)/
        ok = ($1.to_i > 1) || ($1.to_i == 1 && $2.to_i >= 7)
      end
    end
    unless ok
      OptionList.usage(generators.keys, 
            "You need dot V1.7 or later to use the --diagram option\n"+
            "(see http://www.research.att.com/sw/tools/graphviz/)")
    end
  end
  
  # Check that the files on the command line exist
  
  def check_files
    @files.each do |f|
      stat = File.stat f rescue error("File not found: #{f}")
      error("File '#{f}' not readable") unless stat.readable?
    end
  end

  def error(str)
    $stderr.puts str
    exit(1)
  end
end
