import { EventTargetAssembler } from './eventtarget'

const { Node } = window
const {
    ATTRIBUTE_NODE,
    COMMENT_NODE,
    DOCUMENT_NODE,
    DOCUMENT_TYPE_NODE,
    DOCUMENT_FRAGMENT_NODE,
    ELEMENT_NODE,
    TEXT_NODE
} = Node
const { getTargetOf } = EventTargetAssembler
const NODE_PROPERTY_NAME = 'node'

/**
 * @see https://www.w3.org/TR/dom/#interface-node
 * @abstract
 */
export class NodeAssembler extends EventTargetAssembler {
    /**
     * @param {boolean} [deep]
     * @returns {NodeAssembler|*}
     */
    clone(deep) {
        return this.constructor.getInstanceOf(this.node.cloneNode(deep))
    }

    /**
     * @param {Node|NodeAssembler|*|null} node
     * @returns {NodeAssembler|*}
     */
    getInstanceOf(node) {
        return node?
            this.constructor.getAssemblerOf(node).getInstanceOf(node) :
            null
    }

    /**
     * @returns {Node|*}
     */
    get node() {
        return getTargetOf(this)
    }

    /**
     * @returns {DocumentAssembler}
     */
    get ownerDocument() {
        return this.getInstanceOf(this.node.ownerDocument)
    }

    /**
     * @param {Node|*} node
     * @returns {class|*}
     */
    static getAssemblerOf(node) {
        switch(node.nodeType) {
            case ATTRIBUTE_NODE :
                return this.attrAssembler.resolve(node)
            case COMMENT_NODE :
                return this.commentAssembler.resolve(node)
            case DOCUMENT_NODE :
                return this.documentAssembler.resolve(node)
            case DOCUMENT_TYPE_NODE :
                return this.doctypeAssembler.resolve(node)
            case DOCUMENT_FRAGMENT_NODE :
                return this.documentFragmentAssembler.resolve(node)
            case ELEMENT_NODE :
                return this.elementAssembler.resolve(node)
            case TEXT_NODE :
                return this.textAssembler.resolve(node)
            default :
                return NodeAssembler.ChildNodeAssembler
        }
    }

    /**
     * @param {NodeAssembler|Node|*} object
     * @returns {Node|*|null}
     */
    static getNodeOf(object) {
        const result = getTargetOf(object)
        return result instanceof Node || result instanceof window.Attr? // https://github.com/jsdom/jsdom/issues/1641
            result :
            null
    }

    /**
     * @param {Node|*} node
     * @returns {NodeAssembler|*}
     * @abstract
     */
    static resolve(node) {
        return this
    }

    /**
     * @returns {class|*} AttrAssembler
     */
    static get attrAssembler() {
        return NodeAssembler.AttrAssembler
    }

    /**
     * @returns {class|*} CommentAssembler or ChildNodeAssembler
     */
    static get commentAssembler() {
        return NodeAssembler.CommentAssembler || NodeAssembler.ChildNodeAssembler
    }

    /**
     * @returns {class|*} DocumentTypeAssembler or ChildNodeAssembler
     */
    static get doctypeAssembler() {
        return NodeAssembler.DocumentTypeAssembler || NodeAssembler.ChildNodeAssembler
    }

    /**
     * @returns {class|*} DocumentAssembler or ParentNodeAssembler
     */
    static get documentAssembler() {
        return NodeAssembler.DocumentAssembler || NodeAssembler.ParentNodeAssembler
    }

    /**
     * @returns {class|*} DocumentFragmentAssembler or ParentNodeAssembler
     */
    static get documentFragmentAssembler() {
        return NodeAssembler.DocumentFragmentAssembler || NodeAssembler.ParentNodeAssembler
    }

    /**
     * @returns {class|*} ElementAssembler
     */
    static get elementAssembler() {
        return NodeAssembler.ElementAssembler
    }

    /**
     * @returns {class|*} TextAssembler or ChildNodeAssembler
     */
    static get textAssembler() {
        return NodeAssembler.TextAssembler || NodeAssembler.ChildNodeAssembler
    }

    /**
     * @returns {string}
     */
    static get targetPropertyName() {
        return NODE_PROPERTY_NAME
    }

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

@@ -1945,35 +1945,132 @@
 NodeAssembler.Ch
-ild
+aracterDataAssembler%0A                    %7C%7C NodeAssembler.ChildNodeAssembler%0A                    %7C%7C 
 NodeAssembler%0A  
@@ -2599,32 +2599,44 @@
 *%7D AttrAssembler
+ or fallback
 %0A     */%0A    sta
@@ -2695,32 +2695,49 @@
 er.AttrAssembler
+ %7C%7C NodeAssembler
 %0A    %7D%0A%0A    /**%0A
@@ -2782,34 +2782,24 @@
 bler or 
-ChildNodeAssembler
+fallback
 %0A     */
@@ -2872,32 +2872,96 @@
 CommentAssembler
+%0A            %7C%7C NodeAssembler.CharacterDataAssembler%0A           
  %7C%7C NodeAssemble
@@ -2973,32 +2973,61 @@
 ldNodeAssembler%0A
+            %7C%7C NodeAssembler%0A
     %7D%0A%0A    /**%0A 
@@ -3076,34 +3076,24 @@
 bler or 
-ChildNodeAssembler
+fallback
 %0A     */
@@ -3171,32 +3171,44 @@
 entTypeAssembler
+%0A           
  %7C%7C NodeAssemble
@@ -3220,32 +3220,61 @@
 ldNodeAssembler%0A
+            %7C%7C NodeAssembler%0A
     %7D%0A%0A    /**%0A 
@@ -3319,35 +3319,24 @@
 bler or 
-ParentNodeAssembler
+fallback
 %0A     */
@@ -3411,32 +3411,44 @@
 ocumentAssembler
+%0A           
  %7C%7C NodeAssemble
@@ -3461,32 +3461,109 @@
 ntNodeAssembler%0A
+            %7C%7C NodeAssembler.ChildNodeAssembler%0A            %7C%7C NodeAssembler%0A
     %7D%0A%0A    /**%0A 
@@ -3616,35 +3616,24 @@
 bler or 
-ParentNodeAssembler
+fallback
 %0A     */
@@ -3724,32 +3724,44 @@
 ragmentAssembler
+%0A           
  %7C%7C NodeAssemble
@@ -3774,32 +3774,109 @@
 ntNodeAssembler%0A
+            %7C%7C NodeAssembler.ChildNodeAssembler%0A            %7C%7C NodeAssembler%0A
     %7D%0A%0A    /**%0A 
@@ -3908,32 +3908,44 @@
 ElementAssembler
+ or fallback
 %0A     */%0A    sta
@@ -4011,32 +4011,158 @@
 lementAssembler%0A
+            %7C%7C NodeAssembler.ParentNodeAssembler%0A            %7C%7C NodeAssembler.ChildNodeAssembler%0A            %7C%7C NodeAssembler%0A
     %7D%0A%0A    /**%0A 
@@ -4203,34 +4203,24 @@
 bler or 
-ChildNodeAssembler
+fallback
 %0A     */
@@ -4295,39 +4295,132 @@
 ssembler
- %7C%7C NodeAssembler.Child
+%0A            %7C%7C NodeAssembler.CharacterDataAssembler%0A            %7C%7C NodeAssembler.ChildNodeAssembler%0A            %7C%7C 
 NodeAsse

return the CharacterDataAssembler by default + proper fallback chains