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

const osPrefs = Cc["@mozilla.org/intl/ospreferences;1"].getService(
  Ci.mozIOSPreferences
);

const localeService = Services.locale;

/**
 * Make sure the locale service can be instantiated.
 */

add_test(function test_defaultLocale() {
  const defaultLocale = localeService.defaultLocale;
  Assert.notStrictEqual(defaultLocale.length, 0, "Default locale is not empty");
  run_next_test();
});

add_test(function test_lastFallbackLocale() {
  const lastFallbackLocale = localeService.lastFallbackLocale;
  Assert.strictEqual(
    lastFallbackLocale,
    "en-US",
    "Last fallback locale is en-US"
  );
  run_next_test();
});

add_test(function test_appLocalesAsLangTags() {
  const appLocale = localeService.appLocaleAsLangTag;
  Assert.notEqual(appLocale, "", "appLocale is non-empty");

  const appLocales = localeService.appLocalesAsLangTags;
  Assert.ok(Array.isArray(appLocales), "appLocales returns an array");

  Assert.equal(
    appLocale,
    appLocales[0],
    "appLocale matches first entry in appLocales"
  );

  const enUSLocales = appLocales.filter(loc => loc === "en-US");
  Assert.equal(enUSLocales.length, 1, "en-US is present exactly one time");

  run_next_test();
});

const PREF_REQUESTED_LOCALES = "intl.locale.requested";
const REQ_LOC_CHANGE_EVENT = "intl:requested-locales-changed";

add_test(function test_requestedLocales() {
  const requestedLocales = localeService.requestedLocales;
  Assert.ok(
    Array.isArray(requestedLocales),
    "requestedLocales returns an array"
  );

  run_next_test();
});

/**
 * In this test we verify that after we set an observer on the LocaleService
 * event for requested locales change, it will be fired when the
 * pref for matchOS is set to true.
 *
 * Then, we test that when the matchOS is set to true, we will retrieve
 * OS locale from requestedLocales.
 */
add_test(function test_requestedLocales_matchOS() {
  do_test_pending();

  Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "ar-IR");

  const observer = {
    observe(aSubject, aTopic) {
      switch (aTopic) {
        case REQ_LOC_CHANGE_EVENT: {
          const reqLocs = localeService.requestedLocales;
          Assert.strictEqual(reqLocs[0], osPrefs.systemLocale);
          Services.obs.removeObserver(observer, REQ_LOC_CHANGE_EVENT);
          do_test_finished();
        }
      }
    },
  };

  Services.obs.addObserver(observer, REQ_LOC_CHANGE_EVENT);
  Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "");

  run_next_test();
});

/**
 * In this test we verify that after we set an observer on the LocaleService
 * event for requested locales change, it will be fired when the
 * pref for browser UI locale changes.
 */
add_test(function test_requestedLocales_onChange() {
  do_test_pending();

  Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "ar-IR");

  const observer = {
    observe(aSubject, aTopic) {
      switch (aTopic) {
        case REQ_LOC_CHANGE_EVENT: {
          const reqLocs = localeService.requestedLocales;
          Assert.strictEqual(reqLocs[0], "sr-RU");
          Services.obs.removeObserver(observer, REQ_LOC_CHANGE_EVENT);
          do_test_finished();
        }
      }
    },
  };

  Services.obs.addObserver(observer, REQ_LOC_CHANGE_EVENT);
  Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "sr-RU");

  run_next_test();
});

add_test(function test_requestedLocale() {
  Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "tlh");

  let requestedLocale = localeService.requestedLocale;
  Assert.strictEqual(
    requestedLocale,
    "tlh",
    "requestedLocale returns the right value"
  );

  Services.prefs.clearUserPref(PREF_REQUESTED_LOCALES);

  run_next_test();
});

add_test(function test_requestedLocales() {
  localeService.requestedLocales = ["de-AT", "de-DE", "de-CH"];

  let locales = localeService.requestedLocales;
  Assert.strictEqual(locales[0], "de-AT");
  Assert.strictEqual(locales[1], "de-DE");
  Assert.strictEqual(locales[2], "de-CH");

  run_next_test();
});

