import dayjs from 'dayjs'
import { v4 as uuidv4 } from 'uuid';
import { KeyboardManager } from "./tools.js";
import { bindEvents } from './ui_utils.js'
import { AnnotationEditorType, AnnotationEditorParamsType } from '../constants';
import { PDFAnnotation } from "./pdf_annotation.js";

class PDFAnnotationSticky extends PDFAnnotation {

  static _defaultColor = "#000";

  static _defaultFontSize = 10;

  static _defaultInternalPadding = 2;

  static _keyboardManager = new KeyboardManager([
    [
      ["ctrl+Enter", "mac+meta+Enter", "Escape", "mac+Escape"],
      PDFAnnotationSticky.prototype.commitOrRemove,
    ],
  ]);
  constructor (options) {
    super({ ...options, name: "stickyAnnotation" });
    this._boundEditorDivBlur = this.editorDivBlur.bind(this);

    this._boundEditorDivFocus = this.editorDivFocus.bind(this);
  
    this._boundEditorDivInput = this.editorDivInput.bind(this);
  
    this._boundEditorDivKeydown = this.editorDivKeydown.bind(this);
  
    this._content = "";
  
    this._editorDivId = `${this.id}-editor`;
  
    this._hasAlreadyBeenCommitted = false;
  
    this.annotationObj = null

    this._color = options.color || PDFAnnotationSticky._defaultColor;
    this._fontSize = options.fontSize || PDFAnnotationSticky._defaultFontSize;
    this._internalPadding = options.internalPadding || PDFAnnotationSticky._defaultInternalPadding;
    this._stickyWidth = 20;
  }

  static updateDefaultParams(type, value) {
    switch (type) {
      case AnnotationEditorParamsType.FREETEXT_SIZE:
        PDFAnnotationSticky._defaultFontSize = value;
        break;
      case AnnotationEditorParamsType.FREETEXT_COLOR:
        PDFAnnotationSticky._defaultColor = value;
        break;
    }
  }

  /** @inheritdoc */
  updateParams(type, value) {
    switch (type) {
      case AnnotationEditorParamsType.FREETEXT_SIZE:
        this._updateFontSize(value);
        break;
      case AnnotationEditorParamsType.FREETEXT_COLOR:
        this._updateColor(value);
        break;
    }
  }

  static get defaultPropertiesToUpdate() {
    return [
      [
        AnnotationEditorParamsType.FREETEXT_SIZE,
        PDFAnnotationSticky._defaultFontSize,
      ],
      [
        AnnotationEditorParamsType.FREETEXT_COLOR,
        PDFAnnotationSticky._defaultColor || PDFAnnotationSticky._defaultLineColor,
      ],
    ];
  }

  get propertiesToUpdate() {
    return [
      [AnnotationEditorParamsType.FREETEXT_SIZE, this._fontSize],
      [AnnotationEditorParamsType.FREETEXT_COLOR, this._color],
    ];
  }

  /**
   * Update the font size and make this action as undoable.
   * @param {number} fontSize
   */
  _updateFontSize(fontSize) {
    this.editorDiv.style.fontSize = `${fontSize}px`;
    this.translate(0, -(fontSize - this._fontSize) * this.parent.scaleFactor);
    this._fontSize = fontSize;
    this._setEditorDimensions();
  }

  /**
   * Update the color and make this action undoable.
   * @param {string} color
   */
  _updateColor(color) {
    this._color = color;
    this.editorDiv.style.color = color;
  }

  /** @inheritdoc */
  getInitialTranslation() {
    // The start of the base line is where the user clicked.
    return [
      -this._internalPadding * this.parent.scaleFactor,
      -(this._internalPadding + this._fontSize) *
        this.parent.scaleFactor,
    ];
  }

  /** @inheritdoc */
  rebuild() {
    super.rebuild();
    if (this.div === null) {
      return;
    }

    if (!this.isAttachedToDOM) {
      // At some point this editor was removed and we're rebuilting it,
      // hence we must add it to its parent.
      this.parent.add(this);
    }
  }

