var createDefaultStream = require('./lib/default_stream');
var Render = require('./lib/render');
var Test = require('./lib/test');

exports = module.exports = createHarness();
exports.createHarness = createHarness;
exports.Test = Test;

var canEmitExit = typeof process !== 'undefined' && process
    && typeof process.on === 'function'
;
var canExit = typeof process !== 'undefined' && process
    && typeof process.exit === 'function'
;
var onexit = (function () {
    var stack = [];
    if (canEmitExit) process.on('exit', function (code) {
        for (var i = 0; i < stack.length; i++) stack[i](code);
    });
    return function (cb) { stack.push(cb) };
})();

function createHarness (conf_) {
    var pending = [];
    var running = false;
    var count = 0;
    
    var began = false;
    var only = false;
    var closed = false;
    var out = new Render();
    
    var test = function (name, conf, cb) {
        count++;
        var t = new Test(name, conf, cb);
        if (!conf || typeof conf !== 'object') conf = conf_ || {};
        
        if (conf.exit !== false) {
            onexit(function (code) {
                t._exit();
                if (!closed) {
                    closed = true
                    out.close();
                }
                if (!code && !t._ok && (!only || name === only)) {
                    process.exit(1);
                }
            });
        }
        
        process.nextTick(function () {
            if (!out.piped) out.pipe(createDefaultStream());
            if (!began) out.begin();
            began = true;
            
            var run = function () {
                running = true;
                out.push(t);
                t.run();
            };
            
            if (only && name !== only) {
                count--;
                return;
            }

            if (running || pending.length) {
                pending.push(run);
            }
            else run();
        });
        
        t.on('test', function sub (st) {
            count++;
            st.on('test', sub);
            st.on('end', onend);
        });
        
        t.on('end', onend);
        
        return t;
        
        function onend () {
            count--;
            if (this._progeny.length) {
                var unshifts = this._progeny.map(function (st) {
                    return function () {
                        running = true;
                        out.push(st);
                        st.run();
                    };
                });
                pending.unshift.apply(pending, unshifts);
            }
            
            process.nextTick(function () {
                running = false;
                if (pending.length) return pending.shift()();
                if (count === 0 && !closed) {
                    closed = true
                    out.close();
                }
                if (conf.exit !== false && canExit && !t._ok) {
                    process.exit(1);
                }
            });
        }
    };
    
    test.only = function (name) {
        if (only) {
            throw new Error("there can only be one only test");
        }
        
        only = name;
        
        return test.apply(null, arguments);
    };
    
    test.stream = out;
    return test;
}

// vim: set softtabstop=4 shiftwidth=4:

@@ -129,113 +129,8 @@
 );%0A%0A
-exports = module.exports = createHarness();%0Aexports.createHarness = createHarness;%0Aexports.Test = Test;%0A%0A
 var 
@@ -556,16 +556,121 @@
 %0A%7D)();%0A%0A
+exports = module.exports = createHarness();%0Aexports.createHarness = createHarness;%0Aexports.Test = Test;%0A%0A
 function
@@ -858,24 +858,553 @@
 w Render();%0A
+    if (!conf_) conf_ = %7B%7D;%0A    %0A    var tests = %5B%5D;%0A    var exitInterval = conf_.exitInterval !== false && canEmitExit%0A    && typeof process._getActiveHandles === 'function'%0A    && setInterval(function () %7B%0A        if (process._getActiveHandles().length === 1) %7B%0A            tests.forEach(function (t) %7B t._exit() %7D);%0A        %7D%0A    %7D, 200);%0A    %0A    var exitCode = 0;%0A    var exit = function (c) %7B exitCode = c %7D;%0A    %0A    out.on('end', function () %7B%0A        clearInterval(exitInterval);%0A        process.exit(exitCode);%0A    %7D);%0A
     %0A    var
@@ -1494,24 +1494,47 @@
  conf, cb);%0A
+        tests.push(t);%0A
         if (
@@ -1584,14 +1584,8 @@
 onf_
- %7C%7C %7B%7D
 ;%0A  
@@ -1889,32 +1889,24 @@
             
-process.
 exit(1);%0A   
@@ -3501,24 +3501,24 @@
 & !t._ok) %7B%0A
+
             
@@ -3521,32 +3521,24 @@
             
-process.
 exit(1);%0A   

use a setInterval to keep the event loop alive