add_test(function test_isAppLocaleRTL() {
  Assert.strictEqual(typeof localeService.isAppLocaleRTL, "boolean");

  run_next_test();
});

add_test(function test_isAppLocaleRTL_pseudo() {
  let avLocales = localeService.availableLocales;
  let reqLocales = localeService.requestedLocales;

  localeService.availableLocales = ["en-US"];
  localeService.requestedLocales = ["en-US"];
  Services.prefs.setCharPref("intl.l10n.pseudo", "");

  Assert.strictEqual(localeService.isAppLocaleRTL, false);

  Services.prefs.setCharPref("intl.l10n.pseudo", "bidi");
  Assert.strictEqual(localeService.isAppLocaleRTL, true);

  Services.prefs.setCharPref("intl.l10n.pseudo", "accented");
  Assert.strictEqual(localeService.isAppLocaleRTL, false);

  // Clean up
  localeService.availableLocales = avLocales;
  localeService.requestedLocales = reqLocales;
  Services.prefs.clearUserPref("intl.l10n.pseudo");

  run_next_test();
});

add_test(function test_packagedLocales() {
  const locales = localeService.packagedLocales;
  Assert.notStrictEqual(locales.length, 0, "Packaged locales are empty");
  run_next_test();
});

add_test(function test_availableLocales() {
  const avLocales = localeService.availableLocales;
  localeService.availableLocales = ["und", "ar-IR"];

  let locales = localeService.availableLocales;
  Assert.equal(locales.length, 2);
  Assert.strictEqual(locales[0], "und");
  Assert.strictEqual(locales[1], "ar-IR");

  localeService.availableLocales = avLocales;

  run_next_test();
});

/**
 * This test verifies that all values coming from the pref are sanitized.
 */
add_test(function test_requestedLocales_sanitize() {
  Services.prefs.setStringPref(
    PREF_REQUESTED_LOCALES,
    "de,2,#$@#,pl,ąó,!a2,DE-at,,;"
  );

  let locales = localeService.requestedLocales;
  Assert.equal(locales[0], "de");
  Assert.equal(locales[1], "pl");
  Assert.equal(locales[2], "de-AT");
  Assert.equal(locales.length, 3);

  Services.prefs.clearUserPref(PREF_REQUESTED_LOCALES);

  run_next_test();
});

add_test(function test_handle_ja_JP_mac() {
  const bkpAvLocales = localeService.availableLocales;

  localeService.availableLocales = ["ja-JP-mac", "en-US"];

  localeService.requestedLocales = ["ja-JP-mac"];

  let reqLocales = localeService.requestedLocales;
  Assert.equal(reqLocales[0], "ja-JP-macos");

  let avLocales = localeService.availableLocales;
  Assert.equal(avLocales[0], "ja-JP-macos");

  let appLocales = localeService.appLocalesAsBCP47;
  Assert.equal(appLocales[0], "ja-JP-macos");

  let appLocalesAsLT = localeService.appLocalesAsLangTags;
  Assert.equal(appLocalesAsLT[0], "ja-JP-mac");

  Assert.equal(localeService.appLocaleAsLangTag, "ja-JP-mac");

  localeService.availableLocales = bkpAvLocales;

  run_next_test();
});

