var Flow = require('./flow');
var utils = require('./utils');
var isPublicPath = new RegExp("^\/[^/]");
var cbBuffer = {};
var instances = {};

// call callbacks
function callbacks (err, instance, cbs) {
    cbs.forEach(function (callback) {
        callback(err, instance);
    });
}

//reset caches
exports.reset = function () {
    instances = {};
    cbBuffer = {};
};

exports.instances = instances;

/**
 * Create a new module instance.
 *
 * @public
 * @param {object} The CommonJS module.
 * @param {object} The composition config.
 * @param {function} Callback handler.
 */
exports.factory = function (name, session,  callback) {

    session = session || {};
    callback = callback || function (err) {err && console.error(err)};

    // handle entrypoint
    if (!name) {
        var host = '';
        var entrypoints = this.config.entrypoints[session.role ? 'private' : 'public'];
        name = entrypoints['*'] || entrypoints[host]; 
    }

    if (!name) {
        return callback(new Error('Instance.factory: No name.'));
    }

    // check instance cache
    if (instances[name]) {

        if (!utils.access(instances[name], session)) {
            return callback(console.log('E', new Error('Access denied for instance:' + name)));
        }

        // buffer callback
        if (instances[name] === 1 || !instances[name]._ready) {
            return cbBuffer[name].push(callback);
        }

        return callback(null, instances[name]);
    }

    // save callback and mark instance as loading
    cbBuffer[name] = [callback];
    instances[name] = 1;

    var flow = this;

    // get composition
    flow.composition(name, function (err, composition) {

        if (err) {
            return callback(err);
        }

        if (!composition || !composition.module) {
            return callback(new Error('Invalid composition'));
        }

        if (!utils.access(composition, session)) {
            return callback(new Error('Access denied for instance:' + composition.name));
        }

        // load styles
        if (flow.styles && composition.styles) {
            flow.styles(composition.styles);
        }

        // pre load instances
        if (composition.load) {
            composition.load.forEach(function (iName) {
                flow.load(iName);
            });
        }

        var count = 1;
        var module;
        var readHandler = function (err, cjsMod) {

            module = module || cjsMod;

            if (--count === 0) {
                build.call(flow, module, composition, callback);
            }
        };

        // load markup
        if (flow.markup && composition.markup) {
            ++count;
            flow.markup(composition.markup, readHandler);
        }

        // get CommonJS module
        flow.module(composition.module, readHandler);
    });
};

function build (module, config, callback) {

    // create new flow emitter
    var flow = this;
    var instance = flow.factory(module);

    // extend instance
    instance._module = config.module;
    instance._config = config.config || {};
    instance._name = config.name;
    instance._roles = config.roles || {};
    instance._flow = {};
    instance._markups = {};
    instance._events = {};

    // setup flow
    if (config.flow) {
        config.flow.forEach(function (flow) {
            if (flow.constructor === Array) {

                // create event streams cache
                if (!instance._flow[flow[0]]) {
                    instance._flow[flow[0]] = [];
                }

                // push flow stream config to stream cache
                instance._flow[flow[0]].push(flow.slice(1));
            }
        });
    }

    // init loader instance
    if (instance.init) {
        instance.init();
    }

    // mark instance as ready
    instance._ready = true;

    // save module instance in cache
    instances[instance._name] = instance;
    callbacks(null, instance, cbBuffer[instance._name]);
}

@@ -641,408 +641,312 @@
     
-session = session %7C%7C %7B%7D;%0A    callback = callback %7C%7C function (err) %7Berr && console.error(err)%7D;%0A%0A    // handle entrypoint%0A    if (!name) %7B
+if (!name) %7B%0A        return callback(new Error('Instance.factory: No name.'));
 %0A    
+%7D%0A%0A
     
-var host = '';%0A        var entrypoints = this.config.entrypoints%5Bsession.role ? 'private' : 'public'%5D;%0A        name = entrypoints%5B'*'%5D %7C%7C entrypoints%5Bhost%5D; %0A    %7D%0A%0A    if (!name) %7B%0A        return callback(new Error('Instance.factory: No name.'));%0A    %7D
+process.nextTick(_factory.bind(this), name, session, callback);%0A%7D;%0A%0Afunction _factory (name, session, callback) %7B%0A%0A    session = session %7C%7C %7B%7D;%0A    callback = callback %7C%7C function (err) %7Berr && console.error(err)%7D;
 %0A%0A  
@@ -971,32 +971,46 @@
 e cache%0A    if (
+name !== '*', 
 instances%5Bname%5D)
@@ -2464,32 +2464,48 @@
 sition, callback
+, (name === '*')
 );%0A            %7D
@@ -2808,32 +2808,46 @@
 config, callback
+, isEntrypoint
 ) %7B%0A%0A    // crea
@@ -3864,16 +3864,83 @@
 stance;%0A
+%0A    if (isEntrypoint) %7B%0A        instances%5B'*'%5D = instance;%0A    %7D%0A%0A
     call
@@ -3970,16 +3970,37 @@
 bBuffer%5B
+isEntrypoint ? '*' : 
 instance

make factory asyn and use "*" to load an entrypoint