#!/usr/bin/ruby -w

#
# == new-epcb.rb
# ExcelPettyCashBookをリリースする際に、新規にワークブックを作成して
# 必要なシートやモジュールのインポートを行うスクリプト。
# Excelワークブックは保存を繰り返すとファイルサイズが増えていくため、
# リリース前にはワークブックを作成し直してファイルサイズ肥大化を防ぐ。
#
# なお、このスクリプトを実行した後に[挿入]->[名前]->[定義]で
# 下記の値を設定する必要がある。
#   =OFFSET(マスタ!$A$2,0,0,COUNTA(マスタ!$A:$A)-1,1)
#
# Revision:: $Id$
# 
require 'fileutils'
require 'optparse'
require 'ostruct'
require 'log'
require 'epcb/cli'

$KCODE = "s"

########################################
# 定数の定義
########################################
MY_NAME = File.basename($0)
MY_VERSION = "$Date$"
ROOT_DIR = File.expand_path("#{File.dirname($0)}/../")
REQUIRED_SHEET_NAMES = [ "README", "PREFS", "マスタ", "雛型", "サンプル" ]
REQUIRED_VBA_FILE_NAMES = [ "ActionForm.frm", "CreateNewDataSheetForm.frm",
                            "Common.bas", "Main.bas", "Prefs.bas", 
                            "ThisWorkbook.cls" ]

########################################
# グローバル変数の定義
########################################
require 'epcb/cli-vars.rb'

$options.vba_code_dir = "."
$options.import_only = false
$options.leave = false

helper = EPCB::CLI::Helper.new($options)

########################################
# オプションの定義と解析
########################################
ARGV.options do |opts|
  opts.banner = <<BANNER
Usage: #{MY_NAME} [options] SOURCE_FILE [dest_file]
BANNER

  opts.separator("options are:")

  # --directory
  opts.on("-d", "--directory DIRECTORY", String,
          "specify a directory VBA code files location. (DEFAULT \".\")") do |arg|
    $options.vba_code_dir = arg
  end
  # --import
  opts.on("-i", "--import",
          "specify only import existed VBA code files or not.") do |arg|
    $options.import_only = arg
  end
  # --leave
  opts.on("-l", "--leave",
          "leave VBA code files.") do |arg|
    $options.leave = arg
  end
  # --verbose
  helper.define_verbose(opts)
  # --version
  helper.define_version(opts, MY_NAME, MY_VERSION)
  # --help
  helper.define_help(opts)

  opts.parse!
end or exit(CLI::ExitStatus::ARGUMENT_ERROR)

