import { DATA, NodeType, ATTRIBUTES, OWNER_DOCUMENT, CHILD, NEXT } from './constants.mjs';

function isCharacterData(node) {
  return DATA in node;
}
function isTextNode(node) {
  return node.nodeType === NodeType.TEXT_NODE;
}
function isCommentNode(node) {
  return node.nodeType === NodeType.COMMENT_NODE;
}
function isElementNode(node) {
  return node.nodeType === NodeType.ELEMENT_NODE;
}
function isDocumentFragmentNode(node) {
  return node.nodeType === NodeType.DOCUMENT_FRAGMENT_NODE;
}
function isParentNode(node) {
  return 'appendChild' in node;
}
function cloneNode(node, deep, document = node.ownerDocument) {
  if (isTextNode(node)) {
    return document.createTextNode(node.data);
  } else if (isCommentNode(node)) {
    return document.createComment(node.data);
  } else if (isElementNode(node)) {
    const cloned = document.createElement(node.localName);
    if (node[ATTRIBUTES]) {
      for (let i = 0; i < node[ATTRIBUTES].length; i++) {
        const attribute = node[ATTRIBUTES].item(i);
        cloned.setAttributeNS(attribute.namespaceURI, attribute.name, attribute.value);
      }
    }
    if (deep) {
      for (const child of node.childNodes) {
        cloned.appendChild(cloneNode(child, true, document));
      }
    }
    return cloned;
  } else if (isDocumentFragmentNode(node)) {
    const fragment = document.createDocumentFragment();
    if (deep) {
      for (const child of node.childNodes) {
        fragment.appendChild(cloneNode(child, true, document));
      }
    }
    return fragment;
  } else {
    const cloned = new node.constructor();
    cloned[OWNER_DOCUMENT] = document;
    return cloned;
  }
}
function descendants(node) {
  const nodes = [];
  const walk = node => {
    nodes.push(node);
    const child = node[CHILD];
    if (child) walk(child);
    const sibling = node[NEXT];
    if (sibling) walk(sibling);
  };
  const child = node[CHILD];
  if (child) walk(child);
  return nodes;
}
function selfAndDescendants(node) {
  const nodes = descendants(node);
  nodes.unshift(node);
  return nodes;
}

export { cloneNode, descendants, isCharacterData, isCommentNode, isDocumentFragmentNode, isElementNode, isParentNode, isTextNode, selfAndDescendants };
