import { define } from '../lib/obj-define.js';
import { reduceObjectProperty } from '../lib/reduce.js';
import { isString, isFunction } from '../lib/typeof.js';
import { matches } from '../lib/matches.js';
import { dispatch } from '../lib/dispatch.js';

/**
 * Check if an event has a selector in mathc in target list.
 * @private
 *
 * @param {Event} event The event to check.
 * @param {HTMLElement} node The root node.
 * @param {String} selector The CSS selector to match.
 * @return {HTMLElement} The element in target list which matches the selector.
 */
function checkDelegate(event, node, selector) {
    if (!selector) {
        return node;
    }
    let target = event.target;
    while (target && target !== node) {
        if (matches(target, selector)) {
            return target;
        }
        target = target.parentNode;
    }
    return null;
}
/**
 * Generate a delegate listener for an event.
 * @private
 *
 * @param {String} selector The CSS selector to match.
 * @param {Function} callback The original callback for the event.
 * @return {Function} Wrapped callback with a delegation check.
 */
function delegateCallback(selector, callback) {
    return (ev) => {
        let target = checkDelegate(ev, this.node, selector);
        if (target) {
            callback.call(this, ev, target);
        }
    };
}

/**
 * Simple Custom Component with events delegation,
 * It also implement a `dispatchEvent` wrapper named `trigger`.
 * @mixin EventsMixin
 * @memberof DNA.MIXINS.
 * @static
 *
 * @example
 * ```js
 * // my-component.js
 * import { BaseComponent } from '@dnajs/core';
 * export class MyComponent extends BaseComponent {
 *   get events() {
 *     return {
 *       'click button': 'onButtonClick'
 *     }
 *   }
 *   onButtonClick() {
 *     console.log('button clicked');
 *   }
 * }
 * ```
 * ```js
 * // app.js
 * import { define } from '@dnajs/core';
 * import { MyComponent } from './my-component.js';
 * define('my-component', MyComponent);
 * var element = new MyComponent();
 * var button = document.createElement('button');
 * button.innerText = 'Click me';
 * element.appendChild(button);
 * button.click(); // logs "button clicked"
 * ```
 */
export const EventsMixin = (SuperClass) => class extends SuperClass {
    /**
     * Iterate `events` properties.
     * @method constructor
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     */
    constructor(node) {
        super(node);
        let events = reduceObjectProperty(this, 'events');
        for (let k in events) {
            let callback = isString(events[k]) ?
                this[events[k]] :
                events[k];
            if (isFunction(callback)) {
                let selector = k.split(' ').slice(1).join(' ').trim();
                events[k] = delegateCallback.call(this, selector, callback);
            } else {
                throw new TypeError('Invalid callback for event.');
            }
        }
        define(this, 'events', { value: events });
    }
    /**
     * Attach and delegate events to the component.
     * @method connectedCallback
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     */
    connectedCallback() {
        super.connectedCallback();
        // bind events
        let events = this.events;
        for (let k in events) {
            let evName = k.split(' ').shift();
            this.node.addEventListener(evName, events[k]);
        }
    }
    /**
     * Detach and undelegate events from the component.
     * @method disconnectedCallback
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     */
    disconnectedCallback() {
        let events = this.events;
        for (let k in events) {
            let evName = k.split(' ').shift();
            this.node.removeEventListener(evName, events[k]);
        }
        super.disconnectedCallback();
    }
    /**
     * Delegate events to the component descendents.
     * @method delegate
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     *
     * @param {String} evName The name of the event to delegate.
     * @param {String} selector A CSS selector for descendents.
     * @param {Function} callback The callback to fire.
     */
    delegate(evName, selector, callback) {
        let wrapCallback = delegateCallback.call(this, selector, callback);
        this.node.addEventListener(evName, wrapCallback);
    }
    /**
     * `Node.prototype.dispatchEvent` wrapper.
     * @method trigger
     * @memberof DNA.MIXINS.EventsMixin
     * @instance
     *
     * @param {String} evName The name of the event to fire.
     * @param {Object} data A set of custom data to pass to the event.
     * @param {Boolean} bubbles Should the event bubble throw the DOM tree.
     * @param {Boolean} cancelable Can be the event cancel by a callback.
     * @return {Boolean} True if event propagation has not be stopped.
     */
    trigger(evName, data, bubbles = true, cancelable = true) {
        return dispatch(this.node, evName, data, bubbles, cancelable);
    }
};

@@ -3372,37 +3372,18 @@
    this.
+o
 n
-ode.addEventListener
 (evName,
@@ -3739,32 +3739,11 @@
 his.
-node.removeEventListener
+off
 (evN
@@ -4286,48 +4286,833 @@
     
-this.node.addEventListener(evName, wrapC
+return this.on(evName, wrapCallback);%0A    %7D%0A    /**%0A     * %60Node.prototype.addEventListener%60 wrapper.%0A     * @method on%0A     * @memberof DNA.MIXINS.EventsMixin%0A     * @instance%0A     *%0A     * @param %7BString%7D evName The name of the event to fire.%0A     * @param %7BFunction%7D callback The callback to fire.%0A     * @param %7BObject%7D options Listener options.%0A     */%0A    on(evName, callback, options) %7B%0A        return this.node.addEventListener(evName, callback, options);%0A    %7D%0A    /**%0A     * %60Node.prototype.removeEventListener%60 wrapper.%0A     * @method off%0A     * @memberof DNA.MIXINS.EventsMixin%0A     * @instance%0A     *%0A     * @param %7BString%7D evName The name of the event to unlisten.%0A     * @param %7BFunction%7D callback The callback to trim.%0A     */%0A    off(evName, callback) %7B%0A        return this.node.removeEventListener(evName, c
 allb

add on and off methods to events mixin prototype