text: "use strict";
  var __extends = (this && this.__extends) || (function () {
      var extendStatics = function (d, b) {
          extendStatics = Object.setPrototypeOf ||
              ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
              function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
          return extendStatics(d, b);
      };
      return function (d, b) {
          extendStatics(d, b);
          function __() { this.constructor = d; }
          d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
      };
  })();
  var __values = (this && this.__values) || function(o) {
      var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
      if (m) return m.call(o);
      if (o && typeof o.length === "number") return {
          next: function () {
              if (o && i >= o.length) o = void 0;
              return { value: o && o[i++], done: !o };
          }
      };
      throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
  };
  Object.defineProperty(exports, "__esModule", { value: true });
  var util_1 = require("@oozcitak/util");
  var interfaces_1 = require("@oozcitak/dom/lib/dom/interfaces");
  var BaseWriter_1 = require("./BaseWriter");
  /**
   * Serializes XML nodes into objects and arrays.
   */
  var ObjectWriter = /** @class */ (function (_super) {
      __extends(ObjectWriter, _super);
      /**
       * Initializes a new instance of `ObjectWriter`.
       *
       * @param builderOptions - XML builder options
       * @param writerOptions - serialization options
       */
      function ObjectWriter(builderOptions, writerOptions) {
          var _this = _super.call(this, builderOptions) || this;
          _this._writerOptions = util_1.applyDefaults(writerOptions, {
              format: "object",
              wellFormed: false,
              noDoubleEncoding: false,
              group: false,
              verbose: false
          });
          return _this;
      }
      /**
       * Produces an XML serialization of the given node.
       *
       * @param node - node to serialize
       */
      ObjectWriter.prototype.serialize = function (node) {
          this._currentList = [];
          this._currentIndex = 0;
          this._listRegister = [this._currentList];
          /**
           * First pass, serialize nodes
           * This creates a list of nodes grouped under node types while preserving
           * insertion order. For example:
           * [
           *   root: [
           *     node: [
           *       { "@" : { "att1": "val1", "att2": "val2" }
           *       { "#": "node text" }
           *       { childNode: [] }
           *       { "#": "more text" }
           *     ],
           *     node: [
           *       { "@" : { "att": "val" }
           *       { "#": [ "text line1", "text line2" ] }
           *     ]
           *   ]
           * ]
           */
          this.serializeNode(node, this._writerOptions.wellFormed, this._writerOptions.noDoubleEncoding);
          /**
           * Second pass, process node lists. Above example becomes:
           * {
           *   root: {
           *     node: [
           *       {
           *         "@att1": "val1",
           *         "@att2": "val2",
           *         "#1": "node text",
           *         childNode: {},
           *         "#2": "more text"
           *       },
           *       {
           *         "@att": "val",
           *         "#": [ "text line1", "text line2" ]
           *       }
           *     ]
           *   }
           * }
           */
          return this._process(this._currentList, this._writerOptions);
      };
      ObjectWriter.prototype._process = function (items, options) {
          var _a, _b, _c, _d, _e, _f, _g;
          if (items.length === 0)
              return {};
          // determine if there are non-unique element names
          var namesSeen = {};
          var hasNonUniqueNames = false;
          var textCount = 0;
          var commentCount = 0;
          var instructionCount = 0;
          var cdataCount = 0;
          for (var i = 0; i < items.length; i++) {
              var item = items[i];
              var key = Object.keys(item)[0];
              switch (key) {
                  case "@":
                      continue;
                  case "#":
                      textCount++;
                      break;
                  case "!":
                      commentCount++;
                      break;
                  case "?":
                      instructionCount++;
                      break;
                  case "$":
                      cdataCount++;
                      break;
                  default:
                      if (namesSeen[key]) {
                          hasNonUniqueNames = true;
                      }
                      else {
                          namesSeen[key] = true;
                      }
                      break;
              }
          }
          var defAttrKey = this._getAttrKey();
          var defTextKey = this._getNodeKey(interfaces_1.NodeType.Text);
          var defCommentKey = this._getNodeKey(interfaces_1.NodeType.Comment);
          var defInstructionKey = this._getNodeKey(interfaces_1.NodeType.ProcessingInstruction);
          var defCdataKey = this._getNodeKey(interfaces_1.NodeType.CData);
          if (textCount === 1 && items.length === 1 && util_1.isString(items[0]["#"])) {
              // special case of an element node with a single text node
              return items[0]["#"];
          }
          else if (hasNonUniqueNames) {
              var obj = {};
              // process attributes first
              for (var i = 0; i < items.length; i++) {
                  var item = items[i];
                  var key = Object.keys(item)[0];
                  if (key === "@") {
                      var attrs = item["@"];
                      var attrKeys = Object.keys(attrs);
                      if (attrKeys.length === 1) {
                          obj[defAttrKey + attrKeys[0]] = attrs[attrKeys[0]];
                      }
                      else {
                          obj[defAttrKey] = item["@"];
                      }
                  }
              }
              // list contains element nodes with non-unique names
              // return an array with mixed content notation
              var result = [];
              for (var i = 0; i < items.length; i++) {
                  var item = items[i];
                  var key = Object.keys(item)[0];
                  switch (key) {
                      case "@":
                          // attributes were processed above
                          break;
                      case "#":
                          result.push((_a = {}, _a[defTextKey] = item["#"], _a));
                          break;
                      case "!":
                          result.push((_b = {}, _b[defCommentKey] = item["!"], _b));
                          break;
                      case "?":
                          result.push((_c = {}, _c[defInstructionKey] = item["?"], _c));
                          break;
                      case "$":
                          result.push((_d = {}, _d[defCdataKey] = item["$"], _d));
                          break;
                      default:
                          // element node
                          var ele = item;
                          if (ele[key].length !== 0 && util_1.isArray(ele[key][0])) {
                              // group of element nodes
                              var eleGroup = [];
                              var listOfLists = ele[key];
                              for (var i_1 = 0; i_1 < listOfLists.length; i_1++) {
                                  eleGroup.push(this._process(listOfLists[i_1], options));
                              }
                              result.push((_e = {}, _e[key] = eleGroup, _e));
                          }
                          else {
                              // single element node
                              if (options.verbose) {
                                  result.push((_f = {}, _f[key] = [this._process(ele[key], options)], _f));
                              }
                              else {
                                  result.push((_g = {}, _g[key] = this._process(ele[key], options), _g));
                              }
                          }
                          break;
                  }
              }
              obj[defTextKey] = result;
              return obj;
          }
          else {
              // all element nodes have unique names
              // return an object while prefixing data node keys
              var textId = 1;
              var commentId = 1;
              var instructionId = 1;
              var cdataId = 1;
              var obj = {};
              for (var i = 0; i < items.length; i++) {
                  var item = items[i];
                  var key = Object.keys(item)[0];
                  switch (key) {
                      case "@":
                          var attrs = item["@"];
                          var attrKeys = Object.keys(attrs);
                          if (!options.group || attrKeys.length === 1) {
                              for (var attrName in attrs) {
                                  obj[defAttrKey + attrName] = attrs[attrName];
                              }
                          }
                          else {
                              obj[defAttrKey] = attrs;
                          }
                          break;
                      case "#":
                          textId = this._processSpecItem(item["#"], obj, options.group, defTextKey, textCount, textId);
                          break;
                      case "!":
                          commentId = this._processSpecItem(item["!"], obj, options.group, defCommentKey, commentCount, commentId);
                          break;
                      case "?":
                          instructionId = this._processSpecItem(item["?"], obj, options.group, defInstructionKey, instructionCount, instructionId);
                          break;
                      case "$":
                          cdataId = this._processSpecItem(item["$"], obj, options.group, defCdataKey, cdataCount, cdataId);
                          break;
                      default:
                          // element node
                          var ele = item;
                          if (ele[key].length !== 0 && util_1.isArray(ele[key][0])) {
                              // group of element nodes
                              var eleGroup = [];
                              var listOfLists = ele[key];
                              for (var i_2 = 0; i_2 < listOfLists.length; i_2++) {
                                  eleGroup.push(this._process(listOfLists[i_2], options));
                              }
                              obj[key] = eleGroup;
                          }
                          else {
                              // single element node
                              if (options.verbose) {
                                  obj[key] = [this._process(ele[key], options)];
                              }
                              else {
                                  obj[key] = this._process(ele[key], options);
                              }
                          }
                          break;
                  }
              }
              return obj;
          }
      };
      ObjectWriter.prototype._processSpecItem = function (item, obj, group, defKey, count, id) {
          var e_1, _a;
          if (!group && util_1.isArray(item) && count + item.length > 2) {
              try {
                  for (var item_1 = __values(item), item_1_1 = item_1.next(); !item_1_1.done; item_1_1 = item_1.next()) {
                      var subItem = item_1_1.value;
                      var key = defKey + (id++).toString();
                      obj[key] = subItem;
                  }
              }
              catch (e_1_1) { e_1 = { error: e_1_1 }; }
              finally {
                  try {
                      if (item_1_1 && !item_1_1.done && (_a = item_1.return)) _a.call(item_1);
                  }
                  finally { if (e_1) throw e_1.error; }
              }
          }
          else {
              var key = count > 1 ? defKey + (id++).toString() : defKey;
              obj[key] = item;
          }
          return id;
      };
      /** @inheritdoc */
      ObjectWriter.prototype.beginElement = function (name) {
          var _a, _b;
          var childItems = [];
          if (this._currentList.length === 0) {
              this._currentList.push((_a = {}, _a[name] = childItems, _a));
          }
          else {
              var lastItem = this._currentList[this._currentList.length - 1];
              if (this._isElementNode(lastItem, name)) {
                  if (lastItem[name].length !== 0 && util_1.isArray(lastItem[name][0])) {
                      var listOfLists = lastItem[name];
                      listOfLists.push(childItems);
                  }
                  else {
                      lastItem[name] = [lastItem[name], childItems];
                  }
              }
              else {
                  this._currentList.push((_b = {}, _b[name] = childItems, _b));
              }
          }
          this._currentIndex++;
          if (this._listRegister.length > this._currentIndex) {
              this._listRegister[this._currentIndex] = childItems;
          }
          else {
              this._listRegister.push(childItems);
          }
          this._currentList = childItems;
      };
      /** @inheritdoc */
      ObjectWriter.prototype.endElement = function () {
          this._currentList = this._listRegister[--this._currentIndex];
      };
      /** @inheritdoc */
      ObjectWriter.prototype.attribute = function (name, value) {
          var _a, _b;
          if (this._currentList.length === 0) {
              this._currentList.push({ "@": (_a = {}, _a[name] = value, _a) });
          }
          else {
              var lastItem = this._currentList[this._currentList.length - 1];
              /* istanbul ignore else */
              if (this._isAttrNode(lastItem)) {
                  lastItem["@"][name] = value;
              }
              else {
                  this._currentList.push({ "@": (_b = {}, _b[name] = value, _b) });
              }
          }
      };
      /** @inheritdoc */
      ObjectWriter.prototype.comment = function (data) {
          if (this._currentList.length === 0) {
              this._currentList.push({ "!": data });
          }
          else {
              var lastItem = this._currentList[this._currentList.length - 1];
              if (this._isCommentNode(lastItem)) {
                  if (util_1.isArray(lastItem["!"])) {
                      lastItem["!"].push(data);
                  }
                  else {
                      lastItem["!"] = [lastItem["!"], data];
                  }
              }
              else {
                  this._currentList.push({ "!": data });
              }
          }
      };
      /** @inheritdoc */
      ObjectWriter.prototype.text = function (data) {
          if (this._currentList.length === 0) {
              this._currentList.push({ "#": data });
          }
          else {
              var lastItem = this._currentList[this._currentList.length - 1];
              if (this._isTextNode(lastItem)) {
                  if (util_1.isArray(lastItem["#"])) {
                      lastItem["#"].push(data);
                  }
                  else {
                      lastItem["#"] = [lastItem["#"], data];
                  }
              }
              else {
                  this._currentList.push({ "#": data });
              }
          }
      };
      /** @inheritdoc */
      ObjectWriter.prototype.instruction = function (target, data) {
          var value = (data === "" ? target : target + " " + data);
          if (this._currentList.length === 0) {
              this._currentList.push({ "?": value });
          }
          else {
              var lastItem = this._currentList[this._currentList.length - 1];
              if (this._isInstructionNode(lastItem)) {
                  if (util_1.isArray(lastItem["?"])) {
                      lastItem["?"].push(value);
                  }
                  else {
                      lastItem["?"] = [lastItem["?"], value];
                  }
              }
              else {
                  this._currentList.push({ "?": value });
              }
          }
      };
      /** @inheritdoc */
      ObjectWriter.prototype.cdata = function (data) {
          if (this._currentList.length === 0) {
              this._currentList.push({ "$": data });
          }
          else {
              var lastItem = this._currentList[this._currentList.length - 1];
              if (this._isCDATANode(lastItem)) {
                  if (util_1.isArray(lastItem["$"])) {
                      lastItem["$"].push(data);
                  }
                  else {
                      lastItem["$"] = [lastItem["$"], data];
                  }
              }
              else {
                  this._currentList.push({ "$": data });
              }
          }
      };
      ObjectWriter.prototype._isAttrNode = function (x) {
          return "@" in x;
      };
      ObjectWriter.prototype._isTextNode = function (x) {
          return "#" in x;
      };
      ObjectWriter.prototype._isCommentNode = function (x) {
          return "!" in x;
      };
      ObjectWriter.prototype._isInstructionNode = function (x) {
          return "?" in x;
      };
      ObjectWriter.prototype._isCDATANode = function (x) {
          return "$" in x;
      };
      ObjectWriter.prototype._isElementNode = function (x, name) {
          return name in x;
      };
      /**
       * Returns an object key for an attribute or namespace declaration.
       */
      ObjectWriter.prototype._getAttrKey = function () {
          return this._builderOptions.convert.att;
      };
      /**
       * Returns an object key for the given node type.
       *
       * @param nodeType - node type to get a key for
       */
      ObjectWriter.prototype._getNodeKey = function (nodeType) {
          switch (nodeType) {
              case interfaces_1.NodeType.Comment:
                  return this._builderOptions.convert.comment;
              case interfaces_1.NodeType.Text:
                  return this._builderOptions.convert.text;
              case interfaces_1.NodeType.ProcessingInstruction:
                  return this._builderOptions.convert.ins;
              case interfaces_1.NodeType.CData:
                  return this._builderOptions.convert.cdata;
              /* istanbul ignore next */
              default:
                  throw new Error("Invalid node type.");
          }
      };
      return ObjectWriter;
  }(BaseWriter_1.BaseWriter));
  exports.ObjectWriter = ObjectWriter;
  //# sourceMappingURL=ObjectWriter.js.map