
{EventEmitter} = require 'events'
LineStream = require 'linestream'
{encodeLine, decodeLine} = require './appborg_util'


DEFAULT_US    = "subprocess"
DEFAULT_THEM  = "js"


class ABComm extends EventEmitter
  constructor: (opt={}) ->
    super()
    @output = opt.output or process.stdout
    if opt.input
      @input = opt.input
    else
      process.stdin.resume()
      @input = process.stdin
    @name = opt.name or DEFAULT_US
    @nextId = 1
    @callbacks = {}
    ls = new LineStream @input
    ls.on 'data', (line) =>
      @pushEvents [decodeLine line]
    
    ls.on 'end', (line)   => process.exit 0
    ls.on 'error', (line) => process.exit 1
  
  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
    
    @_send {
      id: id
      from: @name
      to: opt.to or DEFAULT_THEM
      method: method
      info: info
    }
  
  _send: (event) ->
    @output.write encodeLine event
  
  _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
    @_send response
  
  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()}
              # error.stacktrace = TODO
              @_respond e, error, null
          
          # manual response
          else
            f e.info, (error=null, result={}) =>
              @_respond e, error, result


module.exports =
  ABComm: ABComm
