import { __export } from "../../../../_virtual/rolldown_runtime.js";
import { isAstNode, isMultiReference, isReference } from "../syntax-tree.js";
import { DONE_RESULT, StreamImpl, TreeStreamImpl } from "./stream.js";
import { inRange } from "./cst-utils.js";
var ast_utils_exports = /* @__PURE__ */ __export({
	assignMandatoryProperties: () => assignMandatoryProperties,
	copyAstNode: () => copyAstNode,
	findRootNode: () => findRootNode,
	getContainerOfType: () => getContainerOfType,
	getDocument: () => getDocument,
	getReferenceNodes: () => getReferenceNodes,
	hasContainerOfType: () => hasContainerOfType,
	linkContentToContainer: () => linkContentToContainer,
	streamAllContents: () => streamAllContents,
	streamAst: () => streamAst,
	streamContents: () => streamContents,
	streamReferences: () => streamReferences
}, 1);
function linkContentToContainer(e, g = {}) {
	for (let [_, v] of Object.entries(e)) _.startsWith("$") || (Array.isArray(v) ? v.forEach((v, y) => {
		isAstNode(v) && (v.$container = e, v.$containerProperty = _, v.$containerIndex = y, g.deep && linkContentToContainer(v, g));
	}) : isAstNode(v) && (v.$container = e, v.$containerProperty = _, g.deep && linkContentToContainer(v, g)));
}
function getContainerOfType(e, h) {
	let g = e;
	for (; g;) {
		if (h(g)) return g;
		g = g.$container;
	}
}
function hasContainerOfType(e, h) {
	let g = e;
	for (; g;) {
		if (h(g)) return !0;
		g = g.$container;
	}
	return !1;
}
function getDocument(e) {
	let h = findRootNode(e).$document;
	if (!h) throw Error("AST node has no document.");
	return h;
}
function findRootNode(e) {
	for (; e.$container;) e = e.$container;
	return e;
}
function getReferenceNodes(e) {
	return isReference(e) ? e.ref ? [e.ref] : [] : isMultiReference(e) ? e.items.map((e) => e.ref) : [];
}
function streamContents(e, g) {
	if (!e) throw Error("Node must be an AstNode.");
	let _ = g?.range;
	return new StreamImpl(() => ({
		keys: Object.keys(e),
		keyIndex: 0,
		arrayIndex: 0
	}), (g) => {
		for (; g.keyIndex < g.keys.length;) {
			let v = g.keys[g.keyIndex];
			if (!v.startsWith("$")) {
				let y = e[v];
				if (isAstNode(y)) {
					if (g.keyIndex++, isAstNodeInRange(y, _)) return {
						done: !1,
						value: y
					};
				} else if (Array.isArray(y)) {
					for (; g.arrayIndex < y.length;) {
						let e = y[g.arrayIndex++];
						if (isAstNode(e) && isAstNodeInRange(e, _)) return {
							done: !1,
							value: e
						};
					}
					g.arrayIndex = 0;
				}
			}
			g.keyIndex++;
		}
		return DONE_RESULT;
	});
}
function streamAllContents(e, h) {
	if (!e) throw Error("Root node must be an AstNode.");
	return new TreeStreamImpl(e, (e) => streamContents(e, h));
}
function streamAst(e, h) {
	if (e) {
		if (h?.range && !isAstNodeInRange(e, h.range)) return new TreeStreamImpl(e, () => []);
	} else throw Error("Root node must be an AstNode.");
	return new TreeStreamImpl(e, (e) => streamContents(e, h), { includeRoot: !0 });
}
function isAstNodeInRange(e, h) {
	if (!h) return !0;
	let g = e.$cstNode?.range;
	return g ? inRange(g, h) : !1;
}
function streamReferences(e) {
	return new StreamImpl(() => ({
		keys: Object.keys(e),
		keyIndex: 0,
		arrayIndex: 0
	}), (h) => {
		for (; h.keyIndex < h.keys.length;) {
			let v = h.keys[h.keyIndex];
			if (!v.startsWith("$")) {
				let y = e[v];
				if (isReference(y) || isMultiReference(y)) return h.keyIndex++, {
					done: !1,
					value: {
						reference: y,
						container: e,
						property: v
					}
				};
				if (Array.isArray(y)) {
					for (; h.arrayIndex < y.length;) {
						let b = h.arrayIndex++, x = y[b];
						if (isReference(x) || isMultiReference(y)) return {
							done: !1,
							value: {
								reference: x,
								container: e,
								property: v,
								index: b
							}
						};
					}
					h.arrayIndex = 0;
				}
			}
			h.keyIndex++;
		}
		return DONE_RESULT;
	});
}
function assignMandatoryProperties(e, h) {
	let g = e.getTypeMetaData(h.$type), _ = h;
	for (let e of Object.values(g.properties)) e.defaultValue !== void 0 && _[e.name] === void 0 && (_[e.name] = copyDefaultValue(e.defaultValue));
}
function copyDefaultValue(e) {
	return Array.isArray(e) ? [...e.map(copyDefaultValue)] : e;
}
function copyAstNode(e, g, v) {
	let y = { $type: e.$type };
	v && (v.set(e, y), v.set(y, e));
	for (let [b, x] of Object.entries(e)) if (!b.startsWith("$")) if (isAstNode(x)) y[b] = copyAstNode(x, g, v);
	else if (isReference(x)) y[b] = g(y, b, x.$refNode, x.$refText, x);
	else if (Array.isArray(x)) {
		let e = [];
		for (let S of x) isAstNode(S) ? e.push(copyAstNode(S, g, v)) : isReference(S) ? e.push(g(y, b, S.$refNode, S.$refText, S)) : e.push(S);
		y[b] = e;
	} else y[b] = x;
	return linkContentToContainer(y, { deep: !0 }), y;
}
export { assignMandatoryProperties, ast_utils_exports, findRootNode, getContainerOfType, getDocument, getReferenceNodes, linkContentToContainer, streamAllContents, streamAst, streamContents, streamReferences };
