#!/usr/bin/env ruby

# Evan's Colorz stuff comes in handy for warnings!
module Colorz
  extend self

  COLOR_CODES = { red: 1, green: 2, yellow: 3, blue: 4, purple: 5, cyan: 6, white: 7 }
  DEFAULT_COLORS = "\e[0m\e[37m\e[40m"

  def color string, code = 1
    "\e[3#{code}m#{string}#{DEFAULT_COLORS}"
  end

  COLOR_CODES.each do |color, code|
    define_method(color) { color(self, code) }
  end
end

String.send(:include, Colorz)

class H4MBuild

  attr_accessor :build_path
  attr_accessor :getharvest_path
  attr_accessor :key_path

  def initialize(build, getharvest, key)
    @build_path      = build
    @getharvest_path = getharvest
    @key_path        = key
  end

  def log(msg)
    puts "#{ File.basename(build_path) }:#{ human_version }b#{ version }: #{ msg }"
  end

  def ship_it!
    if properly_signed? && spctl_check?
      log("Signature verified.".green)
    else
      log("This app has not been signed by an Apple ID. Gatekeeper will prevent it from running. Try again with a signed app.".red)
      exit(1)
    end

    if sandboxed?
      log("This app has sandboxing enabled. Please disable it and rebuild. (Sparkle is not compatible with sandboxed apps.".red)
      exit(1)
    else
      log("Verified sandboxing is off.".green)
    end

    zip
    copy_to_repo
    link_harvest_zip
    add_release_notes_template
    prepare_appcast

    test_download_instructions
  end

  def properly_signed?
    `codesign -vv --verify --deep-verify #{ build_path }`
    $?.exitstatus == 0
  end

  def spctl_check?
    result = `spctl --assess --verbose --raw #{ build_path } 2>&1`
    return true if result.match(/.*Harvest\.app: accepted.*/)
    log("sptcl --assess failed: #{ result }".red)
    false
  end

  def sandboxed?
    entitlement_data = `codesign -d --entitlements :- #{ build_path }`
    entitlement_data.match(/<key>com.apple.security.app-sandbox<\/key>\s+<true\/>/)
  end

  def zip
    log("Zipping...".green)
    # The command:
    #  ditto -c -k --sequesterRsrc --keepParent src_directory archive.zip
    # will create a PKZip archive similarly to the Finder's Compress functionality.
    `pushd #{ build_dir }; ditto -c -k --sequesterRsrc --keepParent  #{ File.basename(build_path) } #{ File.basename(zip_file_name) } ; popd`
  end

  def copy_to_repo
    log("Copying zip to #{ h4m_public_dir} ...".green)
    `cp #{ zip_file_name } #{ h4m_public_dir }`
  end

  def link_harvest_zip
    log("Linking #{ File.basename(zip_file_name) } to Harvest.zip...".green)
    `pushd #{ h4m_public_dir }; ln -sf #{ File.basename(zip_file_name) } Harvest.zip; popd` 
  end

  def add_release_notes_template
    release_notes = <<-RELEASE_NOTES
      <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
         "http://www.w3.org/TR/html4/loose.dtd">
      <html lang="en">
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Harvest for Mac #{ human_version }</title>
        <!-- Date: #{ Time.now.strftime("%Y-%m-%d") } -->
        <style>
          body { font-family: helvetica,arial,sans-serif }
          h1 { margin-top: 3px; margin-bottom: 3px; }
          h2 { margin-top: 2px; margin-bottom: 4px; }
          p  { margin-top: 3px; }
        </style>
      </head>
      <body>
        <h1>Harvest for Mac</h1>
        <h2>Version #{ human_version }</h2>
        <ul>
          <!-- Add release notes here! -->
          <li>EDIT ME</li>
        </ul>
      </body>
      </html>
    RELEASE_NOTES
    release_notes_filename = File.expand_path("release_notes.#{ human_version }.html", h4m_public_dir)
    log("Creating release notes #{ release_notes_filename }...".green)
    File.open(release_notes_filename, 'w') { |f| f.puts release_notes }
    `open #{ release_notes_filename }`
  end

  def prepare_appcast
    time = Time.now.utc.strftime "%a, %d %b %Y %H:%M:%S %z"
    sig = `openssl dgst -sha1 -binary < "#{ zip_file_name }" | openssl dgst -dss1 -sign #{ key_path } | openssl enc -base64`.chomp

    log("Now add this to #{ appcast_path }...".green)
    #`open #{ appcast_path }`
    puts <<-SPARKLE
    <item>
      <title>Version #{ human_version }</title>
      <sparkle:minimumSystemVersion>10.8.0</sparkle:minimumSystemVersion>
      <sparkle:releaseNotesLink><%= root_url %>/harvest/mac/release_notes.#{ human_version }.html</sparkle:releaseNotesLink>
      <pubDate>#{ time }</pubDate>
      <enclosure url="<%= root_url %>/harvest/mac/#{ File.basename(zip_file_name) }"
        sparkle:version="#{ version }" 
        sparkle:shortVersionString="#{ human_version }"
        sparkle:dsaSignature="#{sig}"
        length="#{ zip_size }" 
        type="application/octet-stream" />
    </item>
    SPARKLE

  end

  def test_download_instructions
    puts "#{ 'WARNING'.red }: To be #{ 'absolutely sure this zip file will work'.red }, you must:"
    puts "#{ '1'.red }. Upload this zip file somewhere web accessible, like your public Dropbox folder."
    puts "#{ '2'.red }. Download it via a browser on a #{ 'different'.yellow } machine."
    puts "#{ '3'.red }. Run it on that machine with #{ 'Gatekeeper set to allow AppStore or developer-signed apps only'.red }."
  end

  protected

  def build_dir
    File.dirname(self.build_path)
  end

  def h4m_public_dir
    File.expand_path("./public/harvest/mac/", getharvest_path)
  end

  def appcast_path
    File.expand_path("appcast.erb", File.expand_path('./app/views/pages', getharvest_path))
  end

  def plist(name)
    `defaults read #{ File.expand_path('./Contents/Info.plist', self.build_path) } #{ name }`.chomp
  end

  def human_version
    plist('CFBundleShortVersionString')
  end

  def version
    plist('CFBundleVersion')
  end

  def zip_file_name
    "#{ self.build_path.chomp(".app") }.#{ self.human_version }.zip"
  end

  def zip_size
    File.stat(zip_file_name).size
  end
    
end

if ARGV.size != 3
  puts "Usage: #{ $PROGRAM_NAME } path_to_signed_build path_to_GetHarvest_repo path_to_dsa_priv.pem"
  exit(1)
end

#key = File.expand_path("../ProjectXKey/dsa_priv.pem", File.dirname(__FILE__))

build = H4MBuild.new(ARGV[0], ARGV[1], ARGV[2])
build.ship_it!