#
# VBAのコードをコピーするクラス。
#
class VBACodeCopy < CLI::Application
  def initialize(src_file, dest_file)
    Log::voutln("ROOT_DIR = #{ROOT_DIR}")
    CLI::ensure_file_exist(src_file)
    lib_dir = File.join(ROOT_DIR, "common/ruby")
    CLI::ensure_dir_exist(lib_dir)
    $:.unshift(lib_dir)
    require 'win32ole-ext'

    @src_path = File.file_system.getAbsolutePathName(src_file)
    @dest_path = File.file_system.getAbsolutePathName(dest_file)
    @vba_code_path = File.file_system.getAbsolutePathName($options.vba_code_dir)
    Log::voutln("src_path      = #{@src_path}")
    Log::voutln("dest_path     = #{@dest_path}")
    Log::voutln("vba_code_path = #{@vba_code_path}")

    @excel = nil
    @src_workbook = nil
    @dest_workbook = nil
  end
  
  def run
    @excel = Excel.new(true, true)
    
    # 移行先のワークブックを作成する
    orig_num = @excel.sheetsInNewWorkbook
    @excel.sheetsInNewWorkbook = 1
    dest_workbook = @excel.workbooks.add
    @excel.sheetsInNewWorkbook = orig_num

    # 移行元のワークブックを開く
    src_workbook = @excel.workbooks.open(@src_path)
    src_workbook.activate

    # 必要なシートをコピーする
    sheet_before = dest_workbook.worksheets(1)
    for i in 1 .. src_workbook.worksheets.count
      sheet = src_workbook.worksheets(i)
      found = false
      REQUIRED_SHEET_NAMES.each do |name|
        if sheet.name == name
          found = true
          break
        end
      end
      next if !found
      # コピー
      sheet.copy(sheet_before)
    end
    Log::voutln("Copied required sheets.")

    # ワークブック作成時の不要な"Sheet1"は削除
    @excel.displayAlerts = false
    dest_workbook.worksheets(dest_workbook.worksheets.count).delete
    @excel.displayAlerts = true

    # VBAコードファイルをエクスポートする
    src_components = src_workbook.vbproject.vbcomponents
    for i in 1 .. src_components.count
      component = src_components.item(i)
      file_name = nil
      REQUIRED_VBA_FILE_NAMES.each do |fname|
        name = File.basename(fname, ".*")
        if name == component.name
          file_name = fname
          break
        end
      end
      next if file_name.nil?
      p = File.file_system.getAbsolutePathName("#{@vba_code_path}/#{file_name}")
      component.export(p)
    end
    Log::voutln("Exported VBA code files.")

    # 必要なVBAコードファイルをインポートする
    dest_components = dest_workbook.vbproject.vbcomponents
    REQUIRED_VBA_FILE_NAMES.each do |name|
      path = File.file_system.getAbsolutePathName("#{@vba_code_path}/#{name}")
      if name != "ThisWorkbook.cls"
        dest_components.import(path)
      else
        for i in 1 .. dest_components.count
          component = dest_components.item(i)
          next if component.name != "ThisWorkbook"
          code_module = component.codeModule
          line = code_module.countOfLines
          code_module.deleteLines(1, line)
          content = load_vba_file(path)
          code_module.addFromString(content)
        end
      end  
    end
    Log::voutln("Imported VBA code files.")

    # VBProject名を設定
    ver = @excel.run("Const_VERSION")
    if ver != nil
      dest_workbook.vbproject.name = "ExcelPettyCashBook_#{ver.gsub(/\./,"_")}"
      Log::voutln("Set VBProject name.")
    end

    # 移行先のワークブックを保存する
    dest_workbook.worksheets(1).activate
    if FileTest.exist?(@dest_path)
      FileUtils.rm_f(@dest_path, :verbose => $options.verbose)
    end
    #@excel.run("SetOnExcel", false)
    dest_workbook.saveAs(@dest_path)
    Log::voutln("Saved dest_file as #{@dest_path}.")

    return CLI::ExitStatus::OK
  end

  def destroy
    @excel.quit if @excel != nil
    unless $options.leave
      REQUIRED_VBA_FILE_NAMES.each do |name|
        path = "#{@vba_code_path}/#{name}"
        if FileTest.exist?(path)
          FileUtils.rm_f(path, :verbose => $options.verbose)
        end
        if File.extname(name) == ".frm"
          p = "#{@vba_code_path}/#{File.basename(name, '.*')}.frx"
          if FileTest.exist?(p)
            FileUtils.rm_f(p, :verbose => $options.verbose)
          end
        end
      end
      Log::voutln("Cleaned up VBA code files.")
    end
    super
  end

  private
  def load_vba_file(path)
    str = ""
    File.open(path, "r") do |f|
      i = 0
      while line = f.gets
        i += 1
        next if i < 10
        str += line
      end
    end
    return str
  end

end #VBACodeCopy#

########################################
# mainに相当するstart関数
########################################
def start(helper)
  if ARGV[0].nil?
    Log::eoutln("Must specify SOURCE_FILE")
    exit(CLI::ExitStatus::ARGUMENT_ERROR)
  elsif ARGV[1].nil?
    dir = File.dirname(ARGV[0])
    name = File.basename(ARGV[0], ".*")
    ext = File.extname(ARGV[0])
    dest_file = File.join(dir, "#{name}_NEW#{ext}")
  end

  exit_status = CLI::ExitStatus::OK
  begin
    app = VBACodeCopy.new(ARGV[0], dest_file)
    exit_status = app.run
  rescue Exception => e
    exit_status = helper.handle_exception(e)
  ensure
    app.destroy if app != nil
  end

  exit(exit_status)
end

if __FILE__ == $0
  start(helper)
end
