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 { CodeInputPaste } from './code-input-paste.abstract';
/**
 * The class used to control the code block modify content action.
 */
export class CodeBlockInputController extends CodeInputPaste {
  constructor() {
    super(...arguments);
    /**
     * Stop the input event bubbles and update the code value.
     *
     * @param {InputEvent} e - The input event object.
     */
    this.handleInputEvent = (e) => {
      this.stopEventPropagation(e);
      this.updateCodeContent();
    };
    /**
     * Prevent paste the file into code block. Prevent to delete the last space.
     *
     * @param {InputEvent} e - The input event object.
     */
    this.handleBeforeInput = (e) => {
      e.stopPropagation();
      const inputType = e.inputType;
      switch (inputType) {
        case 'deleteContentBackward':
          if (!this.value || !this.value.code || this.value.code === '') {
            e.preventDefault();
          }
          else {
            this.handleDeleteAction(e);
          }
          return;
        case 'historyUndo':
        case 'historyRedo':
          e.preventDefault();
          return;
      }
      this.updateCodeBlockRange(e);
    };
  }
  /**
   * Stop `beforeinput` and `input` event bubbles to parent element.
   */
  registerInputEvent() {
    const codeContentContainerRef = this.contentRenderer.getCodeContentContainerRef();
    if (codeContentContainerRef) {
      codeContentContainerRef.addEventListener('beforeinput', this.handleBeforeInput);
      codeContentContainerRef.addEventListener('input', this.handleInputEvent);
    }
  }
  /**
   * Remove the event listener.
   */
  unregisterEventListener() {
    const codeContentContainerRef = this.contentRenderer.getCodeContentContainerRef();
    if (codeContentContainerRef) {
      codeContentContainerRef.removeEventListener('beforeinput', this.handleBeforeInput);
      codeContentContainerRef.removeEventListener('input', this.handleInputEvent);
    }
  }
  /**
   * Modify the component code content when user press delete key.
   *
   * @param {InputEvent} e - The input event object.
   */
  handleDeleteAction(e) {
    const range = this.selectionController.getCurrentRange();
    if (range) {
      e.preventDefault();
      const contentStartOffset = range.contentOffset;
      const currentNodes = this.contentParseController.getCurrentCodeNodes();
      let content = this.getCodeNodesContent(currentNodes);
      let nextOffset = contentStartOffset;
      // In low version firefox, the selected text could not delete when press delete key, so we handle the action manually.
      if (range.selectedText.length) {
        content = this.deleteSelectedText(content, contentStartOffset, range.selectedText.length);
      }
      else if (contentStartOffset > 0) {
        content = content.slice(0, contentStartOffset - 1) + content.slice(contentStartOffset);
        nextOffset -= 1;
      }
      this.selectionController.setNextCodeBlockRange(nextOffset);
      this.valueController.updateValue(content, this.value.language, {
        recordHistory: true,
        offset: nextOffset,
      });
    }
  }
  /**
   * Update the code block range base on input data.
   *
   * @param {InputEvent} e - The input event object.
   */
  updateCodeBlockRange(e) {
    const range = this.selectionController.getCurrentRange();
    if (!this.insertCompositionTextController.isComposing() && range) {
      this.selectionController.setNextCodeBlockRange(range.contentOffset + this.getInsertDataLength(e));
    }
  }
  /**
   * Calculates the length of the inserted data based on the input event type.
   *
   * @param {InputEvent} e - The input event object.
   * @returns {number} - The length of the inserted data.
   */
  getInsertDataLength(e) {
    const { data, inputType } = e;
    switch (inputType) {
      case 'insertText':
        return data.length; // For insert text, we return the length of the inserted text.
      case 'insertParagraph':
      case 'insertLineBreak':
        return 1;
      default:
        return 0; // For other input types, we return 0.
    }
  }
  /**
   * Update the code content after user modify action.
   */
  updateCodeContent() {
    var _a;
    if (!this.insertCompositionTextController.isComposing()) {
      const codeContentContainerRef = this.contentRenderer.getCodeContentContainerRef();
      let newText = codeContentContainerRef.innerText;
      if (this.isInsertLineBreakInLowVersionFirefox(newText)) {
        newText = newText + '\n';
      }
      this.valueController.updateValue(newText, (_a = this.value) === null || _a === void 0 ? void 0 : _a.language, {
        recordHistory: true,
        offset: this.selectionController.getCurrentRange().contentOffset,
      });
    }
  }
  /**
   * Check if the new content update by insert line break in low version Firefox.
   *
   * @param {string} newText - The new text content.
   * @returns {boolean} - Boolean value.
   */
  isInsertLineBreakInLowVersionFirefox(newText) {
    var _a;
    const oldValue = ((_a = this.value) === null || _a === void 0 ? void 0 : _a.code) || '';
    if (!oldValue.endsWith('\n') && newText === oldValue + '\n') {
      return true;
    }
    return false;
  }
}
__decorate([
  MapToComponentField()
], CodeBlockInputController.prototype, "selectionController", void 0);
__decorate([
  MapToComponentField()
], CodeBlockInputController.prototype, "contentRenderer", void 0);
__decorate([
  MapToComponentField()
], CodeBlockInputController.prototype, "valueController", void 0);
__decorate([
  MapToComponentField()
], CodeBlockInputController.prototype, "contentParseController", void 0);
__decorate([
  MapToComponentField()
], CodeBlockInputController.prototype, "insertCompositionTextController", void 0);
__decorate([
  MapToComponentField()
], CodeBlockInputController.prototype, "value", void 0);
__decorate([
  MapToComponentMethod('componentDidLoad')
], CodeBlockInputController.prototype, "registerInputEvent", null);
__decorate([
  MapToComponentMethod('disconnectedCallback')
], CodeBlockInputController.prototype, "unregisterEventListener", null);
