#!/usr/bin/ruby

# Copyright (C) 2006,2007 Daiki Ueno <ueno@unixuser.org>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

require 'pathname'
require 'rbconfig'

base = Pathname.new(__FILE__).dirname + 'ruby'
arch = base + RbConfig::CONFIG['arch']

$LOAD_PATH.unshift(arch.to_s)
$LOAD_PATH.unshift(base.to_s)

begin
  require 'treil'
rescue LoadError
  la = Pathname.new('libtreil.la')
  if la.exist?
    $stderr.puts("Can't load shared library; try `libtool --mode=execute -dlopen #{la} #{$0} #{ARGV.join(' ')}'")
  end
  exit(1)
end

require 'optparse'
require 'yaml'

output = nil
width, height = 800.0, 600.0
max_depth = nil
resolution = nil
config = nil
Excludes = ["RCS", "CVS", "SCCS", ".svn", ".git", "*.o", "*.elc"]
excludes = nil
reader_class = nil
writer_class = nil
verbose = false
print_help_and_exit = false

opts = OptionParser.new do |opts|
  opts.banner = <<"End"
Usage: #{$0} [OPTIONS] INPUT OUTPUT
Where INPUT is a directory and OUTPUT is a file (default: treil.png).
End
  opts.on('--output=FILE', '-o', 'Store the output to FILE.') do |file|
    output = Pathname.new(file)
  end
  opts.on('--geometry=WIDTHxHEIGHT', '-g',
          'Specify size of the outermost rectangle.') do |geometry|
    if geometry =~ /\A(\d+)x(\d+)\z/
      width, height = $1.to_f, $2.to_f
    else
      $stdout.print(opts.to_s)
      exit(0)
    end
  end
  opts.on('--max-depth=DEPTH', '-d',
          'Specify max depth of rectangles displayed.') do |depth|
    unless depth =~ /\A\d+\z/
      $stdout.print(opts.to_s)
      exit(0)
    end
    max_depth = depth.to_i
  end
  opts.on('--resolution=RES',
          'Specify pixel width.') do |res|
    unless res =~ /\A\d+\z/
      $stdout.print(opts.to_s)
      exit(0)
    end
    resolution = res.to_i
  end
  opts.on('--exclude=PAT', '-x', 'Exclude files that match PAT.') do |pat|
    excludes ||= Array.new
    excludes << pat
  end
  opts.on('--config=FILE', '-c', 'Read config from FILE.') do |file|
    config = Pathname.new(file)
  end
  opts.on('--reader=CLASS', '-R', 'Specify reader class.', 'Do not use if you unsure.') do |class_name|
    reader_class = class_name
  end
  opts.on('--writer=CLASS', '-W', 'Specify writer class.', 'Do not use if you unsure.') do |class_name|
    writer_class = class_name
  end
  opts.on('--verbose', '-v', 'Run verbosely.') do
    verbose = true
  end
  opts.on_tail('--help', '-h', 'Show this message.') do
    print_help_and_exit = true
  end
end

begin
  opts.parse!(ARGV)
rescue OptionParser::ParseError
  $stderr.print(opts.to_s)
  exit(1)
end

if !print_help_and_exit && ARGV.length < 1
  $stderr.print(opts.to_s)
  exit(1)
end

if verbose
  opts.top.short['R'].desc[1] = "Available readers: #{Treil::Reader::list.join(' ')}"
  opts.top.short['W'].desc[1] = "Available writers: #{Treil::Writer::list.join(' ')}"
end

if print_help_and_exit
  $stdout.print(opts.to_s)
  exit(0)
end

begin
  input = ARGV[0]
  output ||= ARGV[1] || Pathname.new('treil.png')
  if config
    config = YAML.load(config.read)
  else
    config = Hash.new
  end
  config[''] ||= Hash.new
  config['']['excludes'] ||= excludes || Excludes
  config['']['max_depth'] = max_depth if max_depth
  config['']['resolution'] = resolution if resolution
  config = Treil::Config.new(config)

  begin
    reader = Treil::Reader.instance(input, config, reader_class)
  rescue Exception => e
    if verbose
      $stderr.puts("Can't read from #{input}: #{e.to_s}\n#{e.backtrace.join("\n")}")
    else
      $stderr.puts("Can't read from #{input}")
    end
  end
  $stderr.puts("Reader: #{reader.class.name.sub(/\ATreil::/, '')}") if verbose

  begin
    writer = Treil::Writer.instance(output, config, writer_class)
  rescue Exception => e
    if verbose
      $stderr.puts("Can't write to #{output}: #{e.to_s}\n#{e.backtrace.join("\n")}")
    else
      $stderr.puts("Can't write to #{output}")
    end
  end
  $stderr.puts("Writer: #{writer.class.name.sub(/\ATreil::/, '')}") if verbose

  tree = reader.read

  rect = Treil::Rect.new(0.0, 0.0, width, height)
  rect.fill(tree)

  writer.write(rect)
  $stderr.puts("`#{output.to_s}' generated")
rescue Exception => e
  $stderr.puts("failed to generate `#{output.to_s}': #{e.to_s}\n#{e.backtrace.join("\n")}") if verbose
  exit(1)
end
