+async = require 'async'
+
 class Kevas extends (require 'stream').Transform
   constructor: (options) ->
     super options
     @_parse = opening
+    starter = (next) -> next undefined, key:starter.key, values:[]
+    @_keyListeners = [starter]
     if options?.values?
       values = options.values
-      @on 'key', (key, push) ->
-        push values[key] if values[key]?
+      @on 'key', (context) ->
+        context.values.push values[context.key] if values[context.key]?
 
-  _transform: (string, encoding, next) ->
+  _transform: (string, encoding, done) ->
     string = string.toString 'utf8' # buffer becomes string, string returns itself
+    @_parser string, done
+
+  _parser: (string, next) ->
     parse = @_parse
-    parse = parse(this, string) while parse?
-    next()
+    parse = parse(this, string, next) while parse?
 
   _flush: (done) -> #if @_parse is closing # TODO: log warning about an unclosed tag?
     @push keyOf this if this.__key?.length > 0
     done()
 
-opening = (stream, string) ->
+  on: (event, listener) ->
+    if event is 'key'
+      if listener.length < 2 # then there's no callback arg. so, we want to call it for them
+        listener = do(fn = listener) -> (context, next) ->
+          try
+            fn context
+            next undefined, context
+          catch error
+            next error
+      @_keyListeners ?= []
+      @_keyListeners.push listener
+    else super event, listener
+
+  _emitKey: (key, done) ->
+    # TODO: slice() array to make a copy?
+    @_keyListeners[0].key = key
+    async.waterfall @_keyListeners, (error, result) =>
+      if error? then console.error 'key listeners error: ',error.message
+      else
+        @push value for value in result.values
+        process.nextTick done
+
+
+opening = (stream, string, done) ->
   stream._parse = opening
   start = 0
   for ch,i in string

        start = i
        if i is string.length - 1
          stream._parse = closingDelim
          return
        else if string[i+1] is '}'
          key = keyOf stream
          stream.emit 'key', key, stream.push.bind(stream)
          return -> opening stream, string[i+2...]
      else # the opening brace has been escaped, so, treat it like a regular char
        delete stream.__slashIndex #start++ # skip the slash tho because it was *used* to escape the brace

  if start < string.length then keyOf stream, string[start..]
  return

openingDelim = (stream, string) -> delim stream, string, opening, '{', closing
closingDelim = (stream, string) -> delim stream, string, closing, '}', opening, (stream) ->
  key = keyOf stream
  stream.emit 'key', key, stream.push.bind(stream)

delim = (stream, string, continuing, char, switching, doKey) ->
  # we're starting with a delimeter from previous chunk
  if string[0] is char
    doKey? stream
    return -> switching stream, string[1..]
  else
    if doKey? then keyOf stream, char else stream.push char
    return -> continuing stream, string

keyOf = (stream, key) ->
  if key? then stream.__key = (stream.__key ? '') + key
  else
    key = stream.__key
    delete stream.__key
    return key


#  2. kvstream = require('kvstream')
#     stream = kvstream(options)
#  3. stream = require('kvstream') (options)
module.exports = (options) -> new Kevas options
module.exports.Kevas = Kevas
