import { Scope as v, LeafBlot as g } from "../../../../parchment@3.0.0/node_modules/parchment/dist/parchment.mjs";
import l from "./emitter.mjs";
import E from "./logger.mjs";
import O from "../../../../lodash-es@4.17.21/node_modules/lodash-es/isEqual.mjs";
import m from "../../../../lodash-es@4.17.21/node_modules/lodash-es/cloneDeep.mjs";
const f = E("quill:selection");
class p {
  constructor(t) {
    let s = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 0;
    this.index = t, this.length = s;
  }
}
class B {
  constructor(t, s) {
    this.emitter = s, this.scroll = t, this.composing = !1, this.mouseDown = !1, this.root = this.scroll.domNode, this.cursor = this.scroll.create("cursor", this), this.savedRange = new p(0, 0), this.lastRange = this.savedRange, this.lastNative = null, this.handleComposition(), this.handleDragging(), this.emitter.listenDOM("selectionchange", document, () => {
      !this.mouseDown && !this.composing && setTimeout(this.update.bind(this, l.sources.USER), 1);
    }), this.emitter.on(l.events.SCROLL_BEFORE_UPDATE, () => {
      if (!this.hasFocus())
        return;
      const n = this.getNativeRange();
      n != null && n.start.node !== this.cursor.textNode && this.emitter.once(l.events.SCROLL_UPDATE, (e, o) => {
        try {
          this.root.contains(n.start.node) && this.root.contains(n.end.node) && this.setNativeRange(n.start.node, n.start.offset, n.end.node, n.end.offset);
          const i = o.some((r) => r.type === "characterData" || r.type === "childList" || r.type === "attributes" && r.target === this.root);
          this.update(i ? l.sources.SILENT : e);
        } catch {
        }
      });
    }), this.emitter.on(l.events.SCROLL_OPTIMIZE, (n, e) => {
      if (e.range) {
        const {
          startNode: o,
          startOffset: i,
          endNode: r,
          endOffset: a
        } = e.range;
        this.setNativeRange(o, i, r, a), this.update(l.sources.SILENT);
      }
    }), this.update(l.sources.SILENT);
  }
  handleComposition() {
    this.emitter.on(l.events.COMPOSITION_BEFORE_START, () => {
      this.composing = !0;
    }), this.emitter.on(l.events.COMPOSITION_END, () => {
      if (this.composing = !1, this.cursor.parent) {
        const t = this.cursor.restore();
        if (!t)
          return;
        setTimeout(() => {
          this.setNativeRange(t.startNode, t.startOffset, t.endNode, t.endOffset);
        }, 1);
      }
    });
  }
  handleDragging() {
    this.emitter.listenDOM("mousedown", document.body, () => {
      this.mouseDown = !0;
    }), this.emitter.listenDOM("mouseup", document.body, () => {
      this.mouseDown = !1, this.update(l.sources.USER);
    });
  }
  focus() {
    this.hasFocus() || (this.root.focus({
      preventScroll: !0
    }), this.setRange(this.savedRange));
  }
  format(t, s) {
    this.scroll.update();
    const n = this.getNativeRange();
    if (!(n == null || !n.native.collapsed || this.scroll.query(t, v.BLOCK))) {
      if (n.start.node !== this.cursor.textNode) {
        const e = this.scroll.find(n.start.node, !1);
        if (e == null)
          return;
        if (e instanceof g) {
          const o = e.split(n.start.offset);
          e.parent.insertBefore(this.cursor, o);
        } else
          e.insertBefore(this.cursor, n.start.node);
        this.cursor.attach();
      }
      this.cursor.format(t, s), this.scroll.optimize(), this.setNativeRange(this.cursor.textNode, this.cursor.textNode.data.length), this.update();
    }
  }
  getBounds(t) {
    let s = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 0;
    const n = this.scroll.length();
    t = Math.min(t, n - 1), s = Math.min(t + s, n - 1) - t;
    let e, [o, i] = this.scroll.leaf(t);
    if (o == null)
      return null;
    if (s > 0 && i === o.length()) {
      const [u] = this.scroll.leaf(t + 1);
      if (u) {
        const [N] = this.scroll.line(t), [R] = this.scroll.line(t + 1);
        N === R && (o = u, i = 0);
      }
    }
    [e, i] = o.position(i, !0);
    const r = document.createRange();
    if (s > 0)
      return r.setStart(e, i), [o, i] = this.scroll.leaf(t + s), o == null ? null : ([e, i] = o.position(i, !0), r.setEnd(e, i), r.getBoundingClientRect());
    let a = "left", h;
    if (e instanceof Text) {
      if (!e.data.length)
        return null;
      i < e.data.length ? (r.setStart(e, i), r.setEnd(e, i + 1)) : (r.setStart(e, i - 1), r.setEnd(e, i), a = "right"), h = r.getBoundingClientRect();
    } else {
      if (!(o.domNode instanceof Element))
        return null;
      h = o.domNode.getBoundingClientRect(), i > 0 && (a = "right");
    }
    return {
      bottom: h.top + h.height,
      height: h.height,
      left: h[a],
      right: h[a],
      top: h.top,
      width: 0
    };
  }
  getNativeRange() {
    const t = document.getSelection();
    if (t == null || t.rangeCount <= 0)
      return null;
    const s = t.getRangeAt(0);
    if (s == null)
      return null;
    const n = this.normalizeNative(s);
    return f.info("getNativeRange", n), n;
  }
  getRange() {
    const t = this.scroll.domNode;
    if ("isConnected" in t && !t.isConnected)
      return [null, null];
    const s = this.getNativeRange();
    return s == null ? [null, null] : [this.normalizedToRange(s), s];
  }
  hasFocus() {
    return document.activeElement === this.root || document.activeElement != null && d(this.root, document.activeElement);
  }
  normalizedToRange(t) {
    const s = [[t.start.node, t.start.offset]];
    t.native.collapsed || s.push([t.end.node, t.end.offset]);
    const n = s.map((i) => {
      const [r, a] = i, h = this.scroll.find(r, !0), u = h.offset(this.scroll);
      return a === 0 ? u : h instanceof g ? u + h.index(r, a) : u + h.length();
    }), e = Math.min(Math.max(...n), this.scroll.length() - 1), o = Math.min(e, ...n);
    return new p(o, e - o);
  }
  normalizeNative(t) {
    if (!d(this.root, t.startContainer) || !t.collapsed && !d(this.root, t.endContainer))
      return null;
    const s = {
      start: {
        node: t.startContainer,
        offset: t.startOffset
      },
      end: {
        node: t.endContainer,
        offset: t.endOffset
      },
      native: t
    };
    return [s.start, s.end].forEach((n) => {
      let {
        node: e,
        offset: o
      } = n;
      for (; !(e instanceof Text) && e.childNodes.length > 0; )
        if (e.childNodes.length > o)
          e = e.childNodes[o], o = 0;
        else if (e.childNodes.length === o)
          e = e.lastChild, e instanceof Text ? o = e.data.length : e.childNodes.length > 0 ? o = e.childNodes.length : o = e.childNodes.length + 1;
        else
          break;
      n.node = e, n.offset = o;
    }), s;
  }
  rangeToNative(t) {
    const s = this.scroll.length(), n = (e, o) => {
      e = Math.min(s - 1, e);
      const [i, r] = this.scroll.leaf(e);
      return i ? i.position(r, o) : [null, -1];
    };
    return [...n(t.index, !1), ...n(t.index + t.length, !0)];
  }
  setNativeRange(t, s) {
    let n = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : t, e = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : s, o = arguments.length > 4 && arguments[4] !== void 0 ? arguments[4] : !1;
    if (f.info("setNativeRange", t, s, n, e), t != null && (this.root.parentNode == null || t.parentNode == null || // @ts-expect-error Fix me later
    n.parentNode == null))
      return;
    const i = document.getSelection();
    if (i != null)
      if (t != null) {
        this.hasFocus() || this.root.focus({
          preventScroll: !0
        });
        const {
          native: r
        } = this.getNativeRange() || {};
        if (r == null || o || t !== r.startContainer || s !== r.startOffset || n !== r.endContainer || e !== r.endOffset) {
          t instanceof Element && t.tagName === "BR" && (s = Array.from(t.parentNode.childNodes).indexOf(t), t = t.parentNode), n instanceof Element && n.tagName === "BR" && (e = Array.from(n.parentNode.childNodes).indexOf(n), n = n.parentNode);
          const a = document.createRange();
          a.setStart(t, s), a.setEnd(n, e), i.removeAllRanges(), i.addRange(a);
        }
      } else
        i.removeAllRanges(), this.root.blur();
  }
  setRange(t) {
    let s = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : !1, n = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : l.sources.API;
    if (typeof s == "string" && (n = s, s = !1), f.info("setRange", t), t != null) {
      const e = this.rangeToNative(t);
      this.setNativeRange(...e, s);
    } else
      this.setNativeRange(null);
    this.update(n);
  }
  update() {
    let t = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : l.sources.USER;
    const s = this.lastRange, [n, e] = this.getRange();
    if (this.lastRange = n, this.lastNative = e, this.lastRange != null && (this.savedRange = this.lastRange), !O(s, this.lastRange)) {
      if (!this.composing && e != null && e.native.collapsed && e.start.node !== this.cursor.textNode) {
        const i = this.cursor.restore();
        i && this.setNativeRange(i.startNode, i.startOffset, i.endNode, i.endOffset);
      }
      const o = [l.events.SELECTION_CHANGE, m(this.lastRange), m(s), t];
      this.emitter.emit(l.events.EDITOR_CHANGE, ...o), t !== l.sources.SILENT && this.emitter.emit(...o);
    }
  }
}
function d(c, t) {
  try {
    t.parentNode;
  } catch {
    return !1;
  }
  return c.contains(t);
}
export {
  p as Range,
  B as default
};