add_test(function test_locale_service_glue() {
  // On Android, intl.accept_languages is set with a user pref value,
  // so we clear that in order to test the default value handling.
  let origAcceptLanguages = null;
  if (Services.prefs.prefHasUserValue("intl.accept_languages")) {
    origAcceptLanguages = Services.prefs.getCharPref("intl.accept_languages");
    Services.prefs.clearUserPref("intl.accept_languages");
  }
  const origAvLocales = localeService.availableLocales;

  localeService.availableLocales = ["en-US"];
  localeService.requestedLocales = ["en-US"];
  Assert.equal(localeService.acceptLanguages, "en-US, en");
  Assert.equal(localeService.ellipsis, "…");
  Assert.equal(localeService.alwaysAppendAccesskeys, false);
  Assert.equal(localeService.insertSeparatorBeforeAccesskeys, true);

  localeService.availableLocales = ["ca-valencia", "en-US"];
  localeService.requestedLocales = ["ca-valencia"];
  Assert.equal(localeService.acceptLanguages, "ca-valencia, ca, en-US, en");

  localeService.availableLocales = ["hi-IN", "en-US"];
  localeService.requestedLocales = ["hi-IN"];
  Assert.equal(localeService.acceptLanguages, "hi-IN, hi, en-US, en");

  localeService.availableLocales = ["ja-JP-mac", "en-US"];
  localeService.requestedLocales = ["ja-JP-mac"];
  Assert.equal(localeService.acceptLanguages, "ja, en-US, en");
  Assert.equal(localeService.ellipsis, "...");
  Assert.equal(localeService.alwaysAppendAccesskeys, true);
  Assert.equal(localeService.insertSeparatorBeforeAccesskeys, false);

  localeService.availableLocales = ["zh-CN", "en-US"];
  localeService.requestedLocales = ["zh-CN"];
  Assert.equal(
    localeService.acceptLanguages,
    "zh-CN, zh, zh-TW, zh-HK, en-US, en"
  );
  Assert.equal(localeService.ellipsis, "…");
  Assert.equal(localeService.alwaysAppendAccesskeys, false);
  Assert.equal(localeService.insertSeparatorBeforeAccesskeys, false);

  localeService.availableLocales = ["zh-TW", "en-US"];
  localeService.requestedLocales = ["zh-TW"];
  Assert.equal(localeService.acceptLanguages, "zh-TW, zh, en-US, en");
  Assert.equal(localeService.ellipsis, "…");
  Assert.equal(localeService.alwaysAppendAccesskeys, true);
  Assert.equal(localeService.insertSeparatorBeforeAccesskeys, true);

  if (origAcceptLanguages) {
    Services.prefs.setCharPref("intl.accept_languages", origAcceptLanguages);
  }
  localeService.availableLocales = origAvLocales;

  run_next_test();
});

add_test(function test_font_language_group() {
  const origAvLocales = localeService.availableLocales;

  for (const [locales, expGroup] of [
    [["ar", "ckb", "fa", "ks", "skr"], "ar"],
    [["az", "mai", "sat", "vi"], "x-unicode"],
    [["be", "bg", "kk", "mk", "ru", "sah", "sr", "tg", "uk"], "x-cyrillic"],
    [["bn"], "x-beng"],
    [["bo"], "x-tibt"],
    [["brx", "hi", "mr", "ne"], "x-devanagari"],
    [["el"], "el"],
    [["gu-IN"], "x-gujr"],
    [["he"], "he"],
    [["hy", "hye", "xcl"], "x-armn"],
    [["ja"], "ja"],
    [["kn"], "x-knda"],
    [["ko"], "ko"],
    [["lo", "th"], "th"],
    [["ml"], "x-mlym"],
    [["si"], "x-sinh"],
    [["son"], "x-western"],
    [["te"], "x-telu"],
    [["th"], "th"],
    [["zh-CN"], "zh-CN"],
    [["zh-TW"], "zh-TW"],
  ]) {
    for (const locale of locales) {
      localeService.availableLocales = [locale, "en-US"];
      localeService.requestedLocales = [locale];
      Assert.equal(localeService.fontLanguageGroup, expGroup, locale);
    }
  }

  localeService.availableLocales = origAvLocales;

  run_next_test();
});

add_test(function test_url_fixup_suffix() {
  const origAvLocales = localeService.availableLocales;

  for (const [locales, expSuffix] of [
    [["be"], ".by"],
    [["cs"], ".cz"],
    [["da"], ".dk"],
    [["nb-NO", "nn-NO"], ".no"],
    [["sk"], ".sk"],
    [["en-US", "fi"], ".com"],
  ]) {
    for (const locale of locales) {
      localeService.availableLocales = [locale, "en-US"];
      localeService.requestedLocales = [locale];
      Assert.equal(localeService.urlFixupSuffix, expSuffix, locale);
    }
  }

  localeService.availableLocales = origAvLocales;

  run_next_test();
});

registerCleanupFunction(() => {
  Services.prefs.clearUserPref(PREF_REQUESTED_LOCALES);
});
