# Shoeboxer
# version 2
# originally by Anonymous
# this version is a rewrite by chiisaitsu <chiisaitsu@gmail.com>
# tested on Shoes 2 Raisins 0.r1134
#
# Parses http://the-shoebox.org/ and displays all the apps there.  You can
# run apps directly from this program if they're supplied as Ruby source or
# shy's.  We use a cheapo way to run them -- new Shoes processes.

# We'll try to choose the appropriate command to spawn Shoes based on your
# platform.  Set SHOES_EXEC to override.
# SHOES_EXEC        = 'shoes %s'
SHOES_EXEC_PLAT   = { :win32 => 'shoes %s',
                      :osx   => 'open -a Shoes %s',
                      :other => 'shoes %s' }

SHOEBOX_URL       = 'http://the-shoebox.org/'
DEFAULT_THUMB_URL = 'http://the-shoebox.org/images/shoes_default_thumb.png'
DEFAULT_IMG_URL   = 'http://the-shoebox.org/images/shoes_default_photo.png'

THUMB_WIDTH       = 150
IMG_WIDTH         = 300

require 'hpricot'
require 'tmpdir'

APP_NAME    = 'Shoeboxer'
APP_VERSION = '2'

PLATFORM = case RUBY_PLATFORM
           when /win32/:  :win32
           when /darwin/: :osx
           else           :other
           end

class Shoeboxer < Shoes

  url '/',                 :index
  url '/shoebox_app/(.+)', :shoebox_app

  SHOEBOX_APPS= [] # { :name, :url, :thumb_url }

  def download_index
    shoebox_uri= URI.parse(SHOEBOX_URL)
    download(SHOEBOX_URL) do |resp|
      Hpricot(resp.response.body).search('#app_grid > a').each do |anchor|
        SHOEBOX_APPS << {
          :name      => anchor.at('h6').inner_text,
          :url       => anchor['href'],
          :thumb_url => img_url(anchor.at('img'), DEFAULT_THUMB_URL)
        }
      end
      yield
    end
  end

  def hsl h, s, l
    h, s, l= h.to_f, s.to_f, l.to_f
    q= l < 0.5 ? l * (1 + s) : l + s - (l * s)
    p= (2 * l) - q
    hk= h / 360
    tc= [hk + (1.0 / 3.0), hk, hk - (1.0 / 3.0)].map do |c|
      if c < 0:    c + 1
      elsif c > 1: c - 1
      else         c
      end
    end
    rgb(*tc.map do |c|
      if c < 1.0 / 6.0:                  p + ((q - p) * 6 * c)
      elsif 1.0 / 6.0 <= c && c < 0.5:   q
      elsif 0.5 <= c && c < (2.0 / 3.0): p + ((q - p) * 6 * ((2.0 / 3.0) - c))
      else                               p
      end
    end)
  end

  def hsl_gradient h, s, l1, l2
    gradient(hsl(h, s, l1), hsl(h, s, l2))
  end

  def img_url img_elt, default
    shoebox_uri= URI.parse(SHOEBOX_URL)
    begin
      uri= URI.parse(img_elt['src'])
      uri= shoebox_uri.merge(uri) if uri.relative?
      uri.to_s
    rescue StandardError
      default
    end
  end

  def index
    background('#eee')
    if SHOEBOX_APPS.empty?
      msg= loading_msg
      download_index do
        msg.remove
        index_helper
      end
    else
      index_helper
    end
  end

  def index_app_thumb sapp
    polaroid_margin= 5
    polaroid_width= THUMB_WIDTH + (polaroid_margin * 2)
    margin_left= (THUMB_WIDTH + (polaroid_margin * 2)) / 2
    stack(:margin_left   => polaroid_width / 2,
          :width         => polaroid_width + margin_left,
          :margin_bottom => 10,
          :margin_top    => 10) do
      background(hsl_gradient(rand_hue, 0.3, 0.9, 0.8))
      url= "/shoebox_app/#{sapp[:url]}"
      stack(:margin_left => polaroid_margin) do
        img= image(sapp[:thumb_url])
        img.click { visit(url) }
      end
      l= link(sapp[:name],
              :click     => url,
              :stroke    => hsl(rand_hue, 0.5, 0.2),
              :underline => 'none',
              :weight    => 'bold')
      para(l, :align => 'center')
    end
  end

  def index_helper
    flow do
      background('#000')
      subtitle('The Shoebox', :stroke => '#fff', :align => 'center')
    end
    SHOEBOX_APPS.each { |sapp| index_app_thumb(sapp) }
  end

  def loading_msg
    caption('Just a moment...', :align => 'center')
  end

  def rand_hsl s, l
    hsl(rand_hue, s, l)
  end

  def rand_hue
    (rand * 360).round
  end

  def run_app rb_or_shy_url
    fname= File.join(Dir::tmpdir, File.basename(rb_or_shy_url))
    download(rb_or_shy_url, :save => fname) do
      cmd= (defined?(SHOES_EXEC) ? SHOES_EXEC : SHOES_EXEC_PLAT[PLATFORM]).
           gsub(/%s/, fname)
      unless system(cmd)
        alert("Error running app with the following command:\n#{cmd}\n\n" +
              "Try setting SHOES_EXEC in the #{APP_NAME} source.")
      end
    end
  end

  def shoebox_app http_url
    background('#eee')
    msg= loading_msg
    download(http_url) do |resp|
      msg.remove
      doc= Hpricot(resp.response.body)
      shoebox_app_banner(doc)
      shoebox_app_desc(doc)
      shoebox_app_versions(doc)
      flow do
        caption(link('<   Back to the Shoebox', :click => '/'),
                :align => 'center')
      end
    end
  end

  def shoebox_app_banner doc
    polaroid= doc.at('div.app_polaroid')
    img_src= polaroid.at('img')
    name= polaroid.at('h2 > a').inner_text
    stack(:width => IMG_WIDTH) do
      background(hsl_gradient(rand_hue, 0.3, 0.7, 0.9))
      tagline(name, :align  => 'center', :stroke => rand_hsl(0.5, 0.2))
      image(img_url(img_src, DEFAULT_IMG_URL))
    end
  end

  def shoebox_app_desc doc
    desc= doc.at('div.description_display').inner_text
    flow { para(desc) }
  end

  def shoebox_app_versions doc
    versions= doc.search('div.version > a')
    versions.reject! { |anchor| /\.(rb|shy)$/ !~ anchor['href'] } if versions
    stack(:margin => 15) do
      hue= rand_hue
      sat= 0.3
      flow do
        background(hsl(hue, sat, 0.75))
        para(strong('Versions'))
      end
      stack do
        background(hsl_gradient(hue, sat, 0.8, 0.9))
        if versions.empty?
          para(em('No luck, boss'))
        else
          versions.each do |anchor|
            para(link(anchor.inner_text) { run_app(anchor['href']) })
          end
        end
      end
    end
  end

end

Shoes.app(:title => "#{APP_NAME} #{APP_VERSION}", :width => IMG_WIDTH)
