# -*- coding: utf-8; mode: ruby -*-
#
# Copyright (C) 2009-2011  Kouhei Sutou <kou@clear-code.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 2.1 as published by the Free Software Foundation.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

require 'English'

require 'find'
require 'fileutils'
require 'pathname'
require 'erb'
require 'rubygems'
require 'yard'
require 'jeweler'
require 'rake/extensiontask'

if YAML.const_defined?(:ENGINE)
  begin
    YAML::ENGINE.yamler = "psych"
  rescue LoadError
  end
end

base_dir = File.join(File.dirname(__FILE__))

groonga_ext_dir = File.join(base_dir, "ext", "groonga")
groonga_lib_dir = File.join(base_dir, 'lib')
$LOAD_PATH.unshift(groonga_ext_dir)
$LOAD_PATH.unshift(groonga_lib_dir)
ENV["RUBYLIB"] = "#{groonga_lib_dir}:#{groonga_ext_dir}:#{ENV['RUBYLIB']}"

def guess_version(groonga_ext_dir)
  version = {}
  File.open(File.join(groonga_ext_dir, "rb-grn.h")) do |rb_grn_h|
    rb_grn_h.each_line do |line|
      case line
      when /\A#define RB_GRN_([A-Z]+)_VERSION (\d+)/
        version[$1.downcase] = $2
      end
    end
  end
  [version["major"], version["minor"], version["micro"]].join(".")
end

def cleanup_white_space(entry)
  entry.gsub(/(\A\n+|\n+\z)/, '') + "\n"
end

ENV["VERSION"] ||= guess_version(groonga_ext_dir)
version = ENV["VERSION"]
spec = nil
Jeweler::Tasks.new do |_spec|
  spec = _spec
  spec.name = "rroonga"
  spec.version = version.dup
  spec.rubyforge_project = "groonga"
  spec.homepage = "http://groonga.rubyforge.org/"
  authors = File.join(base_dir, "AUTHORS")
  spec.authors = File.readlines(authors).collect do |line|
    if /\s*<[^<>]*>$/ =~ line
      $PREMATCH
    else
      nil
    end
  end.compact
  spec.email = [
    'groonga-users-en@rubyforge.org',
    'groonga-dev@lists.sourceforge.jp',
  ]
  entries = File.read("README.textile").split(/^h2\.\s(.*)$/)
  description = cleanup_white_space(entries[entries.index("Description") + 1])
  spec.summary, spec.description, = description.split(/\n\n+/, 3)
  spec.license = "LGPLv2"
  spec.files = FileList["{lib,benchmark,misc}/**/*.rb",
                        "bin/*",
                        "*.rb",
                        "example/*.rb",
                        "Rakefile",
                        "ext/**/*"]
  spec.test_files = FileList["test/**/*.rb"]
end

Jeweler::RubygemsDotOrgTasks.new do
end

module YARD
  module CodeObjects
    class Proxy
      alias_method :initialize_original, :initialize
      def initialize(namespace, name)
        name = name.gsub(/\AGrn(.*)\z/) do
          suffix = $1
          case suffix
          when ""
            "Groonga"
          when "TableKeySupport"
            "Groonga::Table::KeySupport"
          else
            "Groonga::#{suffix}"
          end
        end
        initialize_original(namespace, name)
      end
    end
  end
end

YARD::Rake::YardocTask.new do |task|
  task.options += ["--title", "#{spec.name} - #{version}"]
  # task.options += ["--charset", "UTF-8"]
  task.options += ["--readme", "README.textile"]
  task.options += ["--files", "text/tutorial.textile"]
  task.files += FileList["ext/groonga/**/*.c"]
  task.files += FileList["lib/**/*.rb"]
end

def windows?(platform=nil)
  platform ||= RUBY_PLATFORM
  platform =~ /mswin(?!ce)|mingw|cygwin|bccwin/
end

def collect_binary_files(binary_dir)
  binary_files = []
  Find.find(binary_dir) do |name|
    next unless File.file?(name)
    next if /\.zip\z/i =~ name
    binary_files << name
  end
  binary_files
end

relative_vendor_dir = "vendor"
relative_binary_dir = File.join("vendor", "local")
vendor_dir = File.join(base_dir, relative_vendor_dir)
binary_dir = File.join(base_dir, relative_binary_dir)

groonga_win32_i386_p = ENV["GROONGA32"] == "yes"

Rake::ExtensionTask.new("groonga", spec) do |ext|
  if groonga_win32_i386_p
    ext.cross_platform = ["x86-mingw32", "i386-mswin32"]
  else
    ext.cross_platform = ["x64-mingw32"]
    # ext.cross_platform << "x64-mswin64" # We need to build with VC++ 2010. :<
  end
  if windows?
    ext.gem_spec.files += collect_binary_files(relative_binary_dir)
  else
    ext.cross_compile = true
    ext.cross_compiling do |_spec|
      if windows?(_spec.platform.to_s)
        _spec.files += collect_binary_files(relative_binary_dir)
      end
    end
  end
end

include ERB::Util

