var utils = require('./utils');

// Event stream factory
module.exports = function factory (instance, inputStream) {

    // create stream object
    var stream = utils.clone(Stream);
    stream._ = instance;
    stream._d = [];
    stream._o = [];
    stream._b = [];
    
    if (inputStream) {
        stream._i = inputStream;
        stream._ii = inputStream._o.length;
        inputStream._o.push(stream);
    }

    return stream;
};

/**
 * Stream like events.
 *
 * @class Stream
 */
var Stream = {

    // handler for incoming data
    data: function (handler, options) {
        this._d.push([handler, options]);
        return this;
    },

    // send data
    write: function (err, data, reverse) {

        // buffer data in paused mode
        if (this._p) {
            this._b.push([err, data]);
            return this;
        }

        // call custom write handler
        if (typeof this._write === 'function') {
            data = this._write(err, data, this) || data;
        }

        // call handlers on output streams
        if (this._o.length) {
            for (var i = 0, l = this._o.length; i < l; ++i) {
                this._o[i].emit(err, data);
            }
        }
        
        // emit data on input handler (back stream)
        if (this._i) {
            this._i.emit(err, data, this._ii);
        }

        return this;
    },
    
    emit: function (err, data, ignore) {
      
        var i, l;

        // call data handlers with err and data as arguments
        for (i = 0, l = this._d.length; i < l; ++i) {

            // call data handler with stream instance as function scope
            // and overwrite data with transformed data
            data = this._d[i][0].call(this._, err, data, this, this._d[i][1]) || data;
        }

        // call handlers on output streams
        if (this._o.length) {
            for (i = 0, l = this._o.length; i < l; ++i) {
                if (ignore !== i) {
                    this._o[i].emit(err, data, this._ii);
                }
            }
        }
    },

    end: function () {

        // remove stream from input's outputs
        if (this._i) {
            this._i._o.splice(this._ii, 1);
        }

        // remove refs
        this._o = this._b = [];
        this._ = this._i = null;

        // call custom end handler
        if (typeof this._end === 'function') {
            this._end.apply(this, arguments);
        }

        return this;
    },

    // buffer writes
    pause: function () {

        // enable pause mode
        this._p = true;

        return this;
    },

    // write bufferd data
    resume: function () {

        // disable pause mode
        this._p = false;

        // write bufferd data
        if (this._b.length) {
            for (var i = 0, l = this._b.length; i < l; ++i) {
                this.write(this._b[i][0], this._b[i][1]);
            }
        }

        return this;
    }
};

@@ -558,27 +558,89 @@
 on (
-handler, options) %7B
+method) %7B%0A        var args = %5B%5D.slice.call(arguments);%0A        args.splice(0, 1);
 %0A   
@@ -662,23 +662,19 @@
 sh(%5B
-handler, option
+method, arg
 s%5D);
@@ -1496,18 +1496,32 @@
 var i, l
+, method, args
 ;%0A
-
 %0A       
@@ -1624,25 +1624,37 @@
 %3C l; ++i) %7B%0A
+            
 %0A
-
             
@@ -1802,20 +1802,21 @@
 d%5Bi%5D%5B0%5D.
-call
+apply
 (this._,
@@ -1816,16 +1816,17 @@
 this._, 
+%5B
 err, dat
@@ -1832,18 +1832,25 @@
 ta, this
-, 
+%5D.concat(
 this._d%5B
@@ -1855,16 +1855,17 @@
 d%5Bi%5D%5B1%5D)
+)
  %7C%7C data

extend data handler arguments with err, data and stream