"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const syntaxTree = require("./lib-syntax-tree.cjs");
const stream = require("./lib-utils-stream.cjs");
const cstUtils = require("./lib-utils-cst-utils.cjs");
function linkContentToContainer(node) {
  for (const [name, value] of Object.entries(node)) {
    if (!name.startsWith("$")) {
      if (Array.isArray(value)) {
        value.forEach((item, index) => {
          if (syntaxTree.isAstNode(item)) {
            item.$container = node;
            item.$containerProperty = name;
            item.$containerIndex = index;
          }
        });
      } else if (syntaxTree.isAstNode(value)) {
        value.$container = node;
        value.$containerProperty = name;
      }
    }
  }
}
function getContainerOfType(node, typePredicate) {
  let item = node;
  while (item) {
    if (typePredicate(item)) {
      return item;
    }
    item = item.$container;
  }
  return void 0;
}
function getDocument(node) {
  const rootNode = findRootNode(node);
  const result = rootNode.$document;
  if (!result) {
    throw new Error("AST node has no document.");
  }
  return result;
}
function findRootNode(node) {
  while (node.$container) {
    node = node.$container;
  }
  return node;
}
function streamContents(node, options) {
  if (!node) {
    throw new Error("Node must be an AstNode.");
  }
  const range = options === null || options === void 0 ? void 0 : options.range;
  return new stream.StreamImpl(() => ({
    keys: Object.keys(node),
    keyIndex: 0,
    arrayIndex: 0
  }), (state) => {
    while (state.keyIndex < state.keys.length) {
      const property = state.keys[state.keyIndex];
      if (!property.startsWith("$")) {
        const value = node[property];
        if (syntaxTree.isAstNode(value)) {
          state.keyIndex++;
          if (isAstNodeInRange(value, range)) {
            return { done: false, value };
          }
        } else if (Array.isArray(value)) {
          while (state.arrayIndex < value.length) {
            const index = state.arrayIndex++;
            const element = value[index];
            if (syntaxTree.isAstNode(element) && isAstNodeInRange(element, range)) {
              return { done: false, value: element };
            }
          }
          state.arrayIndex = 0;
        }
      }
      state.keyIndex++;
    }
    return stream.DONE_RESULT;
  });
}
function streamAllContents(root, options) {
  if (!root) {
    throw new Error("Root node must be an AstNode.");
  }
  return new stream.TreeStreamImpl(root, (node) => streamContents(node, options));
}
function streamAst(root, options) {
  if (!root) {
    throw new Error("Root node must be an AstNode.");
  }
  return new stream.TreeStreamImpl(root, (node) => streamContents(node, options), { includeRoot: true });
}
function isAstNodeInRange(astNode, range) {
  var _a;
  if (!range) {
    return true;
  }
  const nodeRange = (_a = astNode.$cstNode) === null || _a === void 0 ? void 0 : _a.range;
  if (!nodeRange) {
    return false;
  }
  return cstUtils.inRange(nodeRange, range);
}
function streamReferences(node) {
  return new stream.StreamImpl(() => ({
    keys: Object.keys(node),
    keyIndex: 0,
    arrayIndex: 0
  }), (state) => {
    while (state.keyIndex < state.keys.length) {
      const property = state.keys[state.keyIndex];
      if (!property.startsWith("$")) {
        const value = node[property];
        if (syntaxTree.isReference(value)) {
          state.keyIndex++;
          return { done: false, value: { reference: value, container: node, property } };
        } else if (Array.isArray(value)) {
          while (state.arrayIndex < value.length) {
            const index = state.arrayIndex++;
            const element = value[index];
            if (syntaxTree.isReference(element)) {
              return { done: false, value: { reference: element, container: node, property, index } };
            }
          }
          state.arrayIndex = 0;
        }
      }
      state.keyIndex++;
    }
    return stream.DONE_RESULT;
  });
}
function assignMandatoryProperties(reflection, node) {
  const typeMetaData = reflection.getTypeMetaData(node.$type);
  const genericNode = node;
  for (const property of typeMetaData.properties) {
    if (property.defaultValue !== void 0 && genericNode[property.name] === void 0) {
      genericNode[property.name] = copyDefaultValue(property.defaultValue);
    }
  }
}
function copyDefaultValue(propertyType) {
  if (Array.isArray(propertyType)) {
    return [...propertyType.map(copyDefaultValue)];
  } else {
    return propertyType;
  }
}
exports.assignMandatoryProperties = assignMandatoryProperties;
exports.findRootNode = findRootNode;
exports.getContainerOfType = getContainerOfType;
exports.getDocument = getDocument;
exports.linkContentToContainer = linkContentToContainer;
exports.streamAllContents = streamAllContents;
exports.streamAst = streamAst;
exports.streamContents = streamContents;
exports.streamReferences = streamReferences;
//# sourceMappingURL=lib-utils-ast-utils.cjs.map
