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

// tests the translation infobar, using a fake 'Translation' implementation.

let tmp = {};
Cu.import("resource:///modules/translation/Translation.jsm", tmp);
let {Translation} = tmp;

const kShowUIPref = "browser.translation.ui.show";

function waitForCondition(condition, nextTest, errorMsg) {
  var tries = 0;
  var interval = setInterval(function() {
    if (tries >= 30) {
      ok(false, errorMsg);
      moveOn();
    }
    var conditionPassed;
    try {
      conditionPassed = condition();
    } catch (e) {
      ok(false, e + "\n" + e.stack);
      conditionPassed = false;
    }
    if (conditionPassed) {
      moveOn();
    }
    tries++;
  }, 100);
  var moveOn = function() { clearInterval(interval); nextTest(); };
}

var TranslationStub = {
  translate: function(aFrom, aTo) {
    this.state = Translation.STATE_TRANSLATING;
    this.translatedFrom = aFrom;
    this.translatedTo = aTo;
  },

  _reset: function() {
    this.translatedFrom = "";
    this.translatedTo = "";
  },

  failTranslation: function() {
    this.state = Translation.STATE_ERROR;
    this._reset();
  },

  finishTranslation: function() {
    this.showTranslatedContent();
    this.state = Translation.STATE_TRANSLATED;
    this._reset();
  }
};

function showTranslationUI(aDetectedLanguage) {
  let browser = gBrowser.selectedBrowser;
  Translation.documentStateReceived(browser, {state: Translation.STATE_OFFER,
                                              originalShown: true,
                                              detectedLanguage: aDetectedLanguage});
  let ui = browser.translationUI;
  for (let name of ["translate", "_reset", "failTranslation", "finishTranslation"])
    ui[name] = TranslationStub[name];
  return ui.notificationBox.getNotificationWithValue("translation");
}

function hasTranslationInfoBar() {
  return !!gBrowser.getNotificationBox().getNotificationWithValue("translation");
}

function test() {
  waitForExplicitFinish();

  Services.prefs.setBoolPref(kShowUIPref, true);
  let tab = gBrowser.addTab();
  gBrowser.selectedTab = tab;
  tab.linkedBrowser.addEventListener("load", function onload() {
    tab.linkedBrowser.removeEventListener("load", onload, true);
    TranslationStub.browser = gBrowser.selectedBrowser;
    registerCleanupFunction(function () {
      gBrowser.removeTab(tab);
      Services.prefs.clearUserPref(kShowUIPref);
    });
    run_tests(() => {
      finish();
    });
  }, true);

  content.location = "data:text/plain,test page";
}

function checkURLBarIcon(aExpectTranslated = false) {
  is(!PopupNotifications.getNotification("translate"), aExpectTranslated,
     "translate icon " + (aExpectTranslated ? "not " : "") + "shown");
  is(!!PopupNotifications.getNotification("translated"), aExpectTranslated,
     "translated icon " + (aExpectTranslated ? "" : "not ") + "shown");
}

