require 'reap/task'

require "enumerator"
require "http-access2"


#
# Release Task
#

# This Rake task releases files to RubyForge and other GForge instaces
# or SourceForge clones. In its most simple usage it looks like:
#
#   project = MetaProject::Project::XForge::RubyForge.new('xforge')
#   # Create a new release of the xforge project on Rubyforge.
#   task :release => [:gem] do
#     Rake::XForge::Release.new(project) {}
#   end
#
# The previous example will use defaults where it can. It will prompt
# you for your xForge user name and password before it uploads all
# gems under the pkg folder and creates a RubyForge release.
#
# While defaults are nice, you may want a little more control. You can
# specify additional attributes:
#
# * #user_name
# * #password
# * #changes_file
# * #version
# * #files
# * #release_name
# * #release_notes
# * #release_changes
# * #package_name
#
# Example:
#   project = MetaProject::Project::XForge::RubyForge.new('xforge')
#   task :release => [:gem] do
#     release_files = FileList[ 'pkg/*.gem', 'CHANGES' ]
#
#     Rake::XForge::Release.new(project) do |xf|
#       # Never hardcode user name and password in the Rakefile!
#       xf.user_name = ENV['RUBYFORGE_USER']
#       xf.password = ENV['RUBYFORGE_PASSWORD']
#       xf.files = release_files.to_a
#       xf.release_name = "XForge 0.1"
#   end
#
# This can be invoked from the command line:
#
#   rake release RUBYFORGE_USER=myuser \
#                RUBYFORGE_PASSWORD=mypassword
#
# If you don't like blocks, you can do like this:
#
#   project = MetaProject::Project::XForge::RubyForge.new('xforge')
#   task :release => [:gem] do
#     xf = Rake::XForge::Release.new(project)
#     ... # Set additional attributes
#     xf.execute
#   end

class Reap::Release < Reap::Task

  task_desc "Release distribution files."

  section_required true

  attr_accessor :project, :version
  attr_accessor :host, :username, :password
  attr_accessor :package, :packageid, :groupid, :private, :processor
  attr_accessor :name, :changes, :notes, :changelog, :notelog
  attr_accessor :cookiejar

  def init
    @project   ||= master['rubyforge']['project'] || master['name']
    @version   ||= master['version'] || master['date']

    @host      ||= 'rubyforge.org'
    @username  ||= master['rubyforge']['username']
    @password  ||= master['rubyforge']['password']

    @package   ||= master['rubyforge']['package']
    @packageid ||= master['rubyforge']['packageid']
    @groupid   ||= master['rubyforge']['groupid']
    @private   ||= false
    @processor ||= 'Any'

    #@files     ||= section['files']
    @name      ||= master['version'] || master['date']
    @date      ||= master['date'] || Time::now.strftime('%Y-%m-%d %H:%M')

    @cookiejar ||= File::join(File::expand_path("~"), ".rubyforge.cookie_jar")
  end

  # run task

  def run
    abort "missing field -- package" unless @package
    abort "missing field -- packageid" unless @packageid
    abort "missing field -- groupid" unless @groupid
    # add more...

    case @host
    when 'rubyforge', 'rubyforge.org'
    else
      puts %{Unrecognized release host '#{@host}'. Skipped.}
      skip = true
    end

    unless skip
      puts "Reap is preparing release ..."
      @password = get_password
      login
      #create_package (doesn't fit)
      add_release( @file.unshift )
      @files.each { |f| add_file( f ) }
      add_release
    end
  end

  FILETYPES = {
    ".deb"     => 1000,
    ".rpm"     => 2000,
    ".zip"     => 3000,
    ".bz2"     => 3100,
    ".gz"      => 3110,
    ".src.zip" => 5000,
    ".src.bz2" => 5010,
    ".src.gz"  => 5020,
    ".src.rpm" => 5100,
    ".src"     => 5900,
    ".jpg"     => 8000,
    ".txt"     => 8100,
    ".text"    => 8100,
    ".htm"     => 8200,
    ".html"    => 8200,
    ".pdf"     => 8300,
    ".oth"     => 9999,
    ".ebuild"  => 1300,
    ".exe"     => 1100,
    ".dmg"     => 1200,
    ".tar.gz"  => 5000,
    ".tgz"     => 5000,
    ".gem"     => 1400,
    ".pgp"     => 8150,
    ".sig"     => 8150,
  }

  PROCESSORS = {
    "i386"       => 1000,
    "IA64"       => 6000,
    "Alpha"      => 7000,
    "Any"        => 8000,
    "PPC"        => 2000,
    "MIPS"       => 3000,
    "Sparc"      => 4000,
    "UltraSparc" => 5000,
    "Other"      => 9999,
  }


  # login

  def login
    page = "/account/login.php"
    method = "post_content"
    form = {
      "return_to" => "",
      "form_loginname" => @username,
      "form_pw" => @password,
      "login" => "Login"
    }
    http_transaction( page, method, form )
  end

  # create a new package ( should be another task )

  def create_package
    page = "/frs/admin/index.php"
    method = "post_content"
    form = {
      "group_id"     => @groupid,
      "package_name" => @package,
      "func"         => "add_package",
      "is_public"    => (@private ? 0 : 1),
      "submit"       => "Create This Package",
    }
    http_transaction( page, method, form )
  end

  # add a new release

  def add_release( userfile )
    page = "/frs/admin/qrs.php"
    method = "post_content"

    type_id = @type || userfile[%r|\.[^\./]+$|]
    type_id = FILETYPES[type_id]

    proc_id = PROCESSORS[@processor]

    # how to use these?
    notes   = @notes ? @notes : ( @notelog ? open(@notelog) : nil )
    changes = @changes ? @changes : ( @changelog ? open(@changelog) : nil )

    userfile = open(userfile)

    preformatted = '1'

    form = {
      "group_id"     => @groupid,
      "package_id"   => @packageid,
      "release_name" => @name,
      "release_date" => @date,
      "type_id"      => type_id,
      "processor_id" => proc_id,
      "preformatted" => preformatted,
      "userfile"     => userfile,
      "submit"       => "Release File"
    }

    boundary = Array::new(8){ "%2.2d" % rand(42) }.join('__')
    extheader = { 'content-type'=>"multipart/form-data; boundary=___#{ boundary }___" }

    http_transaction( page, method, form, extheader )
  end

  # add file to release

  def add_file( userfile )

    # to do

  end

  # http transaction

  def http_transaction( page, method, form, extheader={} )
    client = HTTPAccess2::Client::new ENV['HTTP_PROXY']
    client.debug_dev = STDERR if ENV['DEBUG']
    client.set_cookie_store cookie_jar
    # fixes http-access2 bug
    client.redirect_uri_callback = lambda do |res|
      page = res.header['location'].first
      page = page =~ %r/http/ ? page : "#{ config['uri'] }/#{ page }"
      page
    end
    response = client.send "#{ method }", "#{ config['uri'] }/#{ page }", form, extheader
    client.save_cookie_store
  end


# fixes http-access2 bug
BEGIN {
   require "http-access2"
   module WebAgent::CookieUtils
     def domain_match(host, domain)
       case domain
       when /\d+\.\d+\.\d+\.\d+/
         return (host == domain)
       when '.'
         return true
       when /^\./
         #return tail_match?(domain, host)
         return tail_match?(host, domain)
       else
         return (host == domain)
       end
     end
   end
}