#!/usr/pkg/bin/ruby33
#
# Copyright (c) 2008-2019 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".
#

$LOAD_PATH.unshift(File.realpath('../lib', __dir__))

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

include ReVIEW::Loggable

DEFAULT_CONFIG_FILENAME = 'config.yml'.freeze

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

def _main
  @logger = ReVIEW.logger
  @mode = :files
  @basedir = nil
  @target = if File.basename($PROGRAM_NAME).start_with?('review2')
              File.basename($PROGRAM_NAME, '.rb').sub('review2', '')
            end
  @check_only = false
  @output_filename = nil

  @config = ReVIEW::Configure.values

  parse_opts

  begin
    loader = ReVIEW::YAMLLoader.new
    if @config['yaml']
      error! "#{@config['yaml']} not found." unless File.exist?(@config['yaml'])
      begin
        @config.deep_merge!(loader.load_file(@config['yaml']))
      rescue StandardError => e
        error! "yaml error #{e.message}"
      end
    elsif File.exist?(DEFAULT_CONFIG_FILENAME)
      begin
        @config.deep_merge!(loader.load_file(DEFAULT_CONFIG_FILENAME))
      rescue StandardError => e
        error! "yaml error #{e.message}"
      end
    end

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

    @mode = :dir if ARGV.blank?

    case @mode
    when :files
      error!('no input') if ARGV.empty?

      @basedir = File.dirname(ARGV[0])
      book = ReVIEW::Book::Base.new(@basedir, config: @config)
      ARGV.each do |item|
        error!("file not found: #{item}") unless File.exist?(File.join(book.config['contentdir'], item))
        chap_name = File.basename(item, '.*')
        chap = book.chapter(chap_name)
        compiler = ReVIEW::Compiler.new(load_builder_class(@target, @check_only))
        result = compiler.compile(chap)
        if @output_filename
          File.write(@output_filename, result)
        else
          puts result unless @check_only
        end
      end
    when :dir
      book = @basedir ? ReVIEW::Book::Base.new(@basedir, config: @config) : ReVIEW::Book::Base.new(config: @config)
      compiler = ReVIEW::Compiler.new(load_builder_class(@target, @check_only))
      book.chapters.each do |chap|
        str = compiler.compile(chap)
        File.write("#{chap.name}#{compiler.builder.extname}", str) unless @check_only
      end
      # PART
      book.parts_in_file.each do |part|
        str = compiler.compile(part)
        File.write("#{part.name}#{compiler.builder.extname}", str) unless @check_only
      end
    else
      raise "must not happen: #{@mode}"
    end
  rescue ReVIEW::ApplicationError => e
    error! e.message
  end
end

def parse_opts
  opts = OptionParser.new
  opts.version = ReVIEW::VERSION
  opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [--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') { @config['math_format'] = 'mathml' }
  opts.on('--htmlversion=VERSION', 'HTML version.') do |v|
    v = v.to_i
    @config['htmlversion'] = v if [4, 5].include?(v)
  end
  opts.on('--epubversion=VERSION', 'EPUB version.') do |v|
    v = v.to_i
    @config['epubversion'] = v if [2, 3].include?(v)
  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.') { |filename| @output_filename = filename }
  opts.on('--tabwidth=WIDTH', 'tab width') { |width| @config['tabwidth'] = width.to_i }
  opts.on('--catalogfile=FILENAME', 'Set catalog file') { |catalogfile| @config['catalogfile'] = catalogfile }
  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 => e
    warn(e.message)
    $stderr.puts opts.help
    exit 1
  end
end

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

main
