import { i as RTENode, G as RTEDecoratorNode, s as stateEntityRenderingRegistry } from './code-block-9e761791.js';
import { i as isHTMLElement } from './ui-b7600a48.js';

/** Manages selection range for a rich text editor, providing methods to set, sync, and create native ranges based on RTENodes and Nodes. */
class RTERange {
  /**
   * Initial the rte range properties
   *
   * @param {Node} [startContainer] The range start element.
   * @param {Node} [endContainer] The range end element.
   * @param {number} [startOffset] The range start element content offset, default value is 0.
   * @param {number} [endOffset]  The range end element content offset, default value is 0.
   * @param {RTENode} [startNode] The range start node, the start node could be null if the start element is root element.
   * @param {RTENode} [endNode]  The range start node, the start node could be null if the start element is root element.
   * @param {Range} [nativeRange] The browser native range instance.
   */
  constructor(startContainer, endContainer, startOffset = 0, endOffset = 0, startNode, endNode, nativeRange) {
    this._startOffset = 0;
    this._endOffset = 0;
    this._startContainer = startContainer;
    this._endContainer = endContainer;
    this._startOffset = startOffset;
    this._endOffset = endOffset;
    this._startNode = startNode;
    this._endNode = endNode;
    if (nativeRange) {
      this._oldNativeRange = nativeRange;
    }
  }
  /**
   * Returns the start container as a Node or null if it is not set.
   * The start container could be null when the dom node should be create after re-render.
   *
   * @returns {Nullable<Node>} The range start element.
   */
  get startContainer() {
    return this._startContainer;
  }
  /**
   * Returns the end container as a Node or null if it is not set.
   * The end container could be null when the dom node should be create after re-render.
   *
   * @returns {Nullable<Node>} The range start element.
   */
  get endContainer() {
    return this._endContainer;
  }
  /**
   * Returns the range start RTE node as a RTENode or null if it is not set.
   * The start RTENode could be null when the range start node is the root element of the rich text editor.
   *
   * @returns {Nullable<RTENode>} The range start element.
   */
  get startNode() {
    return this._startNode;
  }
  /**
   * Returns the range end RTE node as a RTENode or null if it is not set.
   * The end RTENode could be null when the range end node is the root element of the rich text editor.
   *
   * @returns {Nullable<RTENode>} The range start element.
   */
  get endNode() {
    return this._endNode;
  }
  /**
   * Returns range's start offset.
   *
   * @returns {number} The character length in text node or the element index of parent element.
   */
  get startOffset() {
    return this._startOffset;
  }
  /**
   * Returns range's start offset.
   *
   * @returns {number} The character length in text node or the element index of parent element.
   */
  get endOffset() {
    return this._endOffset;
  }
  /**
   * Updates the range options for a Rich Text Editor.
   * This method will called after modify the content, the start node and end node should be rte node.
   * This method will called after selectionchange event emit, the start node and end node should be the html node.
   *
   * @param {RTERangeOptions} options - An object that contains the following properties:
   * @param {Range} [nativeRange] - The native range instance
   * @returns {RTERange} - The new RTERange instance
   */
  updateRTERange(options, nativeRange) {
    const { startOffset, endOffset } = options;
    this._startOffset = startOffset;
    this._endOffset = endOffset;
    const startNode = options.startNode;
    const endNode = options.endNode;
    if (startNode instanceof RTENode) {
      this._startNode = startNode;
      this._endNode = endNode;
      const startContainer = this._startNode.getRangeContainer();
      const endContainer = this._startNode === this._endNode ? startContainer : this._endNode.getRangeContainer();
      if (startContainer) {
        this._startContainer = startContainer;
        this._endContainer = endContainer;
      }
    }
    else {
      // The selectionchange callback has a 50 ms delay, so we need to check the next range is the same as the current ret range.
      this._startContainer = startNode;
      this._endContainer = endNode;
      this._startNode = this.getRTENodeByElement(this._startContainer);
      this._endNode =
        this._startContainer === this._endContainer
          ? this._startNode
          : this.getRTENodeByElement(this._endContainer);
      if (nativeRange) {
        this._oldNativeRange = nativeRange;
      }
    }
    return new RTERange(this._startContainer, this._endContainer, this._startOffset, this._endOffset, this._startNode, this._endNode, this._oldNativeRange);
  }
  /**
   * Creates a new instance of the `RTERange` class with the same properties as the current instance.
   *
   * @returns {RTERange} A new `RTERange` instance with the same start and end containers, offsets, and nodes.
   */
  clone() {
    return new RTERange(this._startContainer, this._endContainer, this._startOffset, this._endOffset, this._startNode, this._endNode, this._oldNativeRange);
  }
  /**
   * Creates a Range object based on start and end nodes and offsets.
   * Invoke this method to get the future range state if the range content modified.
   * Invoke this method to get the native range instance if range start node and end node not defined
   *
   * @returns {Nullable<Range>} The range instance. The return value could be null if the start node or end node are not rendered.
   */
  toNativeRange() {
    let range = null;
    if (this._startNode) {
      const startContainer = this._startNode.getRangeContainer();
      const endContainer = this._startNode === this._endNode
        ? startContainer
        : this._endNode.getRangeContainer();
      if (startContainer && endContainer) {
        range = new Range();
        range.setStart(startContainer, this._startOffset);
        range.setEnd(endContainer, this._endOffset);
        this._startContainer = startContainer;
        this._endContainer = endContainer;
        this._oldNativeRange = range;
        return range;
      }
    }
    if (!range && this._oldNativeRange) {
      return this._oldNativeRange;
    }
  }
  /**
   * Retrieves an RTENode based on the given element by checking its type and parent elements.
   *
   * @param {Node} element - An element in the DOM (Document Object Model).
   * @returns { Nullable<RTENode>} Returns a nullable `RTENode` object.
   */
  getRTENodeByElement(element) {
    if (element) {
      const registerElement = element.nodeType === Node.TEXT_NODE
        ? element.parentElement
        : isHTMLElement(element) && RTEDecoratorNode.isDecoratorNodeContainerElement(element)
          ? RTEDecoratorNode.getDecoratorNodeElement(element)
          : element;
      return stateEntityRenderingRegistry.getEntityByDOM(registerElement);
    }
  }
}

export { RTERange as R };
