#
# config.rb
#

if idx = ARGV.index(/\A--rbconfig=/)
  require ARGV.delete_at(idx).split(/=/, 2)[1]
else
  require 'rbconfig'
end

class ConfigTable

  c = ::Config::CONFIG

  rubypath = c['bindir'] + '/' + c['ruby_install_name']

  major = c['MAJOR'].to_i
  minor = c['MINOR'].to_i
  teeny = c['TEENY'].to_i
  version = "#{major}.#{minor}"

  # ruby ver. >= 1.4.4?
  newpath_p = ((major >= 2) or
               ((major == 1) and
                ((minor >= 5) or
                 ((minor == 4) and (teeny >= 4)))))
  
  subprefix = lambda {|path|
    path.sub(/\A#{Regexp.quote(c['prefix'])}/o, '$prefix')
  }

  if c['rubylibdir']
    # V < 1.6.3
    stdruby    = subprefix.call(c['rubylibdir'])
    siteruby   = subprefix.call(c['sitedir'])
    versite    = subprefix.call(c['sitelibdir'])
    sodir      = subprefix.call(c['sitearchdir'])
  elsif newpath_p
    # 1.4.4 <= V <= 1.6.3
    stdruby    = "$prefix/lib/ruby/#{version}"
    siteruby   = subprefix.call(c['sitedir'])
    versite    = siteruby + '/' + version
    sodir      = "$site-ruby/#{c['arch']}"
  else
    # V < 1.4.4
    stdruby    = "$prefix/lib/ruby/#{version}"
    siteruby   = "$prefix/lib/ruby/#{version}/site_ruby"
    versite    = siteruby
    sodir      = "$site-ruby/#{c['arch']}"
  end

  common_descripters = [
    [ 'prefix',    [ c['prefix'],
                     'path',
                     'path prefix of target environment' ] ],
    [ 'std-ruby',  [ stdruby,
                     'path',
                     'the directory for standard ruby libraries' ] ],
    [ 'site-ruby-common', [ siteruby,
                     'path',
                     'the directory for version-independent non-standard ruby libraries' ] ],
    [ 'site-ruby', [ versite,
                     'path',
                     'the directory for non-standard ruby libraries' ] ],
    [ 'bin-dir',   [ '$prefix/bin',
                     'path',
                     'the directory for commands' ] ],
    [ 'rb-dir',    [ '$site-ruby',
                     'path',
                     'the directory for ruby scripts' ] ],
    [ 'so-dir',    [ sodir,
                     'path',
                     'the directory for ruby extentions' ] ],
    [ 'data-dir',  [ '$prefix/share',
                     'path',
                     'the directory for shared data' ] ],
    [ 'ruby-path', [ rubypath,
                     'path',
                     'path to set to #! line' ] ],
    [ 'ruby-prog', [ rubypath,
                     'name',
                     'the ruby program using for installation' ] ],
    [ 'make-prog', [ 'make',
                     'name',
                     'the make program to compile ruby extentions' ] ],
    [ 'without-ext', [ 'no',
                       'yes/no',
                       'does not compile/install ruby extentions' ] ]
  ]
  multipackage_descripters = [
    [ 'with',      [ '',
                     'name,name...',
                     'package names that you want to install',
                     'ALL' ] ],
    [ 'without',   [ '',
                     'name,name...',
                     'package names that you do not want to install',
                     'NONE' ] ]
  ]
  if multipackage_install?
    DESCRIPTER = common_descripters + multipackage_descripters
  else
    DESCRIPTER = common_descripters
  end

  SAVE_FILE = 'config.save'

  def ConfigTable.each_name(&block)
    keys().each(&block)
  end

  def ConfigTable.keys
    DESCRIPTER.map {|name, *dummy| name }
  end

  def ConfigTable.each_definition(&block)
    DESCRIPTER.each(&block)
  end

  def ConfigTable.get_entry(name)
    name, ent = DESCRIPTER.assoc(name)
    ent
  end

  def ConfigTable.get_entry!(name)
    get_entry(name) or raise ArgumentError, "no such config: #{name}"
  end

  def ConfigTable.add_entry(name, vals)
    ConfigTable::DESCRIPTER.push [name,vals]
  end

  def ConfigTable.remove_entry(name)
    get_entry(name) or raise ArgumentError, "no such config: #{name}"
    DESCRIPTER.delete_if {|n, arr| n == name }
  end

  def ConfigTable.config_key?(name)
    get_entry(name) ? true : false
  end

  def ConfigTable.bool_config?(name)
    ent = get_entry(name) or return false
    ent[1] == 'yes/no'
  end

  def ConfigTable.value_config?(name)
    ent = get_entry(name) or return false
    ent[1] != 'yes/no'
  end

  def ConfigTable.path_config?(name)
    ent = get_entry(name) or return false
    ent[1] == 'path'
  end


  class << self
    alias newobj new
  end

  def ConfigTable.new
    c = newobj()
    c.initialize_from_table
    c
  end

  def ConfigTable.load
    c = newobj()
    c.initialize_from_file
    c
  end

  def initialize_from_table
    @table = {}
    DESCRIPTER.each do |k, (default, vname, desc, default2)|
      @table[k] = default
    end
  end

  def initialize_from_file
    raise InstallError, "#{File.basename $0} config first"\
        unless File.file?(SAVE_FILE)
    @table = {}
    File.foreach(SAVE_FILE) do |line|
      k, v = line.split(/=/, 2)
      @table[k] = v.strip
    end
  end

  def save
    File.open(SAVE_FILE, 'w') {|f|
      @table.each do |k, v|
        f.printf "%s=%s\n", k, v if v
      end
    }
  end

  def []=(k, v)
    raise InstallError, "unknown config option #{k}"\
        unless ConfigTable.config_key?(k)
    @table[k] = v
  end
    
  def [](key)
    return nil unless @table[key]
    @table[key].gsub(%r<\$([^/]+)>) { self[$1] }
  end

  def set_raw(key, val)
    @table[key] = val
  end

  def get_raw(key)
    @table[key]
  end

end


module MetaConfigAPI

  def eval_file_ifexist(fname)
    instance_eval File.read(fname), fname, 1 if File.file?(fname)
  end

  def config_names
    ConfigTable.keys
  end

  def config?(name)
    ConfigTable.config_key?(name)
  end

  def bool_config?(name)
    ConfigTable.bool_config?(name)
  end

  def value_config?(name)
    ConfigTable.value_config?(name)
  end

  def path_config?(name)
    ConfigTable.path_config?(name)
  end

  def add_config(name, argname, default, desc)
    ConfigTable.add_entry name,[default,argname,desc]
  end

  def add_path_config(name, default, desc)
    add_config name, 'path', default, desc
  end

  def add_bool_config(name, default, desc)
    add_config name, 'yes/no', default ? 'yes' : 'no', desc
  end

  def set_config_default(name, default)
    if bool_config?(name)
      ConfigTable.get_entry!(name)[0] = (default ? 'yes' : 'no')
    else
      ConfigTable.get_entry!(name)[0] = default
    end
  end

  def remove_config(name)
    ent = ConfigTable.get_entry(name)
    ConfigTable.remove_entry name
    ent
  end

end
