#--
#
# Author:: Francis Cianfrocca (gmail: blackhedd)
# Homepage::  http://rubyeventmachine.com
# Date:: 13 Dec 07
# 
# See EventMachine and EventMachine::Connection for documentation and
# usage examples.
#
#----------------------------------------------------------------------------
#
# Copyright (C) 2006-08 by Francis Cianfrocca. All Rights Reserved.
# Gmail: blackhedd
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of either: 1) the GNU General Public License
# as published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version; or 2) Ruby's License.
# 
# See the file COPYING for complete licensing information.
#
#---------------------------------------------------------------------------
#
# 


module EventMachine

  # EM::DeferrableChildProcess is a sugaring of a common use-case
  # involving EM::popen.
  # Call the #open method on EM::DeferrableChildProcess, passing
  # a command-string. #open immediately returns an EM::Deferrable
  # object. It also schedules the forking of a child process, which
  # will execute the command passed to #open.
  # When the forked child terminates, the Deferrable will be signalled
  # and execute its callbacks, passing the data that the child process
  # wrote to stdout.
  #
  class DeferrableChildProcess < EventMachine::Connection
    include EventMachine::Deferrable

    # @private
    def initialize
      super
      @data = []
    end

    # Sugars a common use-case involving forked child processes.
    # #open takes a String argument containing an shell command
    # string (including arguments if desired). #open immediately
    # returns an EventMachine::Deferrable object, without blocking.
    #
    # It also invokes EventMachine#popen to run the passed-in
    # command in a forked child process.
    #
    # When the forked child terminates, the Deferrable that
    # #open calls its callbacks, passing the data returned
    # from the child process.
    #
    def self.open cmd
      EventMachine.popen( cmd, DeferrableChildProcess )
    end

    # @private
    def receive_data data
      @data << data
    end

    # @private
    def unbind
      succeed( @data.join )
    end
  end

  # @private
  class SystemCmd < EventMachine::Connection
    def initialize cb
      @cb = cb
      @output = []
    end
    def receive_data data
      @output << data
    end
    def unbind
      @cb.call @output.join(''), get_status if @cb
    end
  end

  # EM::system is a simple wrapper for EM::popen. It is similar to Kernel::system, but requires a
  # single string argument for the command and performs no shell expansion.
  #
  # The block or proc passed to EM::system is called with two arguments: the output generated by the command,
  # and a Process::Status that contains information about the command's execution.
  #
  #  EM.run{
  #    EM.system('ls'){ |output,status| puts output if status.exitstatus == 0 }
  #  }
  #
  # You can also supply an additional proc to send some data to the process:
  #
  #  EM.run{
  #    EM.system('sh', proc{ |process|
  #      process.send_data("echo hello\n")
  #      process.send_data("exit\n")
  #    }, proc{ |out,status|
  #      puts(out)
  #    })
  #  }
  #
  # Like EventMachine.popen, EventMachine.system currently does not work on windows.
  # It returns the pid of the spawned process.
  def EventMachine::system cmd, *args, &cb
    cb ||= args.pop if args.last.is_a? Proc
    init = args.pop if args.last.is_a? Proc

    # merge remaining arguments into the command
    cmd = ([cmd] + args.map{|a|a.to_s.dump}).join(' ')

    EM.get_subprocess_pid(EM.popen(cmd, SystemCmd, cb) do |c|
      init[c] if init
    end.signature)
  end
end
