#!/usr/bin/ruby

# project launcher

require 'find'
require 'rubygems'
require 'appscript'
include Appscript

DEV_ROOT = "~/development"
MAX_DEPTH = 2
PROFILES =<<__END_PROFILES__
rails:
  detection:
    folders:
      - app
      - config
      - db
      - log
      - public
      - test
      - vendor
  tasks:
    - label: Dev Log
      command: rake log:clear; tail -f log/development.log
    - label: Rails Console
      command: ruby script/console
    - label: IRB
      command: irb
    - label: Mongrel
      command: mate .; mongrel_rails start
    - label: Testing
      command: rake
    - label: Terminal
      command:
erlang:
  detection:
    folders:
      - ebin
      - src
  tasks:
    - label: Erlang
      command: mate .; erl
    - label: Terminal
      command:
default:
  tasks:
    - label: Terminal
      command: mate .
__END_PROFILES__

class DevLauncher
  @@terminal = app('Terminal')
  @@profiles = YAML::load(PROFILES)
  @@default_profile = @@profiles.delete('default')
  
  def initialize(project_name)
    @window = nil
    
    needs_pwd = true
    matches = find_project(project_name)
    if matches.empty?
      matches = find_project(project_name, File.expand_path(DEV_ROOT))
      needs_pwd = false
    end
    
    case matches.length
    when 0
      puts "Error:  nothing found matching #{project_name}"
      exit
    when 1
      @project_folder = needs_pwd ? File.join(Dir.pwd, matches.first) : matches.first
    else
      # TODO : pick one?
      puts "Too many matches:\n\t#{matches.join("\n\t")}"
      exit
    end
    
    detect_project_type
  end
  
  def find_project(name_fragment, root_folder = nil)
    matches = []
    Find.find(root_folder ||= Dir.pwd) do |path|
      # limit to MAX_DEPTH
      partial_path = path.gsub(root_folder, '')
      Find.prune if partial_path =~ /^\./
      depth = partial_path.split('/').length
      Find.prune if depth > MAX_DEPTH + 1
      
      # look for directory matching name fragment
      if FileTest.directory?(path)
        if path =~ /#{name_fragment}/i
          matches << path
          Find.prune
        end
      end
      
      next
    end
    matches
  end
  
  def detect_project_type
    folders, files = Dir["#{@project_folder}/*"].map{|f| File.basename(f) }.partition{|f| FileTest.directory?("#{@project_folder}/#{f}") }
    @profile = @@profiles.find do |name,config|
      if name == "default"
        true
      else
        case config['detection']
        when nil, "true"
          true
        when Hash
          ((! config['detection'].has_key? 'folders') ||
             config['detection']['folders'].all? {|f| folders.include?(f) }) &&
          ((! config['detection'].has_key? 'files') ||
             config['detection']['files'].all? {|f| files.include?(f) })
        end
      end
    end
    
    if @profile
      @profile = @profile[1]
    else
      @profile = @@default_profile
    end
    
    unless @profile
      puts "Error: could not find a matching development profile"
      exit
    end
    
    puts "using profile: #{@profile.to_yaml}"
  end
  
  def run_profile
    @profile['tasks'].each do |tab|
      run tab
    end
  end
  
  def run(action)
    @window && new_tab(action) || new_window(action)
  end
  
  def new_window(action)
    @first_tab = @@terminal.do_script(build_project_command(action))
    @window = eval @first_tab.to_s.gsub(/\.tabs.*$/, '') # nasty hack.  is there a legit way to find my mommy?
  end

  def new_tab(action)
    @window.frontmost.set(true)
    app("System Events").application_processes["Terminal.app"].keystroke("t", :using => :command_down)
    while (@window.tabs.last.busy.get)
      # we're not on the new tab yet.  wait a bit...
      sleep 1
    end
    @@terminal.do_script(build_project_command(action), :in => @window.tabs.last.get)
  end
  
  def build_project_command(action)
    ["cd #{@project_folder}",
     "echo -n -e \"\\033]0;#{action['label']}\\007\"",
     "clear",
     action['command'] || 'clear'].join(';')
  end
end

if __FILE__ == $0
  project_name = ARGV.shift
  d = DevLauncher.new(project_name)
  d.run_profile
end