require 'pty'

class Agent
  def initialize(options={})
    @directory = options[:directory]
    @command = options[:command]
    @compile = options[:compile]
  end

  def cd
    "cd #{@directory}"
  end

  def compile!
    return unless @compile
    command = "#{cd} && #{@compile}"
    puts "compiling: #{command}"
    puts `#{command}`
    raise "Compile error" unless $?.to_i == 0
  end

  def command
    "#{cd} && #{@command}"
  end
end

desc "run ruby random agents"
task :agents, :number, :langs, :game, :sleep do |t, args|
  args.with_defaults number: 4, sleep: 0.1, langs: "ruby:coffee", game: "hearts"

  agent_commands = {
    "hearts" => {
      "ruby" => Agent.new(directory: 'dist/hearts/ruby', command: 'ruby my_agent.rb'),
      "coffee" => Agent.new(directory: 'dist/hearts/nodejs', command: 'coffee myAgent.coffee'),
      "haskell" => Agent.new(directory: 'dist/hearts/haskell', compile: 'cabal configure && cabal build', command: 'dist/build/myAgent/myAgent'),
      "go" => Agent.new(directory: 'dist/hearts/go', compile: 'make', command: 'bin/my_agent')
    },
    "ttt" => {
      "ruby" => Agent.new(directory: 'dist/tic_tac_toe/ruby', command: 'ruby my_agent.rb'),
    }
  }

  agents = agent_commands[args.game].values_at(*args.langs.split(":")).shuffle

  agents.each(&:compile!)

  commands = agents.map(&:command).cycle

  pids = []
  args.number.to_i.times do |i|
    command = commands.next
    pids << fork do
      STDOUT.sync = true

      begin
        PTY.spawn(command) do |stdin, stdout, pid|
          begin
            stdin.each { |line| puts "[#{i}] #{line}" }
          rescue Errno::EIO
            puts "Errno:EIO error, but this probably just means that the process has finished giving output"
          end
        end
      rescue PTY::ChildExited
        puts "The child process exited!"
      end
    end
    sleep args.sleep.to_f
  end

  pids.each{|pid| Process.wait(pid)}
end
