#!/usr/bin/env ruby
# frozen_string_literal: true

# rubocop:disable Style/GlobalVars, Style/TopLevelMethodDefinition

#
# list_running_app_ids
#

###
### dependencies
###

require "open3"
require "set"
require "io/console"

###
### globals
###

$opt_test = nil
$COLUMNS = IO.console.winsize.last

###
### methods
###

def check_ruby
  return if RUBY_VERSION.to_f >= 2.0

  print "You are currently using Ruby ", RUBY_VERSION, ", but version 2.0 or above is required."
  exit 1
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
  until ARGV.empty?
    if ARGV.first =~ /^-+t(?:est)?$/ && ARGV.length > 1
      ARGV.shift
      $opt_test = ARGV.shift
    elsif /^-+h(?:elp)?$/.match?(ARGV.first)
      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.positive?
    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)
  /^com\.apple\./.match(bundle_id)
end

def excluded_app_name(app_name)
  /^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.ljust($COLUMNS / 2)}\t#{app_name}"
  end

  puts "#{"bundle_id".ljust($COLUMNS / 2)}\tapp_name"
  puts "-" * $COLUMNS
  puts running.to_a.sort.join "\n"
end

###
### main
###

check_ruby
process_args
load_apps

if $opt_test
  exit test_app($opt_test)
else
  report_apps
end
# rubocop:enable Style/GlobalVars, Style/TopLevelMethodDefinition
