#! /usr/bin/ruby

#==============================================================================#
# $Id: exerb,v 1.20 2004/03/25 07:29:42 yuya Exp $
#==============================================================================#

require 'getoptlong'
require 'exerb/recipe'
require 'exerb/executable'

#==============================================================================#

module ExerbCommand

  OPTIONS = [
    ['--corename', '-c', GetoptLong::REQUIRED_ARGUMENT, 'specifies exerb-core name.'],
    ['--corefile', '-C', GetoptLong::REQUIRED_ARGUMENT, 'specifies exerb-core file.'],
    ['--outfile',  '-o', GetoptLong::REQUIRED_ARGUMENT, 'specifies output file.'],
    ['--kcode',    '-k', GetoptLong::REQUIRED_ARGUMENT, 'specifies kanji code. (none/euc/sjis/utf8)'],
    ['--verbose',  '-v', GetoptLong::NO_ARGUMENT,       'enable verbose mode.'],
    ['--debug',    '-g', GetoptLong::NO_ARGUMENT,       'enable debug mode.'],
    ['--execute',  '-e', GetoptLong::NO_ARGUMENT,       'execute the created executable file.'],
    ['--config',   '-i', GetoptLong::NO_ARGUMENT,       'display config information.'],
    ['--version',  '-V', GetoptLong::NO_ARGUMENT,       'display version number.'],
    ['--help',     '-h', GetoptLong::NO_ARGUMENT,       'display this information.'],
  ]

  def self.main(argv)
    options = self.parse_options(argv)

    self.print_config_and_exit  if options[:config]
    self.print_version_and_exit if options[:version]
    self.print_usage_and_exit   if options[:help] || argv.size < 1

    recipe_file = argv.shift

    if /\.exy$/ =~ recipe_file
      require 'exerb/recipe_yaml'

      recipe  = Exerb::RecipeYaml.new(recipe_file)
      archive = recipe.create_archive(options[:kcode])

      core   = options[:corefile]
      core ||= Exerb::Utility.find_core_by_name(options[:corename], true) if options[:corename]

      core_file    = recipe.core_filepath(core)
      output_file  = recipe.output_filepath(options[:outfile])
      archive_file = recipe.archive_filepath(options[:outfile])

      raise(Exerb::ExerbError, "a core file isn't specified") if core_file.nil?
      raise(Exerb::ExerbError, "no such file -- #{core_file}") unless File.exist?(core_file)
      raise(Exerb::ExerbError, "no such directory -- #{File.dirname(output_file)}") unless File.directory?(File.dirname(output_file))

      if options[:verbose]
        puts("Recipe File  : #{recipe_file}")
        puts("Core File    : #{core_file}")
        puts("Archive File : #{archive_file}") if options[:debug]
        puts("Output File  : #{output_file}")
      end

      archive.write_to_file(archive_file) if options[:debug]

      executable = Exerb::Executable.new_from_file(core_file)
      executable.rsrc.add_archive(archive)
      executable.write_to_file(output_file)

      exec(File.expand_path(output_file)) if options[:execute]
    else
      recipe       = Exerb::Recipe.new_from_file(recipe_file)
      core_file    = self.select_core_file(options, recipe)
      out_file     = self.select_out_file(options, recipe, recipe_file)
      archive_file = self.make_filename(recipe_file, 'exa')

      if options[:verbose]
        puts("Recipe File  : #{recipe_file}")
        puts("Archive File : #{archive_file}") if options[:debug]
        puts("Core File    : #{core_file}")
        puts("Output File  : #{out_file}")
      end

      recipe.archive.kcode = options[:kcode] if options[:kcode]
      recipe.archive.write_to_file(archive_file) if options[:debug]

      executable = Exerb::Executable.new_from_file(core_file)
      executable.rsrc.add_archive(recipe.archive)
      executable.write_to_file(out_file)

      exec(File.expand_path(out_file)) if options[:execute]
    end

    exit(0)
  rescue Exerb::ExerbError => e
    STDERR.puts("exerb: #{e.message}")
    exit(1)
  end

  def self.parse_options(argv)
    options = {}
    config  = OPTIONS.collect { |ary| ary[0, 3] }

    parser = GetoptLong.new
    parser.set_options(*config)
    parser.each_option { |name, argument|
      options[name.sub(/^--/, '').intern] = argument
    }

    options[:verbose]  = !!options[:verbose]
    options[:debug]    = !!options[:debug]
    options[:execute]  = !!options[:execute]
    options[:config]   = !!options[:config]
    options[:version]  = !!options[:version]
    options[:help]     = !!options[:help]

    return options
  rescue GetoptLong::InvalidOption
    exit(1)
  end

  def self.print_config_and_exit
    puts "Exerb #{Exerb::VERSION}"

    puts
    puts "Search directories of Ruby library:"
    $LOAD_PATH.each { |path| printf("  %s\n", File.expand_path(path)) }

    puts
    puts "Search directories of Exerb core:"
    Exerb::CORE_PATH.each { |path| printf("  %s\n", File.expand_path(path)) }

    puts
    puts "Search directories of Exerb plug-in:"
    Exerb::CORE_PATH.each { |path| printf("  %s\n", File.expand_path(path)) }

    name_max = (Exerb::CORE_NAME.keys + Exerb::PLUGIN_NAME.keys).collect { |name| name.size }.max

    puts
    puts "Defined names of Exerb core:"
    Exerb::CORE_NAME.keys.sort.each { |name| printf("  %s => %s\n", name.ljust(name_max), Exerb::CORE_NAME[name]) }

    puts
    puts "Defined names of Exerb plug-in:"
    Exerb::PLUGIN_NAME.keys.sort.each { |name| printf("  %s => %s\n", name.ljust(name_max), Exerb::PLUGIN_NAME[name]) }

    exit(1)
  end

  def self.print_version_and_exit
    puts "Exerb #{Exerb::VERSION}"
    exit(1)
  end

  def self.print_usage_and_exit
    puts("Exerb #{Exerb::VERSION}")

    puts
    puts("Usage: exerb [options] recipe-file")

    puts
    puts("Options:")
    OPTIONS.each { |long, short, arg, comment|
      printf("  %s  %-10s  %s\n", short, long, comment)
    }

    exit(1)
  end

  def self.make_filename(filename, extension)
    return filename.sub(/(\.exr$|$)/i, '.' + extension)
  end

  def self.select_core_file(options, recipe)
    core_file   = options[:corefile]
    core_file ||= Exerb::Utility.find_core_by_name(options[:corename], true) if options[:corename]
    core_file ||= recipe.corefile
    core_file ||= Exerb::Utility.find_core_by_name('cui')
    core_file ||= Exerb::Utility.find_core_by_name('gui')
    core_file ||= raise(Exerb::ExerbError, "a core file isn't specified in the recipe file and command line options.")

    return core_file
  end

  def self.select_out_file(options, recipe, recipe_file)
    out_file   = options[:outfile]
    out_file ||= recipe.outfile
    out_file ||= self.make_filename(recipe_file, 'exe')

    return out_file
  end

end

#==============================================================================#

ExerbCommand.main(ARGV)

#==============================================================================#
#==============================================================================#
