import { RootCstNodeImpl as y, CompositeCstNodeImpl as N, LeafCstNodeImpl as u } from "../parser/cst-node-builder.js";
import { isAbstractElement as g } from "../languages/generated/ast.js";
import { isAstNode as i, isReference as d, isRootCstNode as $, isCompositeCstNode as m, isLeafCstNode as C } from "../syntax-tree.js";
import { streamAst as f } from "../utils/ast-utils.js";
import { BiMap as h } from "../utils/collections.js";
import { streamCst as c } from "../utils/cst-utils.js";
class k {
  constructor(e) {
    this.grammarElementIdMap = new h(), this.tokenTypeIdMap = new h(), 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,
      // We need to create shallow copies of the errors
      // The original errors inherit from the `Error` class, which is not transferable across worker threads
      parserErrors: e.parserErrors.map((r) => Object.assign(Object.assign({}, r), { message: r.message })),
      value: this.dehydrateAstNode(e.value, this.createDehyrationContext(e.value))
    };
  }
  dehydrateLexerReport(e) {
    return e;
  }
  createDehyrationContext(e) {
    const r = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new Map();
    for (const s of f(e))
      r.set(s, {});
    if (e.$cstNode)
      for (const s of c(e.$cstNode))
        t.set(s, {});
    return {
      astNodes: r,
      cstNodes: t
    };
  }
  dehydrateAstNode(e, r) {
    const t = r.astNodes.get(e);
    t.$type = e.$type, t.$containerIndex = e.$containerIndex, t.$containerProperty = e.$containerProperty, e.$cstNode !== void 0 && (t.$cstNode = this.dehydrateCstNode(e.$cstNode, r));
    for (const [s, a] of Object.entries(e))
      if (!s.startsWith("$"))
        if (Array.isArray(a)) {
          const o = [];
          t[s] = o;
          for (const n of a)
            i(n) ? o.push(this.dehydrateAstNode(n, r)) : d(n) ? o.push(this.dehydrateReference(n, r)) : o.push(n);
        } else i(a) ? t[s] = this.dehydrateAstNode(a, r) : d(a) ? t[s] = this.dehydrateReference(a, r) : a !== void 0 && (t[s] = a);
    return t;
  }
  dehydrateReference(e, r) {
    const t = {};
    return t.$refText = e.$refText, e.$refNode && (t.$refNode = r.cstNodes.get(e.$refNode)), t;
  }
  dehydrateCstNode(e, r) {
    const t = r.cstNodes.get(e);
    return $(e) ? t.fullText = e.fullText : t.grammarSource = this.getGrammarElementId(e.grammarSource), t.hidden = e.hidden, t.astNode = r.astNodes.get(e.astNode), m(e) ? t.content = e.content.map((s) => this.dehydrateCstNode(s, r)) : C(e) && (t.tokenType = e.tokenType.name, t.offset = e.offset, t.length = e.length, t.startLine = e.range.start.line, t.startColumn = e.range.start.character, t.endLine = e.range.end.line, t.endColumn = e.range.end.character), t;
  }
  hydrate(e) {
    const r = e.value, t = this.createHydrationContext(r);
    return "$cstNode" in r && this.hydrateCstNode(r.$cstNode, t), {
      lexerErrors: e.lexerErrors,
      lexerReport: e.lexerReport,
      parserErrors: e.parserErrors,
      value: this.hydrateAstNode(r, t)
    };
  }
  createHydrationContext(e) {
    const r = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new Map();
    for (const a of f(e))
      r.set(a, {});
    let s;
    if (e.$cstNode)
      for (const a of c(e.$cstNode)) {
        let o;
        "fullText" in a ? (o = new y(a.fullText), s = o) : "content" in a ? o = new N() : "tokenType" in a && (o = this.hydrateCstLeafNode(a)), o && (t.set(a, o), o.root = s);
      }
    return {
      astNodes: r,
      cstNodes: t
    };
  }
  hydrateAstNode(e, r) {
    const t = r.astNodes.get(e);
    t.$type = e.$type, t.$containerIndex = e.$containerIndex, t.$containerProperty = e.$containerProperty, e.$cstNode && (t.$cstNode = r.cstNodes.get(e.$cstNode));
    for (const [s, a] of Object.entries(e))
      if (!s.startsWith("$"))
        if (Array.isArray(a)) {
          const o = [];
          t[s] = o;
          for (const n of a)
            i(n) ? o.push(this.setParent(this.hydrateAstNode(n, r), t)) : d(n) ? o.push(this.hydrateReference(n, t, s, r)) : o.push(n);
        } else i(a) ? t[s] = this.setParent(this.hydrateAstNode(a, r), t) : d(a) ? t[s] = this.hydrateReference(a, t, s, r) : a !== void 0 && (t[s] = a);
    return t;
  }
  setParent(e, r) {
    return e.$container = r, e;
  }
  hydrateReference(e, r, t, s) {
    return this.linker.buildReference(r, t, s.cstNodes.get(e.$refNode), e.$refText);
  }
  hydrateCstNode(e, r, t = 0) {
    const s = r.cstNodes.get(e);
    if (typeof e.grammarSource == "number" && (s.grammarSource = this.getGrammarElement(e.grammarSource)), s.astNode = r.astNodes.get(e.astNode), m(s))
      for (const a of e.content) {
        const o = this.hydrateCstNode(a, r, t++);
        s.content.push(o);
      }
    return s;
  }
  hydrateCstLeafNode(e) {
    const r = this.getTokenType(e.tokenType), t = e.offset, s = e.length, a = e.startLine, o = e.startColumn, n = e.endLine, l = e.endColumn, p = e.hidden;
    return new u(t, s, {
      start: {
        line: a,
        character: o
      },
      end: {
        line: n,
        character: l
      }
    }, r, p);
  }
  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 (const r of f(this.grammar))
      g(r) && this.grammarElementIdMap.set(r, e++);
  }
}
export {
  k as DefaultHydrator
};
