// pdfjs_dom.js — l'albero XML su cui `SVGGraphics` di pdf.js disegna, e il
// serializzatore che lo trasforma nella stringa che il motore C++ riceve.
//
// Il back-end SVG di pdf.js NON rasterizza: costruisce nodi
// (`document.createElementNS("http://www.w3.org/2000/svg", "svg:path")`,
// `setAttributeNS`, `append`) e alla fine qualcuno li serializza. Su Mac quel qualcuno è
// WebKit; qui è questo file. È l'unica ragione per cui esiste — non è un DOM, è
// **l'albero di output**.
//
// La superficie è quella che pdf.js tocca davvero, censita sul file (`pdf.js`, 3.11.174):
//   document.createElementNS / createElement / createTextNode
//   Element: setAttribute(NS) · getAttribute · removeAttribute(NS) · hasAttribute
//            appendChild · insertBefore · removeChild · append · prepend · remove
//            cloneNode · textContent · firstChild/lastChild/children
//            getElementsByTagName · querySelectorAll / querySelector (solo per TAG:
//            il convertitore chiede `"*"` e `"style"`, non selettori CSS)
//   XMLSerializer.serializeToString
// Tutto il resto — eventi, stili calcolati, layout, misure — NON c'è, e non serve: il
// convertitore non chiama mai `getBBox()` (è la stessa scelta del Mac, che grazie a
// questo può usare una WKWebView mai mostrata e senza layout).

