# require
require 'rubygems'

#### INLINE: fixed version of https://github.com/github/upload ####

require 'tempfile'
require 'nokogiri'
require 'httpclient'
require 'stringio'
require 'json'
require 'faster_xml_simple'

module Net
  module GitHub
    class Upload
      VERSION = '0.0.5'
      def initialize params=nil
        @login = params[:login]
        @token = params[:token]

        if @login.empty? or @token.empty?
          raise "login or token is empty"
        end
      end

      def upload info
        unless info[:repos]
          raise "required repository name"
        end
        info[:repos] = @login + '/' + info[:repos] unless info[:repos].include? '/'

        if info[:file]
          file = info[:file]
          unless File.exist?(file) && File.readable?(file)
            raise "file does not exsits or readable"
          end
          info[:name] ||= File.basename(file)
        end
        unless  info[:file] || info[:data]
          raise "required file or data parameter to upload"
        end

        unless info[:name]
          raise "required name parameter for filename with data parameter"
        end

        if info[:replace]
          list_files(info[:repos]).each { |obj|
            next unless obj[:name] == info[:name]
            delete info[:repos], obj[:id]
          }
        elsif list_files(info[:repos]).any?{|obj| obj[:name] == info[:name]}
          raise "file '#{info[:name]}' is already uploaded. please try different name"
        end

        info[:content_type] ||= 'application/octet-stream'
        stat = HTTPClient.post("https://github.com/#{info[:repos]}/downloads", {
          "file_size"    => info[:file] ? File.stat(info[:file]).size : info[:data].size,
          "content_type" => info[:content_type],
          "file_name"    => info[:name],
          "description"  => info[:description] || '',
          "login"        => @login,
          "token"        => @token
        })

        unless stat.code == 200
          raise "Failed to post file info"
        end

        upload_info = JSON.parse(stat.content)
        if info[:file]
          f = File.open(info[:file], 'rb')
        else
          f = Tempfile.open('net-github-upload')
          f << info[:data]
          f.flush
        end
        stat = HTTPClient.post("http://github.s3.amazonaws.com/", [
          ['Filename', info[:name]],
          ['policy', upload_info['policy']],
          ['success_action_status', 201],
          ['key', upload_info['path']],
          ['AWSAccessKeyId', upload_info['accesskeyid']],
          ['Content-Type', upload_info['content_type'] || 'application/octet-stream'],
          ['signature', upload_info['signature']],
          ['acl', upload_info['acl']],
          ['file', f]
        ])
        f.close

        if stat.code == 201
          return FasterXmlSimple.xml_in(stat.content)['PostResponse']['Location']
        else
          raise 'Failed to upload' + extract_error_message(stat)
        end
      end

      def replace info
         upload info.merge( :replace => true )
      end

      def delete_all repos
        unless repos
          raise "required repository name"
        end
        repos = @login + '/' + repos unless repos.include? '/'
        list_files(repos).each { |obj|
          delete repos, obj[:id]
        }
      end

      private

      def extract_error_message(stat)
        # @see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/ErrorResponses.html
        error = FasterXmlSimple.xml_in(stat.content)['Error']
        " due to #{error['Code']} (#{error['Message']})"
      rescue
        ''
      end

      def delete repos, id
        HTTPClient.post("https://github.com/#{repos}/downloads/#{id.gsub( "download_", '')}", {
          "_method"      => "delete",
          "login"        => @login,
          "token"        => @token
        })
      end

      def list_files repos
        raise "required repository name" unless repos
        res = HTTPClient.get_content("https://github.com/#{repos}/downloads", {
          "login" => @login,
          "token" => @token
        })
        Nokogiri::HTML(res).xpath('id("manual_downloads")/li').map do |fileinfo|
          obj = {
            :description => fileinfo.at_xpath('descendant::h4').text,
            :date        => fileinfo.at_xpath('descendant::p/time').attribute('title').text,
            :size        => fileinfo.at_xpath('descendant::p/strong').text,
            :id          => /\d+$/.match(fileinfo.at_xpath('a').attribute('href').text)[0]
          }
          anchor = fileinfo.at_xpath('descendant::h4/a')
          obj[:link] = anchor.attribute('href').text
          obj[:name] = anchor.text
          obj
        end
      end
    end
  end
end

#### END INLINE ####

# setup
login = `git config github.user`.chomp  # your login for github
token = `git config github.token`.chomp # your token for github
repos = 'KentBeck/junit'               # your repos name (like 'taberareloo')
gh = Net::GitHub::Upload.new(
  :login => login,
  :token => token
)

version = ARGV[0]

def upload(gh, version, repos, filename, description)
  gh.upload(:repos => repos, 
            :file => "junit#{version}/#{filename}", 
            :description => description)
end

upload(gh, version, repos, "junit-#{version}-src.jar", 'Source jar')
upload(gh, version, repos, "junit-#{version}.jar", 'Basic jar')
upload(gh, version, repos, "junit-dep-#{version}.jar", 'Jar without hamcrest')
upload(gh, version, repos, "junit#{version}.zip", 'Source zip')

# # file upload
# direct_link = gh.upload(
#   :repos => repos,
#   :file  => 'test/test',
#   :description => "test file"
# )
# # direct link is link to Amazon S3.
# # Because GitHub refrection for file changing is async,
# # if you get changed file synchronously, you use this "direct_link"
# 
# # data upload
# # you can define content_type => Amazon S3 Content-Type
# time = Time.now.to_i
# direct_link = gh.upload(
#   :repos => repos,
#   :data  => 'test',
#   :name  => "test_#{time}.txt",
#   :content_type => 'text/plain',
#   :description => "test file2"
# )
# 
# # replace file or data
# # thx id:rngtng !
# direct_link = gh.replace(
#   :repos => repos,
#   :file  => 'test/test',
#   :description => "test file"
# )
# direct_link = gh.replace(
#   :repos => repos,
#   :data  => 'test',
#   :name  => "test_#{time}.txt",
#   :content_type => 'text/plain',
#   :description => "test file2"
# )
