/**
 * XMLtoObject - Converts XML into a JavaScript value or object.
 * GitHub: https://github.com/Pevtrick/XMLtoObject
 * by Patrick Jentsch: https://github.com/Pevtrick
 */

/**
 * The XMLDocument.toObject() method converts the XMLDocument into a JavaScript value or object.
 * @param {String} [attributePrefix=] - A Prefix, which is added to all properties generated by XML attributes.
 * @returns {Object} - The converted result.
 */
 XMLDocument.prototype.toObject = function(attributePrefix='') {
  let obj = {};

  obj[this.documentElement.nodeName] = this.documentElement.toObject(attributePrefix);

  return obj;
};

/**
* The Node.toObject() method converts the Node into a JavaScript value or object.
* @param {String} [attributePrefix=] - A Prefix, which is added to all properties generated by XML attributes.
* @returns {Object|String|null} - The converted result.
*/
Node.prototype.toObject = function(attributePrefix='') {
  let obj = null;

  switch (this.nodeType) {
      case Node.ELEMENT_NODE:
          let hasAttributes = this.attributes.length > 0;
          let hasChildNodes = this.childNodes.length > 0;

          /* Stop conversion if the Node doesn't contain any attributes or child nodes */
          if (!(hasAttributes || hasChildNodes)) {
              break;
          }

          obj = {};

          /* Convert attributes */
          for (let attribute of this.attributes) {
              obj[`attributePrefix${attribute.name}`] = attribute.value;
          }

          /* Convert child nodes */
          for (let childNode of this.childNodes) {
              switch (childNode.nodeType) {
                  case Node.ELEMENT_NODE:
                      break;
                  case Node.TEXT_NODE:
                      /* Check whether the child text node is the only child of the current node. */
                      if (!hasAttributes && this.childNodes.length === 1) {
                          obj = childNode.toObject(attributePrefix);
                          continue;
                      }
                      if (childNode.data.trim() === '') {continue;}
                      break;
                  default:
                      /* This recursion leads to a console message. */
                      childNode.toObject(attributePrefix);
                      continue;
              }
              /**
               * If the child node is the first of its type in this childset,
               * process it and add it directly as a property to the return object.
               * If not add it to an array which is set as a property of the return object.
               */
              if (childNode.nodeName in obj) {
                  if (!Array.isArray(obj[childNode.nodeName])) {
                      obj[childNode.nodeName] = [obj[childNode.nodeName]];
                  }
                  obj[childNode.nodeName].push(childNode.toObject(attributePrefix));
              } else {
                  obj[childNode.nodeName] = childNode.toObject(attributePrefix);
              }
          }
          break;
      case Node.TEXT_NODE:
          if (this.data.trim() !== '') {obj = this.data;}
          break;
      case Node.COMMENT_NODE:
          console.log('Skipping comment node:');
          console.log(node);
          break;
      case Node.DOCUMENT_NODE:
          obj = {};
          obj[this.documentElement.nodeName] = this.documentElement.toObject(attributePrefix);
          break;
      default:
          /**
           * The following node types are not processed because they don't offer data, which has to be stored in the object:
           * Node.PROCESSING_INSTRUCTION_NODE, Node.DOCUMENT_TYPE_NODE, Node.DOCUMENT_FRAGMENT_NODE
           * The following node types are deprecated and therefore not supported by this function:
           * Node.ATTRIBUTE_NODE, Node.CDATA_SECTION_NODE, Node.ENTITY_REFERENCE_NODE, Node.ENTITY_NODE, Node.NOTATION_NODE
           */
          console.log(`Node type: '${this.nodeType}' is not supported.`);
          console.log(node);
          break;
  }

  return obj;
}