(function () {

  function escText(s) {
    return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  }
  function escAttr(s) { return escText(s).replace(/"/g, "&quot;"); }

  // ────────────────────────────────────────────────────────────── nodo di testo ──
  function TextNode(t) { this.nodeType = 3; this.nodeValue = String(t); this.parentNode = null; this.childNodes = []; }
  TextNode.prototype.cloneNode = function () { return new TextNode(this.nodeValue); };

  // ───────────────────────────────────────────────────────────────── elemento ──
  function Element(ns, name) {
    this.nodeType = 1;
    this.namespaceURI = ns;
    this.nodeName = name;
    this.tagName = name;
    this.localName = (name.indexOf(":") >= 0) ? name.split(":")[1] : name;
    // Gli attributi restano una LISTA, non un oggetto: l'ORDINE in cui pdf.js li scrive
    // è l'ordine in cui devono uscire, ed è quello che rende confrontabile il documento
    // prodotto qui con quello prodotto dal Mac.
    this.attrs = [];
    this.childNodes = [];
    this.parentNode = null;
    this.style = {};
  }

  Element.prototype.setAttribute = function (n, v) { this.setAttributeNS(null, n, v); };
  Element.prototype.setAttributeNS = function (ns, n, v) {
    for (var i = 0; i < this.attrs.length; i++) if (this.attrs[i].name === n) { this.attrs[i].value = String(v); return; }
    this.attrs.push({ ns: ns, name: n, value: String(v) });
  };
  Element.prototype.getAttribute = function (n) {
    for (var i = 0; i < this.attrs.length; i++) if (this.attrs[i].name === n) return this.attrs[i].value;
    return null;
  };
  Element.prototype.getAttributeNS = function (ns, n) { return this.getAttribute(n); };
  Element.prototype.hasAttribute = function (n) { return this.getAttribute(n) !== null; };
  Element.prototype.removeAttribute = function (n) {
    for (var i = 0; i < this.attrs.length; i++) if (this.attrs[i].name === n) { this.attrs.splice(i, 1); return; }
  };
  Element.prototype.removeAttributeNS = function (ns, n) { this.removeAttribute(n); };

  Element.prototype.appendChild = function (c) {
    if (c.parentNode) c.parentNode.removeChild(c);
    c.parentNode = this; this.childNodes.push(c); return c;
  };
  Element.prototype.insertBefore = function (c, ref) {
    if (!ref) return this.appendChild(c);
    var i = this.childNodes.indexOf(ref);
    if (i < 0) return this.appendChild(c);
    if (c.parentNode) c.parentNode.removeChild(c);
    c.parentNode = this; this.childNodes.splice(i, 0, c); return c;
  };
  Element.prototype.removeChild = function (c) {
    var i = this.childNodes.indexOf(c);
    if (i >= 0) { this.childNodes.splice(i, 1); c.parentNode = null; }
    return c;
  };
  // `append`/`prepend` variadici: `_initialize` di SVGGraphics (pdf.js:12409) li usa.
  Element.prototype.append = function () {
    for (var i = 0; i < arguments.length; i++) {
      var a = arguments[i];
      this.appendChild(typeof a === "string" ? new TextNode(a) : a);
    }
  };
  Element.prototype.prepend = function () {
    for (var i = arguments.length - 1; i >= 0; i--) {
      var a = arguments[i];
      this.insertBefore(typeof a === "string" ? new TextNode(a) : a, this.childNodes[0] || null);
    }
  };
  Element.prototype.replaceChildren = function () { this.childNodes.length = 0; this.append.apply(this, arguments); };
  Element.prototype.remove = function () { if (this.parentNode) this.parentNode.removeChild(this); };

  Element.prototype.cloneNode = function (deep) {
    var e = new Element(this.namespaceURI, this.nodeName);
    for (var i = 0; i < this.attrs.length; i++) {
      e.attrs.push({ ns: this.attrs[i].ns, name: this.attrs[i].name, value: this.attrs[i].value });
    }
    if (deep) for (var j = 0; j < this.childNodes.length; j++) e.appendChild(this.childNodes[j].cloneNode(true));
    return e;
  };

  Object.defineProperty(Element.prototype, "firstChild", { get: function () { return this.childNodes[0] || null; } });
  Object.defineProperty(Element.prototype, "lastChild", { get: function () { return this.childNodes[this.childNodes.length - 1] || null; } });
  Object.defineProperty(Element.prototype, "children", {
    get: function () { return this.childNodes.filter(function (n) { return n.nodeType === 1; }); }
  });
  Object.defineProperty(Element.prototype, "textContent", {
    get: function () {
      var s = "";
      for (var i = 0; i < this.childNodes.length; i++) {
        var c = this.childNodes[i];
        s += (c.nodeType === 3) ? c.nodeValue : c.textContent;
      }
      return s;
    },
    set: function (v) {
      this.childNodes.length = 0;
      if (v !== null && v !== undefined && v !== "") this.appendChild(new TextNode(v));
    }
  });

  Element.prototype._walk = function (fn) {
    for (var i = 0; i < this.childNodes.length; i++) {
      var c = this.childNodes[i];
      if (c.nodeType === 1) { fn(c); c._walk(fn); }
    }
  };
  // ⚠️ La differenza fra queste due NON è un dettaglio, ed è la ragione per cui sono
  // scritte separate: pdf.js emette ogni elemento col prefisso (`<svg:path>`), e il
  // convertitore usa ENTRAMBE contando sul comportamento del browser —
  //
  //   · `getElementsByTagName(n)` in un documento XML confronta il nome QUALIFICATO:
  //     `getElementsByTagName("path")` NON trova `<svg:path>`, `("svg:path")` sì. È per
  //     questo che `count_tag` del convertitore chiede sempre tutte e due le forme e le
  //     somma: se qui si confrontasse anche il nome locale, ogni elemento verrebbe
  //     contato DUE volte e la riga di copertura direbbe il doppio del vero.
  //   · `querySelectorAll(n)` usa un selettore CSS di tipo, che invece confronta il nome
  //     LOCALE: `querySelectorAll("style")` trova `<svg:style>`. È così che il
  //     convertitore ritrova i blocchi @font-face da issare in cima.
  //
  // `"*"` prende tutto in tutt'e due, come nel browser.
  Element.prototype.getElementsByTagName = function (name) {
    var want = String(name), out = [];
    this._walk(function (e) { if (want === "*" || e.nodeName === want) out.push(e); });
    return out;
  };
  // ⚠️ Solo selettori di TAG (e `"*"`). Il convertitore usa `"*"` e `"style"` e
  // nient'altro; un selettore CSS vero qui non funzionerebbe, e non deve arrivarci — se
  // un domani ci arrivasse tornerebbe una lista vuota, che è un bug silenzioso: per
  // questo sta scritto.
  Element.prototype.querySelectorAll = function (sel) {
    var want = String(sel).trim(), out = [];
    this._walk(function (e) { if (want === "*" || e.localName === want) out.push(e); });
    return out;
  };
  Element.prototype.querySelector = function (sel) { return this.querySelectorAll(sel)[0] || null; };
  Element.prototype.addEventListener = function () {};
  Element.prototype.removeEventListener = function () {};

  // ────────────────────────────────────────────────────────────── serializzatore ──
  function serialize(n) {
    if (n.nodeType === 3) return escText(n.nodeValue);
    var s = "<" + n.nodeName;
    for (var i = 0; i < n.attrs.length; i++) s += " " + n.attrs[i].name + '="' + escAttr(n.attrs[i].value) + '"';
    if (!n.childNodes.length) return s + "/>";
    s += ">";
    for (var j = 0; j < n.childNodes.length; j++) s += serialize(n.childNodes[j]);
    return s + "</" + n.nodeName + ">";
  }

  globalThis.XMLSerializer = function () {};
  XMLSerializer.prototype.serializeToString = function (n) { return serialize(n); };

  // ───────────────────────────────────────────────────────────────── document ──
  var doc = {
    nodeType: 9,
    baseURI: "file:///",
    createElementNS: function (ns, name) { return new Element(ns, name); },
    createElement: function (name) { return new Element("http://www.w3.org/1999/xhtml", name); },
    createTextNode: function (t) { return new TextNode(t); },
    fonts: { add: function () {}, delete: function () {}, forEach: function () {} },
    getElementsByName: function () { return []; },
    querySelector: function () { return null; },
    addEventListener: function () {},
    removeEventListener: function () {}
  };
  doc.documentElement = new Element("http://www.w3.org/1999/xhtml", "html");
  doc.head = new Element("http://www.w3.org/1999/xhtml", "head");
  doc.body = new Element("http://www.w3.org/1999/xhtml", "body");
  doc.documentElement.appendChild(doc.head);
  doc.documentElement.appendChild(doc.body);

  globalThis.document = doc;
  globalThis.Element = Element;
  globalThis.Node = Element;
})();
