 {EventEmitter} = require 'events'
 {BufferedReadStream, BufferedLineStream} = require './buffered'
 
+# Generates a random sock path.
 tmpSock = ->
   pid  = process.pid
   rand = Math.floor Math.random() * 10000000000
   "/tmp/nack." + pid + "." + rand + ".sock"
 
+# **Process** manages a single Ruby worker process.
+#
+# A Process requires a path to a rackup config file _(config.ru)_.
+#
+#     process.createProcess("/path/to/app/config.ru");
+#
+# You can set a idle time so the process dies after a
+# specified amount of milliseconds.
+#
+#     var ms = 15 * 60 * 1000;
+#     process.createProcess("/path/to/app/config.ru", { idle: ms });
+#
+# A Process has 5 states:
+#
+#   `null`: Not started or dead
+#   `spawning`: Is booting and can't accept a connection yet
+#   `ready`: Is ready to accept a connection and handle a request
+#   `busy`: Is currently handling a request, not ready
+#   `quitting`: Was set a kill signal and is shutting down
+#
+# Anytime a process changes states, an event is fired with the new
+# state name. When the process becomes `ready`, a `ready` is fired.
+#
+# Other events:
+#
+#   Event: `error`
+#   function (exception) { }
+#   Emitted when an error occurs.
+#
+#   Event: `spawn`
+#   function (exception) { }
+#   Emitted when the process moves from `spawning` to `ready` state
+#
+#   Event: `exit`
+#   function () { }
+#   Emitted when the Process terminates.
+#
+#   Event: `idle`
+#   function () { }
+#   Emitted when the Process times out because of inactivity.
+#
 exports.Process = class Process extends EventEmitter
   constructor: (@config, options) ->
     options ?= {}
     @idle  = options.idle
+    # Set initial state to `null`
     @state = null
 
     raiseConfigError = =>
       @emit 'error', new Error "configuration \"#{@config}\" doesn't exist"
 
+    # Raise an exception unless `config` exists
     if @config?
       exists @config, (ok) =>
         raiseConfigError() if !ok
     else
       raiseConfigError()
 
+    # Push back the idle time everytime a request is handled
     @on 'busy', =>
       @deferTimeout()
 
+  # Expand path to `nackup` command
   getNackupPath: (callback) ->
     if @nackupPath?
       callback null, @nackupPath
     else
       exec 'which nackup', (error, stdout, stderr) =>
         if error
+          # Throw an exception if `nackup` isn't in the PATH
+          # Probably need to `gem install nack` or fix shitty rubygems
           callback new Error "Couldn't find `nackup` in PATH"
         else
           @nackupPath = stdout.replace /(\n|\r)+$/, ''
           callback error, @nackupPath
 
   spawn: () ->
+    # Do nothing if the process is already started
     return if @state
 
+    # Change start to `spawning` and fire an event
     @changeState 'spawning'
 
     @getNackupPath (err, nackup) =>
+      # Bubble error from getNackupPath
       return @emit 'error', err if err
 
+      # Generate a random sock path
       @sockPath = tmpSock()
+      # Spawn a Ruby server connecting to our `@sockPath`
       @child = spawn "nackup", ['--file', @sockPath, @config]
 
+      # Expose `stdout` and `stderr` on Process
       @stdout = @child.stdout
       @stderr = @child.stderr
 
+      # Listen for "ready" line on process stdout
       readyLineHandler = (line) =>
         if line.toString() is "ready"
           @stdout.removeListener 'data', readyLineHandler
           @changeState 'ready'
+
+      # Wrap stdout with a line buffer
       stdoutLines = new BufferedLineStream @stdout
       stdoutLines.on 'data', readyLineHandler
 
+      # When the child process exists, clear out state and
+      # emit `exit`
       @child.on 'exit', (code, signal) =>
         @clearTimeout()
         @state = @sockPath = @child = null


    this

  onNext: (event, listener) ->
    callback = (args...) =>
      @removeListener event, callback
      listener args...
    @on event, callback

  changeState: (state) ->
    if @state != state
      @state = state
      process.nextTick => @emit state

  onState: (state, callback) ->
    if @state == state
      callback()
    else
      @onNext state, =>
        @onState state, callback

  clearTimeout: ->
    if @_timeoutId
      clearTimeout @_timeoutId

  deferTimeout: ->
    if @idle
      @clearTimeout()

      callback = =>
        @emit 'idle'
        @quit()
      @_timeoutId = setTimeout callback, @idle

  createConnection: (callback) ->
    @spawn()

    @onState 'ready', =>
      @changeState 'busy'

      connection = client.createConnection @sockPath

      connection.on 'close', () =>
        @changeState 'ready'

      callback connection

  proxyRequest: (req, res, callback) ->
    reqBuf = new BufferedReadStream req

    @createConnection (connection) ->

        connection.on 'error', callback
        connection.on 'close', callback

      # Flush any events captured while we were establishing
      # our client connection
      reqBuf.flush()

  # Send `SIGTERM` to process.
  # This will immediately kill it.
  kill: ->
    if @child
      @changeState 'quitting'
      @child.kill 'SIGTERM'

  # Send `SIGTERM` to process.
  # The process will finish serving its request and gracefully quit.
  quit: ->
    if @child
      @changeState 'quitting'
      @child.kill 'SIGQUIT'

# Public API for creating a **Process**
exports.createProcess = (args...) ->
  new Process args...
