function Handler(action, context) {
    this.action = action;
    this.context = context;
}
/**
 * @mixin
 * @alias clay.core.mixin.notifier
 */
var notifier = {
    /**
     * Trigger event
     * @param  {string} name
     */
    trigger: function(name) {
        if (!this.hasOwnProperty('__handlers__')) {
            return;
        }
        if (!this.__handlers__.hasOwnProperty(name)) {
            return;
        }

        var hdls = this.__handlers__[name];
        var l = hdls.length, i = -1, args = arguments;
        // Optimize advise from backbone
        switch (args.length) {
            case 1:
                while (++i < l) {
                    hdls[i].action.call(hdls[i].context);
                }
                return;
            case 2:
                while (++i < l) {
                    hdls[i].action.call(hdls[i].context, args[1]);
                }
                return;
            case 3:
                while (++i < l) {
                    hdls[i].action.call(hdls[i].context, args[1], args[2]);
                }
                return;
            case 4:
                while (++i < l) {
                    hdls[i].action.call(hdls[i].context, args[1], args[2], args[3]);
                }
                return;
            case 5:
                while (++i < l) {
                    hdls[i].action.call(hdls[i].context, args[1], args[2], args[3], args[4]);
                }
                return;
            default:
                while (++i < l) {
                    hdls[i].action.apply(hdls[i].context, Array.prototype.slice.call(args, 1));
                }
                return;
        }
    },
    /**
     * Register event handler
     * @param  {string} name
     * @param  {Function} action
     * @param  {Object} [context]
     * @chainable
     */
    on: function(name, action, context) {
        if (!name || !action) {
            return;
        }
        var handlers = this.__handlers__ || (this.__handlers__={});
        if (!handlers[name]) {
            handlers[name] = [];
        }
        else {
            if (this.has(name, action)) {
                return;
            }
        }
        var handler = new Handler(action, context || this);
        handlers[name].push(handler);

        return this;
    },

    /**
     * Register event, event will only be triggered once and then removed
     * @param  {string} name
     * @param  {Function} action
     * @param  {Object} [context]
     * @chainable
     */
    once: function(name, action, context) {
        if (!name || !action) {
            return;
        }
        var self = this;
        function wrapper() {
            self.off(name, wrapper);
            action.apply(this, arguments);
        }
        return this.on(name, wrapper, context);
    },

    /**
     * Alias of once('before' + name)
     * @param  {string} name
     * @param  {Function} action
     * @param  {Object} [context]
     * @chainable
     */
    before: function(name, action, context) {
        if (!name || !action) {
            return;
        }
        name = 'before' + name;
        return this.on(name, action, context);
    },

    /**
     * Alias of once('after' + name)
     * @param  {string} name
     * @param  {Function} action
     * @param  {Object} [context]
     * @chainable
     */
    after: function(name, action, context) {
        if (!name || !action) {
            return;
        }
        name = 'after' + name;
        return this.on(name, action, context);
    },

    /**
     * Alias of on('success')
     * @param  {Function} action
     * @param  {Object} [context]
     * @chainable
     */
    success: function(action, context) {
        return this.once('success', action, context);
    },

    /**
     * Alias of on('error')
     * @param  {Function} action
     * @param  {Object} [context]
     * @chainable
     */
    error: function(action, context) {
        return this.once('error', action, context);
    },

    /**
     * Remove event listener
     * @param  {Function} action
     * @param  {Object} [context]
     * @chainable
     */
    off: function(name, action) {

        var handlers = this.__handlers__ || (this.__handlers__={});

        if (!action) {
            handlers[name] = [];
            return;
        }
        if (handlers[name]) {
            var hdls = handlers[name];
            var retains = [];
            for (var i = 0; i < hdls.length; i++) {
                if (action && hdls[i].action !== action) {
                    retains.push(hdls[i]);
                }
            }
            handlers[name] = retains;
        }

        return this;
    },

    /**
     * If registered the event handler
     * @param  {string}  name
     * @param  {Function}  action
     * @return {boolean}
     */
    has: function(name, action) {
        var handlers = this.__handlers__;

        if (! handlers ||
            ! handlers[name]) {
            return false;
        }
        var hdls = handlers[name];
        for (var i = 0; i < hdls.length; i++) {
            if (hdls[i].action === action) {
                return true;
            }
        }
    }
};

export default notifier;