#!/usr/bin/ruby
#
# $Id: review-compile 4326 2010-01-12 14:10:17Z kmuto $
#
# Copyright (c) 2008-2016 Kenshi Muto, Masayoshi Takahashi, KADO Masanori, Minero Aoki
# Copyright (c) 1999-2007 Minero Aoki
#
# This program is free software.
# You can distribute or modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
# For details of the GNU LGPL, see the file "COPYING".
#

require 'pathname'

bindir = Pathname.new(__FILE__).realpath.dirname
$LOAD_PATH.unshift((bindir + '../lib').realpath)

require 'review'
require 'fileutils'
require 'optparse'
require 'yaml'

DEFAULT_CONFIG_FILENAME = "config.yml"

def main
  Signal.trap(:INT) { exit 1 }
  if RUBY_PLATFORM !~ /mswin(?!ce)|mingw|cygwin|bccwin/
    Signal.trap(:PIPE, 'IGNORE')
  end
  _main
rescue Errno::EPIPE
  exit 0
end

def _main
  mode = :files
  basedir = nil
  if /\Areview2/ =~ File.basename($0)
    target = File.basename($0, '.rb').sub(/review2/, '')
  else
    target = nil
  end
  check_only = false
  output_filename = nil

  config = ReVIEW::Configure.values

  opts = OptionParser.new
  opts.version = ReVIEW::VERSION
  opts.banner = "Usage: #{File.basename($0)} [--target=FMT]"
  opts.on('--yaml=YAML', 'Read configurations from YAML file.') {|yaml| config["yaml"] = yaml}
  opts.on('-c', '--check', 'Check manuscript') { check_only = true }
  opts.on('--level=LVL', 'Section level to append number.') {|lvl| config["secnolevel"] = lvl.to_i }
  opts.on('--toclevel=LVL', 'Section level to append number.') {|lvl| config["toclevel"] = lvl.to_i }
  opts.on('--structuredxml', 'Produce XML with structured sections. (idgxml)') { config["structuredxml"] = true }
  opts.on('--table=WIDTH', 'Default table width. (idgxml)') {|tbl| config["tableopt"] = tbl }
  opts.on('--listinfo', 'Append listinfo tag to lists to indicate begin/end. (idgxml)') { config["listinfo"] = true }
  opts.on('--chapref="before,middle,after"', 'Chapref decoration. (idgxml)') {|cdec| config["chapref"] = cdec }
  opts.on('--chapterlink', 'make chapref hyperlink') { config["chapterlink"] = true }
  opts.on('--stylesheet=file', 'Stylesheet file for HTML (comma separated)') {|files| config["stylesheet"] = files.split(/\s*,\s*/) }
  opts.on('--mathml', 'Use MathML for TeX equation in HTML') do
    config["mathml"] = true
  end
  opts.on('--htmlversion=VERSION', 'HTML version.') do |v|
    v = v.to_i
    config["htmlversion"] = v if v == 4 || v == 5
  end
  opts.on('--epubversion=VERSION', 'EPUB version.') do |v|
    v = v.to_i
    config["epubversion"] = v if v == 2 || v == 3
  end
  opts.on('--target=FMT', 'Target format.') {|fmt| target = fmt } unless target
  opts.on('--footnotetext',
          'Use footnotetext and footnotemark instead of footnote (latex)') {
    config["footnotetext"] = true
  }
  opts.on('--draft', 'use draft mode(inline comment)') { config["draft"] = true }
  opts.on('--directory=DIR', 'Compile all chapters in DIR.') do |path|
    mode = :dir
    basedir = path
  end
  opts.on('--output-file=FILENAME', 'Write all results into file instead of stdout.') do |filename|
    output_filename = filename
  end
  opts.on('--tabwidth=WIDTH', 'tab width') {|width| config["tabwidth"] = width.to_i }
  opts.on('--catalogfile=FILENAME', 'Set catalog file') do |catalogfile|
    config["catalogfile"] = catalogfile
  end
  opts.on('--help', 'Prints this message and quit.') do
    puts opts.help
    exit 0
  end
  begin
    opts.parse!
    unless target
      if check_only
        target = 'html'
      else
        raise OptionParser::ParseError, "no target given"
      end
    end
  rescue OptionParser::ParseError => err
    error err.message
    $stderr.puts opts.help
    exit 1
  end

  begin
    loader = ReVIEW::YAMLLoader.new
    if config["yaml"]
      config.deep_merge!(loader.load_file(config["yaml"]))
    else
      if File.exist?(DEFAULT_CONFIG_FILENAME)
        config.deep_merge!(loader.load_file(DEFAULT_CONFIG_FILENAME))
      end
    end

    config["builder"] = target
    ReVIEW::I18n.setup(config["language"])
    begin
      config.check_version(ReVIEW::VERSION)
    rescue ReVIEW::ConfigError => e
      warn e.message
    end

    if ARGV.blank?
      mode = :dir
    end

    case mode
    when :files
      if ARGV.empty?
        error 'no input'
        exit 1
      end

      basedir = File.dirname(ARGV[0])
      book = ReVIEW::Book::Base.load(basedir)
      book.config = config # needs only at the first time
      ARGV.each do |item|
        chap_name = File.basename(item, '.*')
        chap = book.chapter(chap_name)
        compiler = ReVIEW::Compiler.new(load_strategy_class(target, check_only))
        result = compiler.compile(chap)
        if output_filename
          write output_filename, result
        else
          puts result unless check_only
        end
      end
    when :dir
      book = basedir ? ReVIEW::Book.load(basedir) : ReVIEW::Book::Base.load
      book.config = config
      compiler = ReVIEW::Compiler.new(load_strategy_class(target, check_only))
      book.chapters.each do |chap|
        str = compiler.compile(chap)
        write "#{chap.name}#{compiler.strategy.extname}", str unless check_only
      end
      # PART
      book.parts_in_file.each do |part|
        str = compiler.compile(part)
        write "#{part.name}#{compiler.strategy.extname}", str unless check_only
      end
    else
      raise "must not happen: #{mode}"
    end
  rescue ReVIEW::ApplicationError => err
    raise if $DEBUG
    error err.message
    exit 1
  end
end

def error(msg)
  $stderr.puts "#{File.basename($0, '.*')}: error: #{msg}"
end

def load_strategy_class(target, strict)
  require "review/#{target}builder"
  ReVIEW.const_get("#{target.upcase}Builder").new(strict)
end

def write(path, str)
  File.open(path, 'w') {|f|
    f.puts str
  }
end

main
