/*
 * Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

/**
 * Purpose of the test is to check that a stopSearch call comes always before a
 * startSearch call.
 */

/**
 * Dummy nsIAutoCompleteInput source that returns
 * the given list of AutoCompleteSearch names.
 *
 * Implements only the methods needed for this test.
 */
function AutoCompleteInput(aSearches) {
  this.searches = aSearches;
}
AutoCompleteInput.prototype = {
  constructor: AutoCompleteInput,
  minResultsForPopup: 0,
  timeout: 10,
  searchParam: "",
  textValue: "hello",
  disableAutoComplete: false,
  completeDefaultIndex: false,
  set popupOpen(val) {}, // ignore
  get popupOpen() {
    return false;
  },
  get searchCount() {
    return this.searches.length;
  },
  getSearchAt(aIndex) {
    return this.searches[aIndex];
  },
  onSearchBegin() {},
  onSearchComplete() {},
  onTextReverted() {},
  onTextEntered() {},
  popup: {
    selectBy() {},
    invalidate() {},
    set selectedIndex(val) {}, // ignore
    get selectedIndex() {
      return -1;
    },
    QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"]),
  },
  QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"]),
};

/**
 * nsIAutoCompleteSearch implementation.
 */
function AutoCompleteSearch(aName) {
  this.name = aName;
}
AutoCompleteSearch.prototype = {
  constructor: AutoCompleteSearch,
  stopSearchInvoked: true,
  startSearch() {
    info("Check stop search has been called");
    Assert.ok(this.stopSearchInvoked);
    this.stopSearchInvoked = false;
  },
  stopSearch() {
    this.stopSearchInvoked = true;
  },
  QueryInterface: ChromeUtils.generateQI([
    "nsIFactory",
    "nsIAutoCompleteSearch",
  ]),
  createInstance(iid) {
    return this.QueryInterface(iid);
  },
};

/**
 * Helper to register an AutoCompleteSearch with the given name.
 * Allows the AutoCompleteController to find the search.
 */
function registerAutoCompleteSearch(aSearch) {
  let name = "@mozilla.org/autocomplete/search;1?name=" + aSearch.name;
  let uuidGenerator = Services.uuid;
  let cid = uuidGenerator.generateUUID();
  let desc = "Test AutoCompleteSearch";
  let componentManager = Components.manager.QueryInterface(
    Ci.nsIComponentRegistrar
  );
  componentManager.registerFactory(cid, desc, name, aSearch);
  // Keep the id on the object so we can unregister later
  aSearch.cid = cid;
}

/**
 * Helper to unregister an AutoCompleteSearch.
 */
function unregisterAutoCompleteSearch(aSearch) {
  let componentManager = Components.manager.QueryInterface(
    Ci.nsIComponentRegistrar
  );
  componentManager.unregisterFactory(aSearch.cid, aSearch);
}

var gTests = [
  function (controller) {
    info("handleText");
    controller.input.textValue = "hel";
    controller.handleText();
  },
  function (controller) {
    info("handleStartComposition");
    controller.handleStartComposition();
    info("handleEndComposition");
    controller.handleEndComposition();
    // an input event always follows compositionend event.
    controller.handleText();
  },
  function (controller) {
    info("handleEscape");
    controller.handleEscape();
  },
  function (controller) {
    info("handleEnter");
    controller.handleEnter(false);
  },
  function (controller) {
    info("handleTab");
    controller.handleTab();
  },

  function (controller) {
    info("handleKeyNavigation");
    // Hardcode KeyboardEvent.DOM_VK_RIGHT, because we can't easily
    // include KeyboardEvent here.
    controller.handleKeyNavigation(0x26 /* KeyboardEvent.DOM_VK_UP */);
  },
];

add_task(async function () {
  // Make an AutoCompleteSearch that always returns nothing
  let search = new AutoCompleteSearch("test");
  registerAutoCompleteSearch(search);

  let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
    Ci.nsIAutoCompleteController
  );

  // Make an AutoCompleteInput that uses our search.
  let input = new AutoCompleteInput([search.name]);
  controller.input = input;

  for (let testFn of gTests) {
    input.onSearchBegin = function () {
      executeSoon(() => testFn(controller));
    };
    let promise = new Promise(resolve => {
      input.onSearchComplete = function () {
        resolve();
      };
    });
    controller.startSearch("hello");
    await promise;
  }

  unregisterAutoCompleteSearch(search);
  controller.stopSearch();
  controller.input = null;
});