function run_tests(aFinishCallback) {
  info("Show an info bar saying the current page is in French");
  let notif = showTranslationUI("fr");
  is(notif.state, Translation.STATE_OFFER, "the infobar is offering translation");
  is(notif._getAnonElt("detectedLanguage").value, "fr", "The detected language is displayed");
  checkURLBarIcon();

  info("Click the 'Translate' button");
  notif._getAnonElt("translate").click();
  is(notif.state, Translation.STATE_TRANSLATING, "the infobar is in the translating state");
  ok(!!notif.translation.translatedFrom, "Translation.translate has been called");
  is(notif.translation.translatedFrom, "fr", "from language correct");
  is(notif.translation.translatedTo, Translation.defaultTargetLanguage, "from language correct");
  checkURLBarIcon();

  info("Make the translation fail and check we are in the error state.");
  notif.translation.failTranslation();
  is(notif.state, Translation.STATE_ERROR, "infobar in the error state");
  checkURLBarIcon();

  info("Click the try again button");
  notif._getAnonElt("tryAgain").click();
  is(notif.state, Translation.STATE_TRANSLATING, "infobar in the translating state");
  ok(!!notif.translation.translatedFrom, "Translation.translate has been called");
  is(notif.translation.translatedFrom, "fr", "from language correct");
  is(notif.translation.translatedTo, Translation.defaultTargetLanguage, "from language correct");
  checkURLBarIcon();

  info("Make the translation succeed and check we are in the 'translated' state.");
  notif.translation.finishTranslation();
  is(notif.state, Translation.STATE_TRANSLATED, "infobar in the translated state");
  checkURLBarIcon(true);

  info("Test 'Show original' / 'Show Translation' buttons.");
  // First check 'Show Original' is visible and 'Show Translation' is hidden.
  ok(!notif._getAnonElt("showOriginal").hidden, "'Show Original' button visible");
  ok(notif._getAnonElt("showTranslation").hidden, "'Show Translation' button hidden");
  // Click the button.
  notif._getAnonElt("showOriginal").click();
  // Check that the url bar icon shows the original content is displayed.
  checkURLBarIcon();
  // And the 'Show Translation' button is now visible.
  ok(notif._getAnonElt("showOriginal").hidden, "'Show Original' button hidden");
  ok(!notif._getAnonElt("showTranslation").hidden, "'Show Translation' button visible");
  // Click the 'Show Translation' button
  notif._getAnonElt("showTranslation").click();
  // Check that the url bar icon shows the page is translated.
  checkURLBarIcon(true);
  // Check that the 'Show Original' button is visible again.
  ok(!notif._getAnonElt("showOriginal").hidden, "'Show Original' button visible");
  ok(notif._getAnonElt("showTranslation").hidden, "'Show Translation' button hidden");

  info("Check that changing the source language causes a re-translation");
  let from = notif._getAnonElt("fromLanguage");
  from.value = "es";
  from.doCommand();
  is(notif.state, Translation.STATE_TRANSLATING, "infobar in the translating state");
  ok(!!notif.translation.translatedFrom, "Translation.translate has been called");
  is(notif.translation.translatedFrom, "es", "from language correct");
  is(notif.translation.translatedTo, Translation.defaultTargetLanguage, "to language correct");
  // We want to show the 'translated' icon while re-translating,
  // because we are still displaying the previous translation.
  checkURLBarIcon(true);
  notif.translation.finishTranslation();
  checkURLBarIcon(true);

  info("Check that changing the target language causes a re-translation");
  let to = notif._getAnonElt("toLanguage");
  to.value = "pl";
  to.doCommand();
  is(notif.state, Translation.STATE_TRANSLATING, "infobar in the translating state");
  ok(!!notif.translation.translatedFrom, "Translation.translate has been called");
  is(notif.translation.translatedFrom, "es", "from language correct");
  is(notif.translation.translatedTo, "pl", "to language correct");
  checkURLBarIcon(true);
  notif.translation.finishTranslation();
  checkURLBarIcon(true);

  // Cleanup.
  notif.close();

  info("Reopen the info bar to check that it's possible to override the detected language.");
  notif = showTranslationUI("fr");
  is(notif.state, Translation.STATE_OFFER, "the infobar is offering translation");
  is(notif._getAnonElt("detectedLanguage").value, "fr", "The detected language is displayed");
  // Change the language and click 'Translate'
  notif._getAnonElt("detectedLanguage").value = "ja";
  notif._getAnonElt("translate").click();
  is(notif.state, Translation.STATE_TRANSLATING, "the infobar is in the translating state");
  ok(!!notif.translation.translatedFrom, "Translation.translate has been called");
  is(notif.translation.translatedFrom, "ja", "from language correct");
  notif.close();

  info("Reopen to check the 'Not Now' button closes the notification.");
  notif = showTranslationUI("fr");
  let notificationBox = gBrowser.getNotificationBox();
  is(hasTranslationInfoBar(), true, "there's a 'translate' notification");
  notif._getAnonElt("notNow").click();
  is(hasTranslationInfoBar(), false, "no 'translate' notification after clicking 'not now'");

  info("Reopen to check the url bar icon closes the notification.");
  notif = showTranslationUI("fr");
  is(hasTranslationInfoBar(), true, "there's a 'translate' notification");
  PopupNotifications.getNotification("translate").anchorElement.click();
  is(hasTranslationInfoBar(), false, "no 'translate' notification after clicking the url bar icon");

  info("Check that clicking the url bar icon reopens the info bar");
  checkURLBarIcon();
  // Clicking the anchor element causes a 'showing' event to be sent
  // asynchronously to our callback that will then show the infobar.
  PopupNotifications.getNotification("translate").anchorElement.click();
  waitForCondition(hasTranslationInfoBar, () => {
    ok(hasTranslationInfoBar(), "there's a 'translate' notification");
    aFinishCallback();
  }, "timeout waiting for the info bar to reappear");
}
