require 'buildr/java/packaging'

module BuildrGemjar
  class GemjarTask < Buildr::Packaging::Java::JarTask
    def initialize(*args)
      super
      @target = File.expand_path("../gem_home", self.to_s)
      # It doesn't seem like this should be necessary or useful --
      # the directory is also created by a task dependency.
      # However, if it isn't here, the file gem dependency is not
      # correctly taken into account.  (Try commenting it out and
      # running the specs.)
      mkdir_p @target

      clean
      include(@target, :as => '.')
      enhance do
        install_gems
      end
    end

    def with_gem(*args)
      new_gem =
        if args.first.respond_to?(:has_key?)
          FileSourcedGem.new(args.first[:file])
        else
          NamedGem.new(args[0], args[1])
        end
      gems << new_gem
      enhance new_gem.dependencies
      self
    end

    def clear_sources
      sources.clear
      self
    end

    def add_source(source)
      sources << source
      self
    end

    def gems
      @gems ||= []
    end

    def sources
      @sources ||= ['http://rubygems.org']
    end

    def gem_home
      @target
    end

    private

    def install_gems
      fail "No gems specified.  Did you call #with_gem?" if gems.empty?
      gems.each do |gem|
        cmd = [
          "GEM_HOME='#{@target}'",
          "java",
          "-jar",
          BuildrGemjar.jruby_complete_jar,
          "-S",
          "gem",
          "install",
          # The behavior of --source changed between rubygems 1.3.6
          # and 1.5.0.  Previously, specifying source(s) overrode the
          # default source(s).  After, specifying source(s) adds onto
          # the default list.  To get the old behavior, you have to
          # include "--clear-sources".  This code wants the old
          # behavior, but no version of jruby-complete contains a
          # version of rubygems > 1.3.6, so it's not an issue today.
          # Later we'll probably have to do version sniffing or
          # something.
          # "--clear-sources",
          sources.collect { |s| ["--source", "'#{s}'"] },
          "--no-ri",
          "--no-rdoc",
          gem.install_command_elements
        ].flatten.join(" ")

        trace cmd
        out = %x[#{cmd} 2>&1]
        trace out
        if out =~ /ERROR/
          fail out.split("\n").grep(/ERROR/).join("\n")
        end
      end
    end

    class FileSourcedGem
      attr_accessor :filename

      def initialize(filename)
        @filename = filename
      end

      def kind; :file; end

      def install_command_elements
        [filename]
      end

      def dependencies
        [(filename if File.exist?(filename))].compact
      end
    end

    class NamedGem
      attr_accessor :name, :version

      def initialize(name, version)
        @name = name
        @version = version
      end

      def kind; :named; end

      def install_command_elements
        [name].tap do |elts|
          elts << "-v '#{version}'" if version
        end
      end

      def dependencies
        []
      end
    end
  end

  include Buildr::Extension

  def package_as_gemjar(filename)
    GemjarTask.define_task(filename)
  end

  def package_as_gemjar_spec(spec)
    spec.merge(:type => 'jar')
  end

  def self.jruby_version
    version = Buildr.settings.build['jruby'] ||
      (JRUBY_VERSION if Kernel.const_defined?(:JRUBY_VERSION)) ||
      "1.5.6"
  end

  def self.jruby_artifact
    "org.jruby:jruby-complete:jar:#{jruby_version}"
  end

  class << self
    attr_accessor :jruby_complete_jar
  end

  after_define do |project|
    jruby = project.artifact(BuildrGemjar.jruby_artifact)
    project.packages.each do |pkg|
      if pkg.is_a?(GemjarTask)
        directory pkg.gem_home
        pkg.enhance [pkg.gem_home, jruby]
        BuildrGemjar.jruby_complete_jar = jruby.to_s
      end
    end
  end
end

class Buildr::Project
  include BuildrGemjar
end