import { Assembler, INSTANCE_PROPERTY_NAME } from 'esmodule'

const { CustomEvent, EventTarget } = window
const { getTargetOf } = Assembler

/**
 * @see https://www.w3.org/TR/dom/#interface-eventtarget
 */
export class EventTargetAssembler extends Assembler {
    /**
     * @param {*} [init]
     * @returns {EventTarget|*}
     */
    assemble(init) {
        Object.defineProperty(this, '__event_handlers__', {
            value : { true : {}, false : {} }
        })
        return super.assemble(init)
    }

    /**
     * @param {Event|string|*} eventOrType
     * @param {CustomEventInit|{}} [eventInitDict]
     * @param {boolean} [eventInitDict.bubbles]
     * @param {boolean} [eventInitDict.cancelable]
     * @param {*} [eventInitDict.detail]
     * @returns {boolean}
     */
    emit(eventOrType, eventInitDict) {
        if(typeof eventOrType === 'string') {
            eventOrType = new CustomEvent(eventOrType, eventInitDict)
        }
        return getTargetOf(this).dispatchEvent(eventOrType)
    }

    /**
     * @param {string} type
     * @param {function} callback
     * @param {{capture,once,passive}|boolean} [options]
     */
    on(type, callback, options) {
        const useCapture = typeof options === 'boolean'?
            options :
            options && options.capture || false
        const handlers = this.__event_handlers__[Boolean(useCapture)]
        const map = handlers[type] || (handlers[type] = new Map)
        if(!map.has(callback)) {
            map.set(callback, callback = callback.bind(this))
            getTargetOf(this).addEventListener(type, callback, options)
        }
    }

    /**
     * @param {string} type
     * @param {function} callback
     * @param {{capture}|boolean} [options]
     */
    un(type, callback, options) {
        const useCapture = typeof options === 'boolean'?
            options :
            options && options.capture || false
        const map = this.__event_handlers__[Boolean(useCapture)][type]
        if(map) {
            getTargetOf(this).removeEventListener(type, map.get(callback), options)
            map.delete(callback)
        }
    }

    /**
     * @returns {EventTarget}
     * @override
     */
    static create() {
        return new EventTarget
    }

    /**
     * @returns {interface} EventTarget
     * @override
     */
    static get interface() {
        return EventTarget
    }
}

Object.defineProperty(
    EventTarget?
        EventTarget.prototype :
        window.Node.prototype, // MSIE fallback
    INSTANCE_PROPERTY_NAME,
    { writable : true, value : null })

/*
 * Workaround for JSDOM, where the Node still is not a superclass of the Attr
 * https://github.com/jsdom/jsdom/issues/1641
 */
if(Object.getPrototypeOf(window.Attr) !== window.Node) {
    Object.defineProperties(window.Attr.prototype, {
        nodeType : {
            configurable : true,
            enumerable : true,
            get() {
                return window.Node.ATTRIBUTE_NODE
            }
        },
        [INSTANCE_PROPERTY_NAME] : {
            writable : true,
            value : null
        }
    })
}

@@ -1122,32 +1122,55 @@
 passive%7D%7Cboolean
+%7CEventTargetAssembler%7C*
 %7D %5Boptions%5D%0A    
@@ -1195,32 +1195,37 @@
 allback, options
+ = %7B%7D
 ) %7B%0A        cons
@@ -1220,35 +1220,19 @@
         
-const useCapture = 
+if(
 typeof o
@@ -1243,33 +1243,35 @@
 ns === 'boolean'
-?
+) %7B
 %0A            opt
@@ -1267,33 +1267,55 @@
         options 
-:
+= %7B capture : options %7D
 %0A            opt
@@ -1311,47 +1311,122 @@
     
+%7D%0A
     
-options && options.capture %7C%7C false
+    else if(options instanceof EventTargetAssembler) %7B%0A            options = %7B context : options %7D%0A        %7D
 %0A   
@@ -1471,36 +1471,41 @@
 dlers__%5BBoolean(
-useC
+options.c
 apture)%5D%0A       
@@ -1650,16 +1650,35 @@
 ck.bind(
+options.context %7C%7C 
 this))%0A 
@@ -1917,16 +1917,21 @@
  options
+ = %7B%7D
 ) %7B%0A    
@@ -1938,27 +1938,11 @@
     
-const useCapture = 
+if(
 type
@@ -1965,17 +1965,19 @@
 boolean'
-?
+) %7B
 %0A       
@@ -1993,57 +1993,135 @@
 ons 
-:
+= %7B capture : options %7D
 %0A     
+   %7D%0A
        
-options && options.capture %7C%7C false
+ else if(options instanceof EventTargetAssembler) %7B%0A            options = %7B%7D%0A        %7D
 %0A   
@@ -2173,12 +2173,17 @@
 ean(
-useC
+options.c
 aptu

add ability to specify a custom context for callback invocation