import { isAstNode, isCompositeCstNode, isLeafCstNode, isReference, isRootCstNode } from "../syntax-tree.js";
import { streamAst } from "../utils/ast-utils.js";
import { isAbstractElement } from "../languages/generated/ast.js";
import { streamCst } from "../utils/cst-utils.js";
import { CompositeCstNodeImpl, LeafCstNodeImpl, RootCstNodeImpl } from "../parser/cst-node-builder.js";
import { BiMap } from "../utils/collections.js";
var DefaultHydrator = class {
	constructor(e) {
		this.grammarElementIdMap = new BiMap(), this.tokenTypeIdMap = new BiMap(), this.grammar = e.Grammar, this.lexer = e.parser.Lexer, this.linker = e.references.Linker;
	}
	dehydrate(e) {
		return {
			lexerErrors: e.lexerErrors,
			lexerReport: e.lexerReport ? this.dehydrateLexerReport(e.lexerReport) : void 0,
			parserErrors: e.parserErrors.map((e) => ({
				...e,
				message: e.message
			})),
			value: this.dehydrateAstNode(e.value, this.createDehyrationContext(e.value))
		};
	}
	dehydrateLexerReport(e) {
		return e;
	}
	createDehyrationContext(e) {
		let a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map();
		for (let o of streamAst(e)) a.set(o, {});
		if (e.$cstNode) for (let a of streamCst(e.$cstNode)) o.set(a, {});
		return {
			astNodes: a,
			cstNodes: o
		};
	}
	dehydrateAstNode(a, o) {
		let c = o.astNodes.get(a);
		c.$type = a.$type, c.$containerIndex = a.$containerIndex, c.$containerProperty = a.$containerProperty, a.$cstNode !== void 0 && (c.$cstNode = this.dehydrateCstNode(a.$cstNode, o));
		for (let [l, u] of Object.entries(a)) if (!l.startsWith("$")) if (Array.isArray(u)) {
			let a = [];
			c[l] = a;
			for (let c of u) isAstNode(c) ? a.push(this.dehydrateAstNode(c, o)) : isReference(c) ? a.push(this.dehydrateReference(c, o)) : a.push(c);
		} else isAstNode(u) ? c[l] = this.dehydrateAstNode(u, o) : isReference(u) ? c[l] = this.dehydrateReference(u, o) : u !== void 0 && (c[l] = u);
		return c;
	}
	dehydrateReference(e, a) {
		let o = {};
		return o.$refText = e.$refText, e.$refNode && (o.$refNode = a.cstNodes.get(e.$refNode)), o;
	}
	dehydrateCstNode(e, s) {
		let l = s.cstNodes.get(e);
		return isRootCstNode(e) ? l.fullText = e.fullText : l.grammarSource = this.getGrammarElementId(e.grammarSource), l.hidden = e.hidden, l.astNode = s.astNodes.get(e.astNode), isCompositeCstNode(e) ? l.content = e.content.map((e) => this.dehydrateCstNode(e, s)) : isLeafCstNode(e) && (l.tokenType = e.tokenType.name, l.offset = e.offset, l.length = e.length, l.startLine = e.range.start.line, l.startColumn = e.range.start.character, l.endLine = e.range.end.line, l.endColumn = e.range.end.character), l;
	}
	hydrate(e) {
		let a = e.value, o = this.createHydrationContext(a);
		return "$cstNode" in a && this.hydrateCstNode(a.$cstNode, o), {
			lexerErrors: e.lexerErrors,
			lexerReport: e.lexerReport,
			parserErrors: e.parserErrors,
			value: this.hydrateAstNode(a, o)
		};
	}
	createHydrationContext(e) {
		let a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map();
		for (let o of streamAst(e)) a.set(o, {});
		let s;
		if (e.$cstNode) for (let a of streamCst(e.$cstNode)) {
			let e;
			"fullText" in a ? (e = new RootCstNodeImpl(a.fullText), s = e) : "content" in a ? e = new CompositeCstNodeImpl() : "tokenType" in a && (e = this.hydrateCstLeafNode(a)), e && (o.set(a, e), e.root = s);
		}
		return {
			astNodes: a,
			cstNodes: o
		};
	}
	hydrateAstNode(a, o) {
		let c = o.astNodes.get(a);
		c.$type = a.$type, c.$containerIndex = a.$containerIndex, c.$containerProperty = a.$containerProperty, a.$cstNode && (c.$cstNode = o.cstNodes.get(a.$cstNode));
		for (let [l, u] of Object.entries(a)) if (!l.startsWith("$")) if (Array.isArray(u)) {
			let a = [];
			c[l] = a;
			for (let d of u) isAstNode(d) ? a.push(this.setParent(this.hydrateAstNode(d, o), c)) : isReference(d) ? a.push(this.hydrateReference(d, c, l, o)) : a.push(d);
		} else isAstNode(u) ? c[l] = this.setParent(this.hydrateAstNode(u, o), c) : isReference(u) ? c[l] = this.hydrateReference(u, c, l, o) : u !== void 0 && (c[l] = u);
		return c;
	}
	setParent(e, a) {
		return e.$container = a, e;
	}
	hydrateReference(e, a, o, s) {
		return this.linker.buildReference(a, o, s.cstNodes.get(e.$refNode), e.$refText);
	}
	hydrateCstNode(e, o, s = 0) {
		let c = o.cstNodes.get(e);
		if (typeof e.grammarSource == "number" && (c.grammarSource = this.getGrammarElement(e.grammarSource)), c.astNode = o.astNodes.get(e.astNode), isCompositeCstNode(c)) for (let a of e.content) {
			let e = this.hydrateCstNode(a, o, s++);
			c.content.push(e);
		}
		return c;
	}
	hydrateCstLeafNode(e) {
		let a = this.getTokenType(e.tokenType), o = e.offset, s = e.length, c = e.startLine, l = e.startColumn, u = e.endLine, d = e.endColumn, f = e.hidden;
		return new LeafCstNodeImpl(o, s, {
			start: {
				line: c,
				character: l
			},
			end: {
				line: u,
				character: d
			}
		}, a, f);
	}
	getTokenType(e) {
		return this.lexer.definition[e];
	}
	getGrammarElementId(e) {
		if (e) return this.grammarElementIdMap.size === 0 && this.createGrammarElementIdMap(), this.grammarElementIdMap.get(e);
	}
	getGrammarElement(e) {
		return this.grammarElementIdMap.size === 0 && this.createGrammarElementIdMap(), this.grammarElementIdMap.getKey(e);
	}
	createGrammarElementIdMap() {
		let e = 0;
		for (let a of streamAst(this.grammar)) isAbstractElement(a) && this.grammarElementIdMap.set(a, e++);
	}
};
export { DefaultHydrator };
