// Stackless continuation management.
// When writing a continuation-passing style function, where you would
// normally invoke the continuation via something like "k(arg);", you
// should instead invoke it using this framework like "K.call(k, arg);"
//
// Also, we will do the right thing if either k or arg happens to be null.
//
// Author: Rick Owens
// Copyright 2008 Mezeo Software
//

var K = {
    continuations: [],
    history: [],
    debug: false,

    call: function(k, arg) {
        if (k) {
            if (this.debug) {
                try {
                    k(arg);
                } catch(err) {
                    this.logException(err);
                }
            } else {
                this.continuations.push([k, arg]);
            }
        }
    },

    a_forEach: function(items, action, k) {
        var loop = function(items) {
            if(items.length <= 0) return K.call(finish);
            action(items[0], function() {
                K.call(loop, items.slice(1));
            }.bind(this));
        }.bind(this);
        var finish = function() {
            K.call(k);
        }.bind(this);
        K.call(loop, items);
    },
    
    a_forEachKey: function(map, action, k) {
        var keys = [];
        for (i in map) {
            keys.push(i);
        }
        this.a_forEach(keys, action, k);
    },

    runContinuations: function() {
        this.history = [];
        while (this.continuations.length > 0) {
            var k = this.continuations.pop();
            // make the function call, with the argument
            // thank goodness Javascript supports closures.
            try {
                if (this.history.length > 10) {
                    this.history = this.history.slice(1);
                }
                this.history.push(k.toString());
            } catch (err) {
                // security manager vetoed action??  really?
            }
            k[0](k[1]);
        }
    },

    checkContinuations: function() {
        // execute the continuations untill it's empty.
        try {
            this.runContinuations();
        } catch (err) {
            this.logException(err);
        }
        // set up a timer to check and see if anything has been added
        window.setTimeout(this.checkContinuations.bind(this), 50);
    },

    logException: function(err) {
        if(typeof console != 'undefined') {
            console.log('Uncaught Exception');
            // sometimes, err is not really any sort of proper exception, so try to
            // print it directly
            console.log(err);
            console.log(err.fileName + ':' + err.lineNumber + ': ' + err.message);
            console.log(err.stack);
            console.log('Trace:');
            for(var i = 0; i < this.history.length; i++) {
                console.log(this.history[i]);
            }
        }
    }
};

K.checkContinuations();