  /** @inheritdoc */
  enableEditMode() {
    if (this.isInEditMode()) {
      return;
    }

    this.parent.setEditingState(false);
    super.enableEditMode();
    this.overlayDiv.classList.remove("enabled");
    this.editorDiv.style.display = 'initial';
    // this.editorDiv.contentEditable = true;
    this.div.draggable = false;
    this.div.removeAttribute("aria-activedescendant");
    // this.editorDiv.addEventListener("keydown", this._boundEditorDivKeydown);
    this.editorDiv.addEventListener("focus", this._boundEditorDivFocus);
    this.editorDiv.addEventListener("blur", this._boundEditorDivBlur);
    this.editorDiv.addEventListener("input", this._boundEditorDivInput);
  }

  /** @inheritdoc */
  disableEditMode() {
    if (!this.isInEditMode()) {
      return;
    }

    this.parent.setEditingState(true);
    super.disableEditMode();
    this.overlayDiv.classList.add("enabled");
    this.editorDiv.contentEditable = false;
    this.div.setAttribute("aria-activedescendant", this._editorDivId);
    this.editorDiv.style.display = "none"
    this.div.draggable = true;
    this.editorDiv.removeEventListener("keydown", this._boundEditorDivKeydown);
    this.editorDiv.removeEventListener("focus", this._boundEditorDivFocus);
    this.editorDiv.removeEventListener("blur", this._boundEditorDivBlur);
    this.editorDiv.removeEventListener("input", this._boundEditorDivInput);

    // On Chrome, the focus is given to <body> when contentEditable is set to
    // false, hence we focus the div.
    this.div.focus();

    // In case the blur callback hasn't been called.
    this.isEditing = false;
    this.parent.div.classList.add("freeTextEditing");
  }

  /** @inheritdoc */
  focusin(event) {
    super.focusin(event);
    if (event.target !== this.editorDiv) {
      this.editorDiv.focus();
    }
  }

  /** @inheritdoc */
  onceAdded() {
    if (this.width) {
      // The editor was created in using ctrl+c.
      return;
    }
    this.enableEditMode();
    this.editorDiv.focus();
  }

  /** @inheritdoc */
  isEmpty() {
    return !this.editorDiv || this.editorDiv.innerText.trim() === "";
  }

  /** @inheritdoc */
  remove() {
    this.isEditing = false;
    this.parent.setEditingState(true);
    this.parent.div.classList.add("freeTextEditing");
    super.remove();
  }

  /**
   * Extract the text from this editor.
   * @returns {string}
   */
  _extractText() {
    const divs = this.editorDiv.getElementsByTagName("div");
    if (divs.length === 0) {
      return this.editorDiv.innerText;
    }
    const buffer = [];
    if (this.editorDiv.firstChild?.nodeName === "#text") {
      buffer.push(this.editorDiv.firstChild.data)
    }
    for (const div of divs) {
      const first = div.firstChild;
      if (first?.nodeName === "#text") {
        buffer.push(first.data);
      } else {
        buffer.push("");
      }
    }
    return buffer.join("\n");
  }

  _setEditorDimensions() {
    const [parentWidth, parentHeight] = this.parent.viewportBaseDimensions;
    const rect = this.div.getBoundingClientRect();

    this.width = rect.width / parentWidth;
    this.height = rect.height / parentHeight;
  }

  /**
   * Commit the content we have in this editor.
   * @returns {undefined}
   */
  commit() {
    this._content = this._extractText().trimEnd();
    const [width, height] = this.parent.viewportBaseDimensions;
    const position = {
      left: this.x * width,
      bottom: this.y * height + this._stickyWidth,
      right: this.x * width + this._stickyWidth,
      top: this.y * height
    }
    this.annotationObj = {
      id: uuidv4(),
      obj_type: 'Text',
      obj_attr: {
        bdcolor: "#FF2600",
        content: this._content || undefined,
        date: dayjs().format('YYYY-MM-DD_HH:mm:ss'),
        ftransparency: 1,
        page: this.pageIndex,
        position: this.parsePositionForBackend(position, this.viewport.height, this.parent.scale),
        transparency: 1
      }
    }
    super.commit();
    if (!this._hasAlreadyBeenCommitted) {
      // This editor has something and it's the first time
      // it's commited so we can add it in the undo/redo stack.
      this._hasAlreadyBeenCommitted = true;
      this.parent.addUndoableEditor(this);
    }

    this.disableEditMode();

    this._setEditorDimensions();
  }

  parsePositionForBackend (position, pageHeight, scale) {
    return {
      left: Math.round(position.left * 1000 / scale) / 1000,
      bottom: Math.round((pageHeight - position.bottom) * 1000 / scale) / 1000,
      right: Math.round(position.right * 1000 / scale) / 1000,
      top: Math.round((pageHeight - position.top) * 1000 / scale) / 1000,
    };
  }