def apply_template(file, head, header, footer, language)
  content = File.read(file)
  content = content.sub(/lang="en"/, 'lang="#{language}"')

  title = nil
  content = content.sub(/<title>(.+?)<\/title>/) do
    title = $1
    head.result(binding)
  end

  content = content.sub(/<body(?:.*?)>/) do |body_start|
    "#{body_start}\n#{header.result(binding)}\n"
  end

  content = content.sub(/<\/body/) do |body_end|
    "\n#{footer.result(binding)}\n#{body_end}"
  end

  File.open(file, "w") do |file|
    file.print(content)
  end
end

def erb_template(name)
  file = File.join("html", "#{name}.html.erb")
  template = File.read(file)
  erb = ERB.new(template, nil, "-")
  erb.filename = file
  erb
end

def rsync_to_rubyforge(spec, source, destination, options={})
  config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
  host = "#{config["username"]}@rubyforge.org"

  rsync_args = "-av --exclude '*.erb' --dry-run"
  rsync_args << " --delete" if options[:delete]
  remote_dir = "/var/www/gforge-projects/#{spec.rubyforge_name}/"
  sh("rsync #{rsync_args} #{source} #{host}:#{remote_dir}#{destination}")
end

namespace :reference do
  translate_languages = [:ja]
  supported_languages = [:en, *translate_languages]
  reference_base_dir = "references"
  html_files = FileList["doc/**/*.html"].to_a

  directory reference_base_dir
  CLOBBER.include(reference_base_dir)

  po_dir = "po"
  namespace :pot do
    pot_file = "#{po_dir}/#{spec.name}.pot"

    directory po_dir
    file pot_file => ["po", *html_files] do |t|
      sh("xml2po", "--keep-entities", "--output", t.name, *html_files)
    end

    desc "Generates pot file."
    task :generate => pot_file
  end

  namespace :po do
    translate_languages.each do |language|
      namespace language do
        po_file = "#{po_dir}/#{language}.po"

        file po_file do |t|
          sh("xml2po", "--keep-entities", "--update", t.name, *html_files)
        end

        desc "Updates po file for #{language}."
        task :update => po_file
      end
    end
  end

  namespace :translate do
    directory reference_base_dir

    translate_languages.each do |language|
      po_file = "#{po_dir}/#{language}.po"
      translate_doc_dir = "#{reference_base_dir}/#{language}"

      doc_dir = Pathname("doc")
      desc "Translates documents to #{language}."
      task language => [po_file, reference_base_dir, *html_files] do
        doc_dir.find do |path|
          base_path = path.relative_path_from(doc_dir)
          translated_path = "#{translate_doc_dir}/#{base_path}"
          if path.directory?
            mkdir_p(translated_path)
            next
          end
          case path.extname
          when ".html"
            sh("xml2po --keep-entities " +
               "--po-file #{po_file} --language #{language} " +
               "#{path} > #{translated_path}")
          else
            cp(path.to_s, translated_path, :preserve => true)
          end
        end
      end
    end
  end

  translate_task_names = translate_languages.collect do |language|
    "reference:translate:#{language}"
  end
  desc "Translates references."
  task :translate => translate_task_names

  desc "Generates references."
  task :generate => [:yard, :translate] do
    cp_r("doc", "#{reference_base_dir}/en", :preserve => true)
  end

  namespace :publication do
    task :prepare do
      head = erb_template("head")
      header = erb_template("header")
      footer = erb_template("footer")
      supported_languages.each do |language|
        doc_dir = "#{reference_base_dir}/#{language}"
        Find.find(doc_dir) do |file|
          if /\.html\z/ =~ file and /_(?:c|rb)\.html\z/ !~ file
            apply_template(file, head, header, footer, language)
          end
        end
      end
      File.open("#{reference_base_dir}/.htaccess", "w") do |file|
        file.puts("Redirect permanent /rroonga/text/TUTORIAL_ja_rdoc.html " +
                  "http://groonga.rubyforge.org/rroonga/ja/file.tutorial.html")
        file.puts("Redirect permanent /rroonga/ " +
                  "http://groonga.rubyforge.org/rroonga/en/")
      end
    end
  end

  task :publish => [:generate, "reference:publication:prepare"] do
    rsync_to_rubyforge(spec, "#{reference_base_dir}/", "/#{spec.name}",
                       :delete => true)
  end
end

namespace :html do
  desc "Publish HTML to Web site."
  task :publish do
    rsync_to_rubyforge(spec, "html/", "")
  end
end
task :publish => ["reference:publish", "html:publish"]

desc "Tag the current revision."
task :tag do
  sh("git tag -a #{version} -m 'release #{version}!!!'")
end

