var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { MapToComponentField, MapToComponentMethod } from 'vega-slimmer/core';
import ShadowSelectionPolyfill from '../../../../polyfill/shadow-selection/shadow-selection-polyfill';
import { AutoRunWhenReRenderTaskQueueSlimmer } from '../../../../helpers/slimmers/auto-run-when-re-render-task-queue-slimmer.abstract';
import changeManager from '../../../../helpers/change-manager/change-manager';
import domNodeSubjectFactory from '../../../../helpers/change-manager/subject/dom-node-subject/dom-node-subject-factory';
import { VegaInternalCodeBlockSelectionChange } from '../../../../helpers/event-manager/event-id/vega-internal-event-id';
import { SelectionController } from '../../../vega-rich-text-editor/slimmers/controllers/selection-controller';
import { debounce } from '../../../../utils/timer';
import { CodeBlockRange } from './code-block-range';
/**
 * The class used to control the code block input cursor.
 */
export class VegaCodeBlockSelectionController extends AutoRunWhenReRenderTaskQueueSlimmer {
  constructor() {
    super(1);
    this.handleSelectionWithDebounce = debounce(this.handleSelectionChange, VegaCodeBlockSelectionController.SELECTION_CHANGE_DEBOUNCE_TIME, this);
    /**
     * The method invoke after the component re-render.
     */
    this.doTask = async () => {
      // add await Promise.resolve to put the code behind to a micro task
      await Promise.resolve();
      const selection = this.getSelection();
      const range = this.codeBlockRange.createNativeRange(this.contentParseController.getCurrentCodeNodes());
      if (selection && range) {
        selection.removeAllRanges();
        selection.addRange(range);
      }
    };
  }
  /**
   * Get current range instance.
   *
   * @returns {Nullable<CodeBlockRange>} - The range instance or null.
   */
  getCurrentRange() {
    return this.codeBlockRange;
  }
  /**
   * Update the text content offset, push the update native range task to a queue.
   *
   * @param {number} contentOffset - The next content offset.
   * @param {boolean} [immediatelyRun] - Run the task immediately or not.
   */
  setNextCodeBlockRange(contentOffset, immediatelyRun = false) {
    this.codeBlockRange.updateContentOffset(contentOffset);
    super.enqueueTheTaskIntoQueue(immediatelyRun);
  }
  /**
   * Component lifecycle
   */
  addSelectionChangeEventListener() {
    this.codeBlockRange = new CodeBlockRange(this.contentRenderer.getCodeContentContainerRef());
    // eslint-disable-next-line no-restricted-globals
    document.addEventListener('selectionchange', this.handleSelectionWithDebounce);
  }
  /**
   * Component lifecycle
   */
  removeSelectionChangeEventListener() {
    // eslint-disable-next-line no-restricted-globals
    document.removeEventListener('selectionchange', this.handleSelectionWithDebounce);
  }
  /**
   * Get the code block selected range
   *
   * @returns {Selection} A [Selection](https://developer.mozilla.org/en-US/docs/Web/API/Selection) object
   */
  getSelection() {
    return ShadowSelectionPolyfill.getSelection(this.host.shadowRoot);
  }
  /**
   * Notify the code content selection change event.
   */
  handleSelectionChange() {
    const selection = this.getSelection();
    if (selection && selection.rangeCount > 0) {
      this.syncCodeRangeByNativeRange();
      changeManager.notify(domNodeSubjectFactory.getSubject(this.host, VegaInternalCodeBlockSelectionChange), { detail: true });
    }
    else {
      changeManager.notify(domNodeSubjectFactory.getSubject(this.host, VegaInternalCodeBlockSelectionChange), { detail: false });
    }
  }
  /**
   * Sync the code block range to make sure the code block range content offset is up to date after user click or selected text.
   */
  syncCodeRangeByNativeRange() {
    const selection = this.getSelection();
    if (this.isCodeBlockActive() &&
      this.autoRunTaskQueue.queue.length === 0 &&
      !this.insertCompositionTextController.isComposing()) {
      const range = selection.getRangeAt(0);
      const { startContainer, startOffset } = range;
      this.codeBlockRange = this.codeBlockRange.updateRange(startContainer, startOffset, this.contentParseController.getCurrentCodeNodes(), range.toString());
    }
  }
  /**
   * This method is used to determine if the code block is currently focused or active.
   * In firefox and safari, the selection change event will trigger when the code block is not active, because the shadow selection polyfill will always return the selection of the code block.
   *
   * @returns {boolean} - Returns true if the code block is active, otherwise false.
   */
  isCodeBlockActive() {
    return this.host && this.host.classList.contains('focus');
  }
}
// Must make sure the debounce time will not greater than the RTE selection range debounce time.
VegaCodeBlockSelectionController.SELECTION_CHANGE_DEBOUNCE_TIME = Math.min(SelectionController.SELECTION_CHANGE_DEBOUNCE_TIME, 40);
__decorate([
  MapToComponentField()
], VegaCodeBlockSelectionController.prototype, "host", void 0);
__decorate([
  MapToComponentField()
], VegaCodeBlockSelectionController.prototype, "contentRenderer", void 0);
__decorate([
  MapToComponentField()
], VegaCodeBlockSelectionController.prototype, "contentParseController", void 0);
__decorate([
  MapToComponentField()
], VegaCodeBlockSelectionController.prototype, "insertCompositionTextController", void 0);
__decorate([
  MapToComponentMethod('componentDidLoad')
], VegaCodeBlockSelectionController.prototype, "addSelectionChangeEventListener", null);
__decorate([
  MapToComponentMethod('disconnectedCallback')
], VegaCodeBlockSelectionController.prototype, "removeSelectionChangeEventListener", null);
