import { NodeType, NAME, PARENT, CHILD, PREV, NEXT, IS_CONNECTED, HOOKS, OWNER_DOCUMENT, NamespaceURI } from './constants.mjs';
import { EventTarget } from './EventTarget.mjs';
import { isCharacterData, descendants, isTextNode, isParentNode, cloneNode } from './shared.mjs';

class Node extends EventTarget {
  nodeType = (() => NodeType.NODE)();
  [NAME] = '';
  [PARENT] = null;
  [CHILD] = null;
  [PREV] = null;
  [NEXT] = null;
  [IS_CONNECTED] = false;
  get [HOOKS]() {
    return this[OWNER_DOCUMENT].defaultView[HOOKS];
  }
  get localName() {
    return this[NAME];
  }
  get nodeName() {
    return this[NAME].toUpperCase();
  }
  get ownerDocument() {
    return this[OWNER_DOCUMENT];
  }
  get isConnected() {
    return this[IS_CONNECTED];
  }
  isDefaultNamespace(namespace) {
    return namespace === NamespaceURI.XHTML;
  }
  get parentNode() {
    return this[PARENT];
  }
  set parentNode(_readonly) {}
  get parentElement() {
    const parent = this[PARENT];
    if (!parent || parent.nodeType !== 1) return null;
    return parent;
  }
  set parentElement(_readonly) {}
  get previousSibling() {
    return this[PREV];
  }
  set previousSibling(_readonly) {}
  get nextSibling() {
    return this[NEXT];
  }
  set nextSibling(_readonly) {}
  get previousElementSibling() {
    let sib = this[PREV];
    while (sib && sib.nodeType !== 1) sib = sib[PREV];
    return sib;
  }
  set previousElementSibling(_readonly) {}
  get nextElementSibling() {
    let sib = this[NEXT];
    while (sib && sib.nodeType !== 1) sib = sib[NEXT];
    return sib;
  }
  set nextElementSibling(_readonly) {}
  get firstChild() {
    return this[CHILD];
  }
  set firstChild(_readonly) {}
  get lastChild() {
    let child = this[CHILD];
    while (child) {
      const next = child[NEXT];
      if (next == null) break;
      child = next;
    }
    return child;
  }
  set lastChild(_readonly) {}
  get nodeValue() {
    if (isCharacterData(this)) return this.data;
    return null;
  }
  set nodeValue(data) {
    if (isCharacterData(this)) this.data = data;
  }
  get textContent() {
    if (isCharacterData(this)) return this.data;
    let text = '';
    for (const node of descendants(this)) {
      if (isTextNode(node)) {
        text += node.data;
      }
    }
    return text;
  }
  set textContent(data) {
    if (isCharacterData(this)) {
      this.data = data;
    } else if (isParentNode(this)) {
      let child;
      while (child = this[CHILD]) {
        this.removeChild(child);
      }
      this.append(data);
    }
  }
  cloneNode(deep) {
    return cloneNode(this, deep);
  }
  contains(node) {
    let currentNode = node;
    while (true) {
      if (currentNode == null) return false;
      if (currentNode === this) return true;
      currentNode = node.parentNode;
    }
  }
}

export { Node };
