#!/usr/bin/env ruby

$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])

require 'rubygems'
require 'optparse'
require 'drb'

options = {:daemonize => true, :port => 17165}

OptionParser.new do |opts|
  opts.banner = <<-EOF
Usage: god [command] [options]

  Commands:
    start <watch or group name>
    restart <watch or group name>
    stop <watch or group name>
    monitor <watch or group name>
    unmonitor <watch or group name>
    load <file>
    log <watch name>
    status
    terminate

  Options:
EOF

  opts.on("-cCONFIG", "--config-file CONFIG", "Configuration file") do |x|
    options[:config] = x
  end
  
  opts.on("-pPORT", "--port PORT", "Communications port") do |x|
    options[:port] = x
  end
  
  opts.on("-PFILE", "--pid FILE", "Where to write the PID file") do |x|
    options[:pid] = x
  end
  
  opts.on("-lFILE", "--log FILE", "Where to write the log file") do |x|
    options[:log] = x
  end
  
  opts.on("-D", "--no-daemonize", "Don't daemonize") do
    options[:daemonize] = false
  end
  
  opts.on("-v", "--version", "Print the version number and exit") do
    options[:version] = true
  end
  
  opts.on("-V", "Print extended version and build information") do
    options[:info] = true
  end
end.parse!

if options[:version]
  require 'god'
  
  # print version
  puts "Version #{God::VERSION}"
  exit!(0)
elsif options[:info]
  require 'god'
  
  puts "Version: #{God::VERSION}"
  puts "Polls: enabled"
  puts "Events: " + God::EventHandler.event_system
  
  exit!(0)
elsif command = ARGV[0]
  require 'god'
  
  # a command was specified
  
  # connect to remote drb
  DRb.start_service
  server = DRbObject.new nil, "druby://127.0.0.1:#{options[:port]}"
  
  begin
    server.ping
  rescue DRb::DRbConnError
    puts "The server is not available (or you do not have permissions to access it)"
    exit!
  rescue => e
    puts e.message
    puts e.backtrace.join("\n")
    exit!
  end
  
  if command == 'load'
    file = ARGV[1]
    
    puts "Sending '#{command}' command"
    
    code = File.read(file)
    
    watches = server.running_load(code)
    
    # output response
    puts 'The following watches were affected:'
    watches.each do |w|
      puts '  ' + w.name
    end
    
    puts "Done"
  elsif command == 'status'
    watches = server.status
    watches.keys.sort.each do |name|
      state = watches[name][:state]
      puts "#{name}: #{state}"
    end
  elsif command == 'log'
    begin
      Signal.trap('INT') { exit!(0) }
      name = ARGV[1]
      t = Time.at(0)
      loop do
        print server.running_log(name, t)
        t = Time.now
        sleep 1
      end
    rescue God::NoSuchWatchError
      puts "No such watch"
    rescue DRb::DRbConnError
      puts "The server went away"
    rescue => e
      puts e.message
      puts e.backtrace.join("\n")
    ensure
      exit!(0)
    end
  elsif command == 'terminate'
    t = Thread.new { loop { STDOUT.print('.'); STDOUT.flush; sleep(1) } }
    if server.stop_all
      t.kill; STDOUT.puts
      puts 'Stopped all watches'
    else
      t.kill; STDOUT.puts
      puts 'Could not stop all watches within 10 seconds'
    end
    
    begin
      server.terminate
      abort 'Could not stop god'
    rescue DRb::DRbConnError
      puts 'Stopped god'
      exit!(0)
    end
  else
    # get the name of the watch/group
    name = ARGV[1]
  
    begin
      puts "Sending '#{command}' command"
      
      t = Thread.new { loop { STDOUT.print('.'); STDOUT.flush; sleep(1) } }
    
      # send command
      watches = server.control(name, command)
    
      # output response
      t.kill; STDOUT.puts
      puts 'The following watches were affected:'
      watches.each do |w|
        puts '  ' + w.name
      end
    rescue God::InvalidCommandError
      abort "Command '#{command}' is not valid. Run 'god --help' for usage"
    end
  end
  
  exit!(0)
else
  # start god
  if !options[:daemonize]
    require 'god'
    
    if options[:port]
      God.port = options[:port]
    end
        
    load File.expand_path(options[:config])
  else
    pid = fork do
      begin
        require 'god'
        
        log_file = options[:log] || "/dev/null"
        
        STDIN.reopen "/dev/null"
        STDOUT.reopen(log_file, "a")
        STDERR.reopen STDOUT
        
        puts "Starting god"
      
        unless God::EventHandler.loaded?
          puts
          puts "***********************************************************************"
          puts "*"
          puts "* Event conditions are not available for your installation of god."
          puts "* You may still use and write custom conditions using the poll system"
          puts "*"
          puts "***********************************************************************"
          puts
        end
    
        puts "Resetting file descriptors"
        
        puts "Loading config"
        
        if options[:port]
          God.port = options[:port]
        end
      
        load File.expand_path(options[:config])
        
        Signal.trap('HUP') {}
      rescue => e
        File.open('god.log', 'a') { |f| f.puts e.message + "\n" + e.backtrace }
        abort "!!! ERROR - See god.log !!!"
      end
    end
  
    if options[:pid]
      File.open(options[:pid], 'w') { |f| f.write pid }
    end
  
    ::Process.detach pid
  
    exit!(0)
  end
end