#!/usr/pkg/bin/ruby33
#
# Copyright (c) 2014-2019 Masanori Kado, Kenshi Muto
#
# 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".
#
# Convert old PREDEF/CHAPS/PART/POSTDEF files into catalog.yml.

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

require 'optparse'
require 'review/version'
require 'review/extentions'
require 'review/logger'
require 'review/catalog'

def main
  @logger = ReVIEW.logger
  opts = OptionParser.new
  opts.version = ReVIEW::VERSION
  opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} dirname"
  opts.on('-h', '--help', 'print this message and quit.') do
    puts opts.help
    exit 0
  end

  begin
    opts.parse!
  rescue OptionParser::ParseError => e
    @logger.error e.message
    $stderr.puts opts.help
    exit 1
  end

  dir = Dir.pwd

  # confirmation
  if File.exist?("#{dir}/catalog.yml")
    if ask_yes?('The catalog.yml already exists. Do you want to overwrite it? [y/n]')
      @logger.info 'Start writing...'
    else
      @logger.info 'bye.'
      exit
    end
  end

  if exist_old_style_catalog_files?(dir)
    File.open("#{dir}/catalog.yml", 'w') do |catalog|
      # predef
      if File.exist?("#{dir}/PREDEF")
        catalog << parse_predef(File.read("#{dir}/PREDEF"))
      end
      # chaps and parts
      if File.exist?("#{dir}/CHAPS")
        catalog << if File.exist?("#{dir}/PART")
                     parse_parts(File.read("#{dir}/PART"),
                                 File.read("#{dir}/CHAPS"))
                   else
                     parse_chaps(File.read("#{dir}/CHAPS"))
                   end
      end
      # postdef
      if File.exist?("#{dir}/POSTDEF")
        postdef = File.read("#{dir}/POSTDEF")
        catalog << if ask_yes?('Do you want to convert POSTDEF into APPENDIX? [y/n]')
                     parse_postdef(postdef, true)
                   else
                     parse_postdef(postdef)
                   end
      end
    end
  end

  new_catalog = replace_old_style_part(dir)
  File.write("#{dir}/catalog.yml", new_catalog)

  puts File.read("#{dir}/catalog.yml")
end

def parse_internal(str, header)
  if str.present?
    header + str.split("\n").map { |i| "  - #{i}\n" }.join
  else
    header
  end
end

def parse_predef(str)
  header = "PREDEF:\n"
  parse_internal(str, header) + "\n"
end

def parse_chaps(str)
  header = "CHAPS:\n"
  parse_internal(str, header) + "\n"
end

def parse_postdef(str, to_appendix = false)
  header = if to_appendix
             "APPENDIX:\n"
           else
             "POSTDEF:\n"
           end
  parse_internal(str, header) + "\n"
end

def parse_parts(parts_str, chaps_str)
  if parts_str.blank? || chaps_str.blank?
    return "CHAPS:\n\n"
  end

  parts = parts_str.split("\n")
  chaps = chaps_str.split("\n\n")
  "CHAPS:\n" + parts.zip(chaps).map do |k, vs|
    "  - #{k}:\n" + vs.split("\n").map { |i| "    - #{i}\n" }.join
  end.join + "\n"
end

def exist_old_style_catalog_files?(dir)
  File.exist?("#{dir}/PREDEF") || File.exist?("#{dir}/CHAPS") ||
    File.exist?("#{dir}/PART") || File.exist?("#{dir}/POSTDEF")
end

def ask_yes?(message)
  loop do
    print message
    case gets
    when /\A[yY]/
      return true
    when /\A[nN]/, /\A\Z/
      return false
    end
  end
end

def replace_old_style_part(dir)
  catalog = ReVIEW::Catalog.new(File.open("#{dir}/catalog.yml"))
  Array.new(catalog.parts.split("\n")).each_with_index do |part, part_idx|
    next if part.end_with?('.re')

    allow_overwrite = true
    part_name = sprintf('part%d.re', part_idx + 1)
    if File.exist?("#{dir}/#{part_name}")
      loop do
        print "The #{part_name} already exists. Do you want to overwrite it? [y/n]"
        case gets
        when /\A[yY]/
          break
        when /\A[nN]/, /\A\Z/
          allow_overwrite = false
          break
        end
      end
    end

    next unless allow_overwrite

    File.write("#{dir}/#{part_name}", "= #{part}")
    catalog.replace_part(part, part_name)
  end

  catalog.to_s
end

if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
  main
end