namespace :win32 do
  patches_dir = (Pathname.new(base_dir) + "patches").expand_path
  if groonga_win32_i386_p
    host = "i586-mingw32msvc"
    mecab_patches = []
  else
    host = "x86_64-w64-mingw32"
    mecab_patches = [
      "mecab-0.98-mingw-w64.diff",
      "mecab-0.98-not-use-locale-on-mingw.diff",
    ]
  end

  desc "Build MeCab and groonga and install them into vendor/local/."
  task(:build => [:build_mecab, :build_mecab_dict, :build_groonga])

  desc "Build MeCab and install it into vendor/local/."
  task(:build_mecab) do
    tmp_dir = "tmp/mecab"
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    require 'open-uri'
    mecab_version = "0.98"
    mecab_base = "mecab-#{mecab_version}"
    mecab_tar_gz = "#{mecab_base}.tar.gz"
    mecab_tar_gz_url = "http://sourceforge.net/projects/mecab/files/mecab/#{mecab_version}/#{mecab_tar_gz}/download"
    Dir.chdir(tmp_dir) do
      open(mecab_tar_gz_url) do |downloaded_tar_gz|
        File.open(mecab_tar_gz, "wb") do |tar_gz|
          tar_gz.print(downloaded_tar_gz.read)
        end
      end
      sh("tar", "xzf", mecab_tar_gz) or exit(false)
    end
    Dir.chdir(File.join(tmp_dir, mecab_base)) do
      mecab_patches.each do |patch|
        sh("patch -p1 < #{patches_dir + patch}")
      end
      sh("./configure",
         "--prefix=#{binary_dir}",
         "--host=#{host}") or exit(false)
      sh("env", "GREP_OPTIONS=--text", "nice", "make", "-j8") or exit(false)
      sh("env", "GREP_OPTIONS=--text", "make", "install") or exit(false)

      mecab_rc_path = File.join(binary_dir, "etc", "mecabrc")
      win32_mecab_rc_path = File.join(binary_dir, "bin", "mecabrc")
      mv(mecab_rc_path, win32_mecab_rc_path)

      mecab_files_dir = File.join(vendor_dir, "mecab")
      mkdir_p(mecab_files_dir)
      files = ["AUTHORS", "BSD", "COPYING", "GPL", "LGPL"]
      cp(files, mecab_files_dir)
    end
  end

  task(:build_mecab_dict) do
    tmp_dir = "tmp/mecab_dict"
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    require 'open-uri'
    naist_jdic_base = "mecab-naist-jdic-0.6.3-20100801"
    naist_jdic_tar_gz = "#{naist_jdic_base}.tar.gz"
    naist_jdic_tar_gz_url = "http://osdn.dl.sourceforge.jp/naist-jdic/48487/#{naist_jdic_tar_gz}"
    Dir.chdir(tmp_dir) do
      open(naist_jdic_tar_gz_url) do |downloaded_tar_gz|
        File.open(naist_jdic_tar_gz, "wb") do |tar_gz|
          tar_gz.print(downloaded_tar_gz.read)
        end
      end
      sh("tar", "xzf", naist_jdic_tar_gz) or exit(false)
    end
    Dir.chdir(File.join(tmp_dir, naist_jdic_base)) do
      sh("./configure",
         "--with-dicdir=#{binary_dir}/share/mecab/dic/naist-jdic",
         "--with-charset=utf-8") or exit(false)
      sh("make", "-j8") or exit(false)
      sh("make", "install-data") or exit(false)

      naist_jdic_files_dir = File.join(vendor_dir, "mecab-naist-jdic")
      mkdir_p(naist_jdic_files_dir)
      files = ["AUTHORS", "COPYING"]
      cp(files, naist_jdic_files_dir)
    end
    dictionary_dir = '$(rcpath)\..\share\mecab\dic\naist-jdic'
    mecab_rc_path = File.join(binary_dir, "bin", "mecabrc")
    mecab_rc_content = File.read(mecab_rc_path)
    File.open(mecab_rc_path, "w") do |mecab_rc|
      mecab_rc.print(mecab_rc_content.gsub(/^dicdir\s*=.+$/,
                                           "dicdir = #{dictionary_dir}"))
    end
  end

  desc "Build groonga and install it into vendor/local/."
  task(:build_groonga) do
    tmp_dir = "tmp/groonga"
    rm_rf(tmp_dir)
    mkdir_p(tmp_dir)
    Dir.chdir(tmp_dir) do
      sh("git", "clone", "git://github.com/groonga/groonga.git") or exit(false)
    end
    Dir.chdir(File.join(tmp_dir, "groonga")) do
      sh("./autogen.sh") or exit(false)
      mecab_config = File.join(binary_dir, "bin", "mecab-config")
      args = ["./configure",
              "--prefix=#{binary_dir}",
              "--host=#{host}",
              "--without-cutter",
              "--disable-benchmark"]
      if File.exist?(mecab_config)
        args << "--with-mecab-config=#{mecab_config}"
      else
        args << "--without-mecab"
      end
      sh(*args) or exit(false)
      sh("env", "GREP_OPTIONS=--text", "nice", "make", "-j8") or exit(false)
      sh("env", "GREP_OPTIONS=--text", "make", "install") or exit(false)

      groonga_files_dir = File.join(vendor_dir, "groonga")
      mkdir_p(groonga_files_dir)
      files = ["AUTHORS", "COPYING"]
      cp(files, groonga_files_dir)
    end
  end
end