class Foreman::Engine

def self.load_env!(env_file)

def self.load_env!(env_file)
  @environment = read_environment_files(env_file)
  apply_environment!
end

def assign_colors

def assign_colors
  procfile.entries.each do |entry|
    colors[entry.name] = next_color
  end
end

def base_port

def base_port
  options[:port] || 5000
end

def colors

def colors
  @colors ||= {}
end

def error(message)

def error(message)
  puts "ERROR: #{message}"
  exit 1
end

def info(message, name="system", color=Term::ANSIColor.white)

def info(message, name="system", color=Term::ANSIColor.white)
  print color
  print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
  print Term::ANSIColor.reset
  print message.chomp
  puts
end

def initialize(procfile, options={})

def initialize(procfile, options={})
  @procfile  = Foreman::Procfile.new(procfile)
  @directory = File.expand_path(File.dirname(procfile))
  @options = options
  @environment = read_environment_files(options[:env])
  @output_mutex = Mutex.new
end

def kill_all(signal="SIGTERM")

def kill_all(signal="SIGTERM")
  running_processes.each do |pid, process|
    Process.kill(signal, pid) rescue Errno::ESRCH
  end
end

def longest_process_name

def longest_process_name
  @longest_process_name ||= begin
    longest = procfile.process_names.map { |name| name.length }.sort.last
    longest = 6 if longest < 6 # system
    longest
  end
end

def next_color

def next_color
  @current_color ||= -1
  @current_color  +=  1
  @current_color >= COLORS.length ? "" : COLORS[@current_color]
end

def pad_process_name(name="system")

def pad_process_name(name="system")
  name.to_s.ljust(longest_process_name + 3) # add 3 for process number padding
end

def port_for(process, num, base_port=nil)

def port_for(process, num, base_port=nil)
  base_port ||= 5000
  offset = procfile.process_names.index(process.name) * 100
  base_port.to_i + offset + num - 1
end

def print(message=nil)

def print(message=nil)
  @output_mutex.synchronize do
    $stdout.print message
  end
end

def process_by_reader(reader)

def process_by_reader(reader)
  readers.invert[reader]
end

def proctitle(title)

def proctitle(title)
  $0 = title
end

def puts(message=nil)

def puts(message=nil)
  @output_mutex.synchronize do
    $stdout.puts message
  end
end

def readers

def readers
  @readers ||= {}
end

def running_processes

def running_processes
  @running_processes ||= {}
end

def spawn_processes

def spawn_processes
  concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
  procfile.entries.each do |entry|
    reader, writer = IO.pipe
    entry.spawn(concurrency[entry.name], writer, @directory, @environment, base_port).each do |process|
      running_processes[process.pid] = process
      readers[process] = reader
    end
  end
end

def start

def start
  proctitle "ruby: foreman master"
  termtitle "#{File.basename(@directory)} - foreman"
  trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
  trap("INT")  { puts "SIGINT received";  terminate_gracefully }
  assign_colors
  spawn_processes
  watch_for_output
  watch_for_termination
end

def terminate_gracefully

def terminate_gracefully
  info "sending SIGTERM to all processes"
  kill_all "SIGTERM"
  Timeout.timeout(5) { Process.waitall }
rescue Timeout::Error
  info "sending SIGKILL to all processes"
  kill_all "SIGKILL"
end

def termtitle(title)

def termtitle(title)
  printf("\033]0;#{title}\007")
end

def watch_for_output

def watch_for_output
  Thread.new do
    begin
      loop do
        rs, ws = IO.select(readers.values, [], [], 1)
        (rs || []).each do |r|
          ps, message = r.gets.split(",", 2)
          color = colors[ps.split(".").first]
          info message, ps, color
        end
      end
    rescue Exception => ex
      puts ex.message
      puts ex.backtrace
    end
  end
end

def watch_for_termination

def watch_for_termination
  pid, status = Process.wait2
  process = running_processes.delete(pid)
  info "process terminated", process.name
  terminate_gracefully
  kill_all
rescue Errno::ECHILD
end