
#
# Implements EventEmitter.{on,listeners,emit}
#
# Events:
#   'send', e
#   'receive', e   # Called after all of e's callbacks and listeners have been invoked.
#   ...and whatever messages you subscribe to.
#
class ABBrowserComm
  constructor: (opt={}) ->
    @name = opt.name or "js"
    @nextId = 1
    @outbox = []
    @callbacks = {}
    
    if window.appborg
      throw new Error "You may create at most one ABBrowserComm instance"
    window.appborg = @
  
  send: (method, args...) ->
    
    # args
    callback = null
    dicts = []
    for x in args
      if (typeof x) == 'function'
        callback = x
      else
        dicts.push x
    info = {}
    opt = {}
    if dicts.length == 1
      [info] = dicts
    else if dicts.length == 2
      [opt, info] = dicts
    
    id = @nextId
    @nextId++
    
    if callback
      @callbacks[id] = callback
    
    e = {
      id: id
      from: @name
      to: opt.to or "subprocess"
      method: method
      info: info
    }
    @outbox.push e
    @emit 'send', e
  
  pushAndPullEvents: (events) ->
    @pushEvents events
    json = JSON.stringify @outbox
    @outbox = []
    json
  
  pushEvents: (events) ->
    for e in events
      
      # response?
      if (e.result or e.error) and e.id
        callback = @callbacks[e.id]
        if callback
          try
            callback (e.error or null), (e.result or {})
          catch exc
            1#TODO
        delete @callbacks[e.id]
      
      # request?
      else if e.method
        for f in @listeners(e.method)
          
          # auto response
          if f.length < 2
            try
              f e.info
              @_respond e, null, {}
            catch exc
              error = {message:exc.toString()}
              if window.printStackTrace
                error.stacktrace = printStackTrace e:exc
              @_respond e, error, null
          
          # manual response
          else
            f e.info, (error=null, result={}) =>
              @_respond e, error, result
      
      @emit 'receive', e
  
  _respond: (e, error, result) ->
    response = {
      id: e.id
      from: @name
      to: e.from
    }
    if error
      response.error = error
      response.result = null
    else
      response.error = null
      response.result = result
    @outbox.push response
  
  #### All the EventEmitter code:
  
  _initEventEmitter: () ->
    if not @EventEmitter_listeners
      @EventEmitter_listeners = {}
  
  on: (k, f) ->
    @_initEventEmitter()
    arr = @EventEmitter_listeners[k] = (@EventEmitter_listeners[k] or [])
    arr.push f
  
  listeners: (k) ->
    @_initEventEmitter()
    if not @EventEmitter_listeners[k]
      @EventEmitter_listeners[k] = []
    @EventEmitter_listeners[k]
  
  emit:(k, args...) ->
    @_initEventEmitter()
    arr = @EventEmitter_listeners[k] = (@EventEmitter_listeners[k] or [])
    for f in arr
      f args...


module.exports =
  ABBrowserComm: ABBrowserComm
