#!/usr/bin/env ruby
#
# list_running_app_ids
#

###
### dependencies
###

require 'open3'
require 'set'

###
### globals
###

$opt_test = nil

###
### methods
###

def check_ruby
  if RUBY_VERSION.to_f < 1.9
    print "You are currently using Ruby ", RUBY_VERSION, ", but version 1.9 or above is required."
    exit 1
  end
end

def usage
  <<-EOS
list_running_app_ids [ -t <bundle-id> ]

Print a list of currently running Applications and associated
Bundle IDs, which may be useful in a Cask uninstall stanza, eg

    uninstall :quit => 'bundle.id.goes.here'

Applications attributed to Apple are excluded from the output.

With optional "-t <bundle-id>", silently test if a given app
is running, exiting with an error code if not.

See CONTRIBUTING.md for more information.

EOS
end

def process_args
  while ! ARGV.empty? do
    if ARGV.first =~ /^-+t(?:est)?$/ and ARGV.length > 1
      ARGV.shift
      $opt_test = ARGV.shift
    elsif ARGV.first =~ /^-+h(?:elp)?$/
      puts usage
      exit 0
    else
      puts usage
      exit 1
    end
  end
end

def load_apps
  out, err, status = Open3.capture3('/usr/bin/osascript', '-e', 'tell application "System Events" to get (name, bundle identifier, unix id) of every process')
  if status.exitstatus > 0
    puts err
    exit status.exitstatus
  end
  out = out.split(', ')
  one_third   = out.length / 3
  @app_names  = out.shift(one_third)
  @bundle_ids = out.shift(one_third)
  @unix_ids   = out.shift(one_third)
end

def test_app(bundle)
  @bundle_ids.include?(bundle) ? 0 : 1
end

def excluded_bundle_id(bundle_id)
  %r{^com\.apple\.}.match(bundle_id)
end

def excluded_app_name(app_name)
  %r{^osascript$}.match(app_name)   # this script itself
end

def report_apps
  running = Set.new
  @app_names.zip(@bundle_ids, @unix_ids).each do |app_name, bundle_id, unix_id|
    next if excluded_bundle_id bundle_id
    next if excluded_app_name app_name
    bundle_id.gsub!(/^(missing value)$/, '<\1>')
    running.add "#{bundle_id}\t#{app_name}"
  end

  puts "bundle_id\tapp_name\n"
  puts "--------------------------------------\n"
  puts running.to_a.sort
end

###
### main
###

check_ruby
process_args
load_apps

if $opt_test
  exit test_app($opt_test)
else
  report_apps
end
