/* -*- indent-tabs-mode: nil; js-indent-level: 2; fill-column: 80 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/sourceeditor.properties");

const { OS } = Services.appinfo;

// On linux, getting immediately the selection's range here fails, returning
const FIND_KEY = L10N.getStr("find.key");
const FINDNEXT_KEY = L10N.getStr("findNext.key");
const FINDPREV_KEY = L10N.getStr("findPrev.key");
// the replace's key with the appropriate modifiers based on OS
const REPLACE_KEY = OS == "Darwin" ? L10N.getStr("replaceAllMac.key")
                                   : L10N.getStr("replaceAll.key");

// values like it's not selected – even if the selection is visible.
// For the record, setting the selection's range immediately doesn't have
// any effect.
// It's like the <input> is not ready yet.
// Therefore, we trigger the UI focus event to the <input>, waiting for the
// response.
// Using a timeout could also work, but that is more precise, ensuring also
// the execution of the listeners added to the <input>'s focus.
const dispatchAndWaitForFocus = (target) => new Promise((resolve) => {
  target.addEventListener("focus", function listener() {
    target.removeEventListener("focus", listener);
    resolve(target);
  });

  target.dispatchEvent(new UIEvent("focus"));
});

function openSearchBox(ed) {
  let edDoc = ed.container.contentDocument;
  let edWin = edDoc.defaultView;

  let input = edDoc.querySelector("input[type=search]");
  ok(!input, "search box closed");

  // The editor needs the focus to properly receive the `synthesizeKey`
  ed.focus();

  synthesizeKeyShortcut(FINDNEXT_KEY, edWin);
  input = edDoc.querySelector("input[type=search]");
  ok(input, "find again command key opens the search box");
}

function testFindAgain(ed, inputLine, expectCursor, isFindPrev = false) {
  let edDoc = ed.container.contentDocument;
  let edWin = edDoc.defaultView;

  let input = edDoc.querySelector("input[type=search]");
  input.value = inputLine;

  // Ensure the input has the focus before send the key – necessary on Linux,
  // it seems that during the tests can be lost
  input.focus();

  if (isFindPrev) {
    synthesizeKeyShortcut(FINDPREV_KEY, edWin);
  } else {
    synthesizeKeyShortcut(FINDNEXT_KEY, edWin);
  }

  ch(ed.getCursor(), expectCursor,
    "find: " + inputLine + " expects cursor: " + expectCursor.toSource());
}

const testSearchBoxTextIsSelected = Task.async(function* (ed) {
  let edDoc = ed.container.contentDocument;
  let edWin = edDoc.defaultView;

  let input = edDoc.querySelector("input[type=search]");
  ok(input, "search box is opened");

  // Ensure the input has the focus before send the key – necessary on Linux,
  // it seems that during the tests can be lost
  input.focus();

  // Close search box
  EventUtils.synthesizeKey("VK_ESCAPE", {}, edWin);

  input = edDoc.querySelector("input[type=search]");
  ok(!input, "search box is closed");

  // Re-open the search box
  synthesizeKeyShortcut(FIND_KEY, edWin);

  input = edDoc.querySelector("input[type=search]");
  ok(input, "find command key opens the search box");

  yield dispatchAndWaitForFocus(input);

  let { selectionStart, selectionEnd, value } = input;

  ok(selectionStart === 0 && selectionEnd === value.length,
    "search box's text is selected when re-opened");

  // Removing selection
  input.setSelectionRange(0, 0);

  synthesizeKeyShortcut(FIND_KEY, edWin);

  ({ selectionStart, selectionEnd } = input);

  ok(selectionStart === 0 && selectionEnd === value.length,
    "search box's text is selected when find key is pressed");

  // Close search box
  EventUtils.synthesizeKey("VK_ESCAPE", {}, edWin);
});

const testReplaceBoxTextIsSelected = Task.async(function* (ed) {
  let edDoc = ed.container.contentDocument;
  let edWin = edDoc.defaultView;

  let input = edDoc.querySelector(".CodeMirror-dialog > input");
  ok(!input, "dialog box with replace is closed");

  // The editor needs the focus to properly receive the `synthesizeKey`
  ed.focus();

  synthesizeKeyShortcut(REPLACE_KEY, edWin);

  input = edDoc.querySelector(".CodeMirror-dialog > input");
  ok(input, "dialog box with replace is opened");

  input.value = "line 5";

  // Ensure the input has the focus before send the key – necessary on Linux,
  // it seems that during the tests can be lost
  input.focus();

  yield dispatchAndWaitForFocus(input);

  let { selectionStart, selectionEnd, value } = input;

  ok(!(selectionStart === 0 && selectionEnd === value.length),
    "Text in dialog box is not selected");

  synthesizeKeyShortcut(REPLACE_KEY, edWin);

  ({ selectionStart, selectionEnd } = input);

  ok(selectionStart === 0 && selectionEnd === value.length,
    "dialog box's text is selected when replace key is pressed");

  // Close dialog box
  EventUtils.synthesizeKey("VK_ESCAPE", {}, edWin);
});

add_task(function* () {
  let { ed, win } = yield setup();

  ed.setText([
    "// line 1",
    "//  line 2",
    "//   line 3",
    "//    line 4",
    "//     line 5"
  ].join("\n"));

  yield promiseWaitForFocus();

  openSearchBox(ed);

  let testVectors = [
    // Starting here expect data needs to get updated for length changes to
    // "textLines" above.
    ["line",
     {line: 0, ch: 7}],
    ["line",
     {line: 1, ch: 8}],
    ["line",
     {line: 2, ch: 9}],
    ["line",
     {line: 3, ch: 10}],
    ["line",
     {line: 4, ch: 11}],
    ["ne 3",
     {line: 2, ch: 11}],
    ["line 1",
      {line: 0, ch: 9}],
    // Testing find prev
    ["line",
      {line: 4, ch: 11},
      true],
    ["line",
      {line: 3, ch: 10},
      true],
    ["line",
      {line: 2, ch: 9},
      true],
    ["line",
      {line: 1, ch: 8},
      true],
    ["line",
      {line: 0, ch: 7},
      true]
  ];

  for (let v of testVectors) {
    yield testFindAgain(ed, ...v);
  }

  yield testSearchBoxTextIsSelected(ed);

  yield testReplaceBoxTextIsSelected(ed);

  teardown(ed, win);
});
