define([

], function () {
    var Wire = {};

    /*  MISSION: write a generic RPC framework

Requirements

* [x] some transmission methods can be interrupted
  * [x] handle disconnects and reconnects
* [x] handle callbacks
* [x] configurable timeout
* [x] be able to answer only queries with a particular id
* be able to implement arbitrary listeners on the service-side
  * and not call 'ready' until those listeners are ready
* identical API for:
  * iframe postMessage
  * server calls over netflux
  * postMessage to webworker
  * postMessage to sharedWorker
* on-wire protocol should actually be the same for rewriting purposes
  * q
  * guid (globally unique id)
  * txid (message id)
  * content
* be able to compose different RPCs as streams
  * intercept and rewrite capacity
  * multiplex multiple streams over one stream
  * blind redirect
  * intelligent router
    * broadcast (with ACK?)
    * message


    */

    var uid = Wire.uid = function () {
        return Number(Math.floor(Math.random () *
            Number.MAX_SAFE_INTEGER)).toString(32);
    };


/*  tracker(options)
    maintains a registry of asynchronous function calls

allows you to:
    hook each call to actually send to a remote service...
    abort any call
    trigger the pending callback with arguments
    set the state of the tracker (active/inactive)


*/
    Wire.tracker = function (opt) {
        opt = opt || {};
        var hook = opt.hook || function () {};
        var timeout = opt.timeout || 5000;
        var pending = {};
        var timeouts = {};

        var call = function (method, data, cb) {
            var id = uid();

            // if the callback is not invoked in time, time out
            timeouts[id] = setTimeout(function () {
                if (typeof(pending[id]) === 'function') {
                    cb("TIMEOUT");
                    delete pending[id];
                    return;
                }
                throw new Error('timed out without function to call');
            }, timeout);

            pending[id] = function () {
                // invoke the function with arguments...
                cb.apply(null, Array.prototype.slice.call(arguments));
                // clear its timeout
                clearTimeout(timeouts[id]);
                // remove the function from pending
                delete pending[id];
            };

            hook(id, method, data);

            return id;
        };

        var respond = function (id, err, response) {
            if (typeof(pending[id]) !== 'function') {
                throw new Error('invoked non-existent callback');
            }
            pending[id](err, response);
        };

        var abort = function (id) {
            if (pending[id]) {
                clearTimeout(timeouts[id]);
                delete pending[id];
                return true;
            }
            return false;
        };

        var t = {
            call: call,
            respond: respond,
            abort: abort,
            state: true,
        };

        t.setState = function (active) {
            t.state = Boolean(active);
        };

        return t;
    };

/*
opt = {
    timeout: 30000,
    send: function () {

    },
    receive: function () {

    },
    constructor: function (cb) {
        cb(void 0 , {
            send: function (content, cb) {

            },
            receive: function () {

            }
        });
    },
};
*/

    var parseMessage = function (raw) {
        try { return JSON.parse(raw); } catch (e) { return; }
    };

    Wire.create = function (opt, cb) {
        opt.constructor(function (e, service) {
            if (e) { return setTimeout(function () { cb(e); }); }
            var rpc = {};

            var guid = Wire.uid();
            var t = Wire.tracker({
                timeout: opt.timeout,
                hook: function (txid, q, content) {
                    service.send(JSON.stringify({
                        guid: guid,
                        q: q,
                        txid: txid,
                        content: content,
                    }));
                },
            });

            rpc.send = function (type, data, cb) {
                t.call(type, data, cb);
            };

            service.receive(function (raw) {
                var data = parseMessage(raw);
                if (typeof(data) === 'undefined') {
                    return console.error("UNHANDLED_MESSAGE", raw);
                }
                if (!data.txid) { throw new Error('NO_TXID'); }
                t.respond(data.txid, data.error, data.content);
            });

            cb(void 0, rpc);
        });
    };

    return Wire;
});