/**
 * Implemented by all those classes that emit events.
 */
export default class EventEmitter {
    /**
     * Connects to an object event.
     *
     * @param {String} eventName The event name
     * @param {Function} callback The callback function
     * @param {Object} thisArg The scope for the callback or %null
     */
    on(eventName, callback, thisArg) {
        if (!this.$events)
            this.$events = {};
        if (!this.$events[eventName])
            this.$events[eventName] = [];
        this.$events[eventName].push({callback, thisArg});
    }

    /**
     * Disconnects all handlers for callback.
     *
     * @param {Function} callback The callback function
     */
    off(callback) {
        if (!this.$events) return;
        for (let event in this.$events)
            for (let i = 0; i < event.length; i++)
                if (event[i].callback === callback)
                    event.splice(i--, 1);
    }

    /**
     * Disconnects all instance callbacks.
     *
     * @param {Object} thisArg The callbacks instance
     */
    disconnect(thisArg) {
        if (!this.$events) return;
        for (let event in this.$events)
            for (let i = 0; i < event.length; i++)
                if (event[i].thisArg === thisArg)
                    event.splice(i--, 1);
    }

    /**
     * Emits an event.
     *
     * @param {String} eventName The event name
     * @param {...*} args Arguments to pass to the callbacks
     */
    emit(eventName) {
        if (!this.$events || !this.$events[eventName])
            return;

        let args = Array.prototype.slice.call(arguments, 1);

        let callbacks = this.$events[eventName];
        for (let callback of callbacks)
            callback.callback.apply(callback.thisArg, args);
    }

    /**
     * Links the object with another object events.
     *
     * @param {Object} propValue The property and the new value
     * @param {Object} handlers The event handlers
     */
    linkEvents(propValue, handlers) {
        for (let prop in propValue) {
            let value = propValue[prop];
            if (this[prop])
                this[prop].disconnect(this);
            this[prop] = value;
            if (value)
                for (let event in handlers)
                    value.on(event, handlers[event], this);
        }
    }
}
EventEmitter.$inject = ['$element', '$scope'];