  /** @inheritdoc */
  shouldGetKeyboardEvents() {
    return this.isInEditMode();
  }

  /**
   * ondblclick callback.
   * @param {MouseEvent} event
   */
  dblclick() {
    this.enableEditMode();
    this.editorDiv.focus();
  }

  /**
   * onkeydown callback.
   * @param {KeyboardEvent} event
   */
  keydown(event) {
    if (event.target === this.div && event.key === "Enter") {
      this.enableEditMode();
      this.editorDiv.focus();
    }
  }

  editorDivKeydown(event) {
    PDFAnnotationSticky._keyboardManager.exec(this, event);
  }

  editorDivFocus() {
    this.isEditing = true;
  }

  editorDivBlur() {
    this.isEditing = false;
  }

  editorDivInput(event) {
    this.parent.div.classList.toggle("freeTextEditing", this.isEmpty());
  }

  /** @inheritdoc */
  disableEditing() {
    this.editorDiv.setAttribute("role", "comment");
    this.editorDiv.removeAttribute("aria-multiline");
  }

  /** @inheritdoc */
  enableEditing() {
    this.editorDiv.setAttribute("role", "textbox");
    this.editorDiv.setAttribute("aria-multiline", true);
  }

  /** @inheritdoc */
  render() {
    if (this.div) {
      return this.div;
    }

    let baseX, baseY;
    if (this.width) {
      baseX = this.x;
      baseY = this.y;
    }
    super.render();
    this.iconDiv = document.createElement("div");
    this.iconDiv.classList = "icon-wrapper";
    this.iconDiv.innerHTML= `<svg data-status="normal" width="32" height="32" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><path d="M0 0h32v32H0z"></path><path d="M28 5.333V24H14.667L8 28v-4H4V5.333h24zM18.667 18H9.333v1.333h9.334V18zm4-4H9.333v1.333h13.334V14zm0-4H9.333v1.333h13.334V10z" fill="#BE0C0E"></path></g></svg>`;
    this.div.append(this.iconDiv);

    this.editorDiv = document.createElement("div");
    this.editorDiv.className = "internal editor-container";

    this.editorDiv.setAttribute("id", this._editorDivId);
    this.enableEditing();

    this.editorDiv.contentEditable = true;

    const { style } = this.editorDiv;
    style.fontSize = `${this._fontSize}px`;
    style.color = this._color;

    style.top = 36 + 'px';
    style.left = "50%";

    this.div.append(this.editorDiv);

    this.overlayDiv = document.createElement("div");
    this.overlayDiv.classList.add("overlay", "enabled");
    this.div.append(this.overlayDiv);

    // TODO: implement paste callback.
    // The goal is to sanitize and have something suitable for this
    // editor.
    bindEvents(this, this.div, ["dblclick", "keydown"]);

    if (this.width) {
      // This editor was created in using copy (ctrl+c).
      this.setAt(
        baseX,
        baseY,
        this.width,
        this.height
      );

      for (const line of this._content.split("\n")) {
        const div = document.createElement("div");
        div.append(
          line ? document.createTextNode(line) : document.createElement("br")
        );
        this.editorDiv.append(div);
      }

      this.div.draggable = true;
      this.editorDiv.contentEditable = false;
    } else {
      this.div.draggable = false;
      this.editorDiv.contentEditable = true;
    }

    return this.div;
  }

  get contentDiv() {
    return this.editorDiv;
  }

  /** @inheritdoc */
  static deserialize(data, parent) {
    let editor;
    if (data.type === 'custom') {
      editor = super.renderImportAnnotations(data, parent);
    } else {
      editor = super.deserialize(data, parent);
    } 

    editor._fontSize = data.fontSize;
    editor._color = this._color;
    editor._content = data.value;

    return editor;
  }

  /** @inheritdoc */
  serialize() {
    if (this.isEmpty()) {
      return null;
    }

    const padding = this._internalPadding * this.parent.scaleFactor;
    const rect = this.getRect(padding, padding);

    return {
      annotationType: AnnotationEditorType.FREETEXT,
      color: this._color,
      fontSize: this._fontSize,
      value: this._content,
      pageIndex: this.parent.pageIndex,
      rect,
      rotation: this.rotation,
    };
  }
}

export { PDFAnnotationSticky };
