#! /usr/bin/ruby
# Usage: rip-package-gem NAME VERSION

require 'rip/script'

source, path, version = Rip::Package.parse_args(ARGV)

# Only handle root packages
exit 3 if path != '/'

unless source =~ /\.gem$/ || source =~ /^(\w|-)+$/
  exit 3 # Can't handle source
end


# Stubbed Gem class for YAML
module Gem
  class Faker
    def respond_to?(ivar)
      instance_variable_defined?("@#{ivar}") || super
    end

    def method_missing(ivar, *args)
      respond_to?(ivar) ? instance_variable_get("@#{ivar}") : super
    end
  end

  class Dependency < Faker
    def tweedle
      if respond_to?(:version_requirements)
        version_requirements.requirements[0].first
      elsif respond_to?(:requirement)
        requirement.requirements[0].first
      end
    end

    def version
      version_requirements.requirements[0].last.version
    end

    def type
      @type || :runtime
    end
  end
  class Specification < Faker; end
  class Requirement   < Faker; end
  class Version       < Faker; end

  # Some older specs reference Version::Requirement
  Version::Requirement = Requirement
end


class Fetcher
  def unpack(name, version)
  end

  def dependencies(name, version)
    io = specification(name, version)
    return [] unless io

    spec = YAML.load(io)

    deps = spec.dependencies

    deps = deps.select { |dep| dep.type == :runtime }

    deps.map do |dep|
      dep_line(dep.name, dep.tweedle, dep.version)
    end
  end

  def dep_line(name, tweedle, version)
    if version == '0'
      name
    elsif tweedle == '='
      "#{name} #{version}"
    else
      "#{name} #{tweedle}#{version}"
    end
  end
end

class GemFetcher < Fetcher
  def list(name)
    gems = gem("list #{name} --remote").split("\n")
    if gems.detect { |f| f =~ /^#{name} \((.+)\)/ }
      $1
    end
  end

  def unpack(name, version)
    info "fetching #{name} #{version}"
    path = gem_path = nil

    cd Rip.cache do
      begin
        if gem("fetch", name, "-v #{version}") =~ /Downloaded (.+)/
          gem_path = File.expand_path($1) + ".gem"
        else
          gem = name.dup
          gem << " #{version}" if version
          abort "#{gem} not found"
        end

        sh("gem unpack '#{gem_path}' --target='#{Rip.packages}'") =~
          /^Unpacked gem: '(.+)'$/
        path = $1
      ensure
        rm_rf gem_path if File.exist?(gem_path.to_s)
      end
    end

    if path.nil?
      abort "#{name} #{version} not found"
    end

    path
  end

  def specification(name, version)
    spec = gem("specification -r #{name} -v #{version}")
    $?.success? ? spec : nil
  end
end

class RpgFetcher < Fetcher
  def list(name)
    if rpg("list -a #{name}") =~ /^#{name} (.+)$/
      $1
    end
  end

  def unpack(name, version)
    info "fetching #{name} #{version}"
    if path = rpg("unpack", name, version)
      path.chomp
    end
  end

  def specification(name, version)
    spec = rpg("unpack", "-c", "-m", name, version)
    $?.success? ? spec : nil
  end
end

fetcher = rpg_available? ? RpgFetcher.new : GemFetcher.new


version = fetcher.list(source) if version.nil?
version = fetcher.list(source) unless version && version =~ /^\d/
version = version.split(" ", 2).first if version && version =~ /\s/

package_path = "#{Rip.packages}/#{source}-#{Rip.md5("#{source}#{version}")}"

synchronize(package_path) do
  if File.directory?(package_path)
    puts package_path
    exit 0
  end

  at_exit do
    if !exited_successfully?
      debug "cleaning up #{package_path}"
      rm_rf package_path
    end
  end

  cd Rip.packages

  path = fetcher.unpack(source, version)

  if !File.exist?(path)
    abort "#{source} #{version} not found"
  end

  mv path, package_path

  write("#{package_path}/metadata.rip") { "#{source} #{version}" }

  if !File.exists?(deps_rip = "#{package_path}/deps.rip")
    deps = fetcher.dependencies(source, version)

    if deps.any?
      write(deps_rip) { deps.sort }
    end
  end

  puts package_path
end
