/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

// This is loaded into chrome windows with the subscript loader. Wrap in
// a block to prevent accidentally leaking globals onto `window`.
{
  const { TabNotes } = ChromeUtils.importESModule(
    "moz-src:///browser/components/tabnotes/TabNotes.sys.mjs"
  );

  const OVERFLOW_WARNING_THRESHOLD = 980;
  const OVERFLOW_MAX_THRESHOLD = 1000;

  const OverflowState = {
    NONE: "none",
    WARN: "warn",
    OVERFLOW: "overflow",
  };

  class MozTabbrowserTabNoteMenu extends MozXULElement {
    static markup = /*html*/ `
    <panel
        id="tabNotePanel"
        type="arrow"
        titlebar="normal"
        class="tab-note-editor-panel panel-no-padding"
        orient="vertical"
        role="dialog"
        ignorekeys="true"
        norolluponanchor="true"
        consumeoutsideclicks="false">

        <html:div class="panel-header" id="tab-note-editor-header" >
          <html:h1
            id="tab-note-editor-title">
          </html:h1>
        </html:div>

        <toolbarseparator id="tab-note-editor-separator" />

        <html:div
          class="panel-subview-body
          tab-note-editor-name">
          <html:textarea
            id="tab-note-text"
            name="tab-note-text"
            rows="3"
            value=""
            data-l10n-id="tab-note-editor-text-field"
          ></html:textarea>
        </html:div>

        <html:div
          class="panel-action-row panel-footer">
          <html:moz-button
              id="tab-note-editor-button-delete"
              type="icon ghost"
              data-l10n-id="tab-note-editor-button-delete">
          </html:moz-button>
          <html:div
            id="tab-note-overflow-indicator">
          </html:div>
          <html:moz-button-group
              id="tab-note-default-actions">
              <html:moz-button
                  id="tab-note-editor-button-cancel"
                  data-l10n-id="tab-note-editor-button-cancel">
              </html:moz-button>
              <html:moz-button
                  type="primary"
                  id="tab-note-editor-button-save"
                  data-l10n-id="tab-note-editor-button-save">
              </html:moz-button>
          </html:moz-button-group>
        </html:div>

    </panel>
       `;

    #initialized = false;
    #panel;
    #noteField;
    #headerEl;
    #separatorEl;
    #titleNode;
    /** @type {MozTabbrowserTab} */
    #currentTab = null;
    /** @type {boolean|null} */
    #createMode = null;
    #cancelButton;
    #saveButton;
    #deleteButton;
    #overflowIndicator;
    /** @type {TabNoteTelemetrySource|null} */
    #telemetrySource = null;

    connectedCallback() {
      if (this.#initialized) {
        return;
      }

      this.textContent = "";
      this.appendChild(this.constructor.fragment);
      this.initializeAttributeInheritance();

      this.#panel = this.querySelector("panel");
      this.#noteField = document.getElementById("tab-note-text");
      this.#headerEl = this.querySelector("#tab-note-editor-header");
      this.#separatorEl = this.querySelector("#tab-note-editor-separator");
      this.#titleNode = this.querySelector("#tab-note-editor-title");
      this.#cancelButton = this.querySelector("#tab-note-editor-button-cancel");
      this.#saveButton = this.querySelector("#tab-note-editor-button-save");
      this.#deleteButton = this.querySelector("#tab-note-editor-button-delete");
      this.#overflowIndicator = this.querySelector(
        "#tab-note-overflow-indicator"
      );

      this.#cancelButton.addEventListener("click", () => {
        this.#panel.hidePopup();
      });
      this.#saveButton.addEventListener("click", () => {
        this.saveNote();
      });
      this.#deleteButton.addEventListener("click", () => {
        this.#deleteNote();
      });
      this.#panel.addEventListener("keypress", this);
      this.#panel.addEventListener("popuphidden", this);
      this.#noteField.addEventListener("input", this);

      this.#initialized = true;
    }

    on_keypress(event) {
      if (event.defaultPrevented) {
        // The event has already been consumed inside of the panel.
        return;
      }

      switch (event.keyCode) {
        case KeyEvent.DOM_VK_ESCAPE:
          this.#panel.hidePopup();
          break;
        case KeyEvent.DOM_VK_RETURN:
          if (!event.shiftKey && event.target === this.#noteField) {
            this.saveNote();
          }
          break;
      }
    }

    on_input() {
      this.#updatePanel();
    }

    on_popuphidden() {
      this.#currentTab = null;
      this.#noteField.value = "";
      this.#telemetrySource = null;
    }

    get createMode() {
      return this.#createMode;
    }

    set createMode(createModeEnabled) {
      if (this.#createMode == createModeEnabled) {
        return;
      }
      let headerL10nId = createModeEnabled
        ? "tab-note-editor-title-create"
        : "tab-note-editor-title-edit";
      this.#titleNode.innerText =
        gBrowser.tabLocalization.formatValueSync(headerL10nId);
      this.#headerEl.hidden = !createModeEnabled;
      this.#separatorEl.hidden = !createModeEnabled;
      this.#deleteButton.hidden = createModeEnabled;
      this.#panel.setAttribute(
        "aria-label",
        gBrowser.tabLocalization.formatValueSync(headerL10nId)
      );

      this.#createMode = createModeEnabled;
    }

    get #panelPosition() {
      if (gBrowser.tabContainer.verticalMode) {
        return SidebarController._positionStart
          ? "topleft topright"
          : "topright topleft";
      }
      return "bottomleft topleft";
    }

    #updatePanel() {
      const inputLength = this.#noteField.value.length;
      const trimmedLength = this.#noteField.value.trim().length;

      let overflow;
      if (inputLength > OVERFLOW_MAX_THRESHOLD) {
        overflow = OverflowState.OVERFLOW;
      } else if (inputLength > OVERFLOW_WARNING_THRESHOLD) {
        overflow = OverflowState.WARN;
      } else {
        overflow = OverflowState.NONE;
      }

      this.#saveButton.disabled =
        overflow == OverflowState.OVERFLOW || trimmedLength === 0;

      if (overflow != OverflowState.NONE) {
        this.#panel.setAttribute("overflow", overflow);
        this.#overflowIndicator.innerText =
          gBrowser.tabLocalization.formatValueSync(
            "tab-note-editor-character-limit",
            {
              totalCharacters: inputLength,
              maxAllowedCharacters: OVERFLOW_MAX_THRESHOLD,
            }
          );
      } else {
        this.#panel.removeAttribute("overflow");
      }

      // Manually adjust panel height and scroll behaviour to compensate for input size
      // CSS has a `field-sizing` attribute that does this automatically,
      // but it is not yet supported.
      // TODO bug2006439: Replace this with `field-sizing` after the implementation of bug1832409
      this.#noteField.style.height = "auto"; // Reset height so previous manual adjustments do not affect calculations
      let computedStyle = getComputedStyle(this.#noteField);
      let contentHeight =
        this.#noteField.scrollHeight -
        parseFloat(computedStyle.paddingTop) -
        parseFloat(computedStyle.paddingBottom);
      this.#noteField.style.height = `${contentHeight}px`;
    }

    /**
     * @param {MozTabbrowserTab} tab
     *   The tab whose note this panel will control.
     * @param {object} [options]
     * @param {TabNoteTelemetrySource} [options.telemetrySource]
     *   The UI surface that requested to open this panel.
     */
    openPanel(tab, options = {}) {
      if (!TabNotes.isEligible(tab)) {
        return;
      }
      // Lazily set the icon to avoid loading it at startup
      if (!this.#deleteButton.iconSrc) {
        this.#deleteButton.iconSrc = "chrome://global/skin/icons/delete.svg";
      }
      this.#currentTab = tab;
      this.#telemetrySource = options.telemetrySource;

      TabNotes.get(tab).then(note => {
        if (note) {
          this.createMode = false;
          this.#noteField.value = note.text;
        } else {
          this.createMode = true;
        }

        this.#panel.addEventListener(
          "popupshown",
          () => {
            this.#noteField.focus();
          },
          {
            once: true,
          }
        );
        this.#panel.openPopup(tab, {
          position: this.#panelPosition,
        });

        this.#updatePanel();
      });
    }

    saveNote() {
      let note = this.#noteField.value;

      if (
        TabNotes.isEligible(this.#currentTab) &&
        note.trim().length &&
        note.length <= OVERFLOW_MAX_THRESHOLD
      ) {
        TabNotes.set(this.#currentTab, note, {
          telemetrySource: this.#telemetrySource,
        });
      }

      this.#panel.hidePopup();
    }

    #deleteNote() {
      if (TabNotes.isEligible(this.#currentTab)) {
        TabNotes.delete(this.#currentTab, {
          telemetrySource: this.#telemetrySource,
        });
      }
      this.#panel.hidePopup();
    }
  }

  customElements.define("tabnote-menu", MozTabbrowserTabNoteMenu);
}
