/**
 * Waits for a tab switch.
 */
function waitTabSwitched() {
  return new Promise(resolve => {
    gBrowser.addEventListener(
      "TabSwitchDone",
      function() {
        executeSoon(resolve);
      },
      { once: true }
    );
  });
}

/**
 * Waits a specified number of miliseconds.
 *
 * Usage:
 *    let wait = yield waitForMs(2000);
 *    ok(wait, "2 seconds should now have elapsed");
 *
 * @param aMs the number of miliseconds to wait for
 * @returns a Promise that resolves to true after the time has elapsed
 */
function waitForMs(aMs) {
  return new Promise(resolve => {
    setTimeout(done, aMs);
    function done() {
      resolve(true);
    }
  });
}

/**
 * Platform string helper for nativeVerticalWheelEventMsg
 */
function getPlatform() {
  if (navigator.platform.indexOf("Win") == 0) {
    return "windows";
  }
  if (navigator.platform.indexOf("Mac") == 0) {
    return "mac";
  }
  if (navigator.platform.indexOf("Linux") == 0) {
    return "linux";
  }
  return "unknown";
}

/**
 * Returns a native wheel scroll event id for dom window
 * uitls sendNativeMouseScrollEvent.
 */
function nativeVerticalWheelEventMsg() {
  switch (getPlatform()) {
    case "windows":
      return 0x020a; // WM_MOUSEWHEEL
    case "mac":
      return 0; // value is unused, can be anything
    case "linux":
      return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway
  }
  throw new Error(
    "Native wheel events not supported on platform " + getPlatform()
  );
}

/**
 * Waits for the first dom "scroll" event.
 */
function waitScrollStart(aTarget) {
  return new Promise((resolve, reject) => {
    aTarget.addEventListener(
      "scroll",
      function(event) {
        resolve(event);
      },
      { capture: true, once: true }
    );
  });
}

/**
 * Waits for the last dom "scroll" event which generally indicates
 * a scroll operation is complete. To detect this the helper waits
 * 1 second intervals checking for scroll events from aTarget. If
 * a scroll event is not received during that time, it considers
 * the scroll operation complete. Not super accurate, be careful.
 */
function waitScrollFinish(aTarget) {
  return new Promise((resolve, reject) => {
    let recent = false;
    let count = 0;
    function listener(event) {
      recent = true;
    }
    aTarget.addEventListener("scroll", listener, true);
    setInterval(function() {
      // one second passed and we didn't receive a scroll event.
      if (!recent) {
        aTarget.removeEventListener("scroll", listener, true);
        resolve();
        return;
      }
      recent = false;
      // ten seconds
      if (count > 10) {
        aTarget.removeEventListener("scroll", listener, true);
        reject();
      }
    }, 1000);
  });
}

/**
 * Set a plugin activation state. See nsIPluginTag for
 * supported states. Affected plugin default to the first
 * test plugin.
 */
function setTestPluginEnabledState(aState, aPluginName) {
  let name = aPluginName || "Test Plug-in";
  let resolved = false;
  SpecialPowers.setTestPluginEnabledState(aState, name).then(() => {
    resolved = true;
  });
  SpecialPowers.Services.tm.spinEventLoopUntil(() => resolved);
}

/**
 * Returns the chrome side nsIPluginTag for this plugin, helper for
 * setTestPluginEnabledState.
 */
function getTestPlugin(aName) {
  let pluginName = aName || "Test Plug-in";
  let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
  let tags = ph.getPluginTags();

  // Find the test plugin
  for (let i = 0; i < tags.length; i++) {
    if (tags[i].name == pluginName) {
      return tags[i];
    }
  }
  ok(false, "Unable to find plugin");
  return null;
}
