/* 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/. */

import { ContentTaskUtils } from "resource://testing-common/ContentTaskUtils.sys.mjs";
import { Preferences } from "resource://gre/modules/Preferences.sys.mjs";
import { setTimeout } from "resource://gre/modules/Timer.sys.mjs";

export var NarrateTestUtils = {
  TOGGLE: ".narrate-toggle",
  POPUP: ".narrate-dropdown .dropdown-popup",
  VOICE_SELECT: ".narrate-voices .select-toggle",
  VOICE_OPTIONS: ".narrate-voices .options",
  VOICE_SELECTED: ".narrate-voices .options .option.selected",
  VOICE_SELECT_LABEL: ".narrate-voices .select-toggle .current-voice",
  RATE: ".narrate-rate-input",
  START: ".narrate-dropdown:not(.speaking) .narrate-start-stop",
  STOP: ".narrate-dropdown.speaking .narrate-start-stop",
  BACK: ".narrate-skip-previous",
  FORWARD: ".narrate-skip-next",

  isVisible(element) {
    let win = element.ownerGlobal;
    let style = win.getComputedStyle(element);
    if (style.display == "none") {
      return false;
    }
    if (style.visibility != "visible") {
      return false;
    }
    if (win.XULPopupElement.isInstance(element) && element.state != "open") {
      return false;
    }

    // Hiding a parent element will hide all its children
    if (element.parentNode != element.ownerDocument) {
      return this.isVisible(element.parentNode);
    }

    return true;
  },

  isStoppedState(window, ok) {
    let $ = window.document.querySelector.bind(window.document);
    ok($(this.BACK).disabled, "back button is disabled");
    ok($(this.FORWARD).disabled, "forward button is disabled");
    ok(!!$(this.START), "start button is showing");
    ok(!$(this.STOP), "stop button is hidden");
    // This checks for a localized label. Not the best...
    ok($(this.START).title == "Start (N)", "Button tooltip is correct");
  },

  isStartedState(window, ok) {
    let $ = window.document.querySelector.bind(window.document);
    ok(!$(this.BACK).disabled, "back button is enabled");
    ok(!$(this.FORWARD).disabled, "forward button is enabled");
    ok(!$(this.START), "start button is hidden");
    ok(!!$(this.STOP), "stop button is showing");
    // This checks for a localized label. Not the best...
    ok($(this.STOP).title == "Stop (N)", "Button tooltip is correct");
  },

  selectVoice(window, voiceUri) {
    if (!this.isVisible(window.document.querySelector(this.VOICE_OPTIONS))) {
      window.document.querySelector(this.VOICE_SELECT).click();
    }

    let voiceOption = window.document.querySelector(
      `.narrate-voices .option[data-value="${voiceUri}"]`
    );

    voiceOption.focus();
    voiceOption.click();

    return voiceOption.classList.contains("selected");
  },

  getEventUtils(window) {
    let eventUtils = {
      _EU_Ci: Ci,
      _EU_Cc: Cc,
      window,
      setTimeout,
      parent: window,
      navigator: window.navigator,
      KeyboardEvent: window.KeyboardEvent,
      KeyEvent: window.KeyEvent,
    };
    Services.scriptloader.loadSubScript(
      "chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
      eventUtils
    );
    return eventUtils;
  },

  getReaderReadyPromise(window) {
    return new Promise(resolve => {
      function observeReady(subject, topic) {
        if (subject == window) {
          Services.obs.removeObserver(observeReady, topic);
          resolve();
        }
      }

      if (window.document.body.classList.contains("loaded")) {
        resolve();
      } else {
        Services.obs.addObserver(observeReady, "AboutReader:Ready");
      }
    });
  },

  waitForNarrateToggle(window) {
    let toggle = window.document.querySelector(this.TOGGLE);
    return ContentTaskUtils.waitForCondition(() => !toggle.hidden, "");
  },

  waitForPrefChange(pref) {
    return new Promise(resolve => {
      function observeChange() {
        Services.prefs.removeObserver(pref, observeChange);
        resolve(Preferences.get(pref));
      }

      Services.prefs.addObserver(pref, observeChange);
    });
  },

  sendBoundaryEvent(window, name, charIndex, charLength) {
    let detail = { type: "boundary", args: { name, charIndex, charLength } };
    window.dispatchEvent(new window.CustomEvent("testsynthevent", { detail }));
  },

  isWordHighlightGone(window, ok) {
    let $ = window.document.querySelector.bind(window.document);
    ok(!$(".narrate-word-highlight"), "No more word highlights exist");
  },

  getWordHighlights(window) {
    let $$ = window.document.querySelectorAll.bind(window.document);
    let nodes = Array.from($$(".narrate-word-highlight"));
    return nodes.map(node => {
      return {
        word: node.dataset.word,
        left: Number(node.style.left.replace(/px$/, "")),
        top: Number(node.style.top.replace(/px$/, "")),
      };
    });
  },
};
