/*
 * Copyright (C) 2010, 2013 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "third_party/blink/renderer/platform/language.h"

#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/thread_specific.h"

namespace blink {

namespace {

static String CanonicalizeLanguageIdentifier(const String& language_code) {
  String copied_code = language_code;
  // Platform::defaultLocale() might provide a language code with '_'.
  copied_code.Replace('_', '-');
  return copied_code;
}

// Main thread static AtomicString. This can be safely shared across threads.
const AtomicString* g_platform_language = nullptr;

const AtomicString& PlatformLanguage() {
  DCHECK(g_platform_language->Impl()->IsStatic())
      << "global language string is used from multiple threads, and must be "
         "static";
  return *g_platform_language;
}

Vector<AtomicString>& PreferredLanguagesOverride() {
  DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<Vector<AtomicString>>,
                                  thread_specific_languages, ());
  return *thread_specific_languages;
}

}  // namespace

void InitializePlatformLanguage() {
  DCHECK(IsMainThread());
  DEFINE_STATIC_LOCAL(
      // We add the platform language as a static string for two reasons:
      // 1. it can be shared across threads.
      // 2. since this is done very early on, we don't want to accidentally
      //    collide with a hard coded static string (like "fr" on SVG).
      const AtomicString, platform_language, (([]() {
        String canonicalized = CanonicalizeLanguageIdentifier(
            Platform::Current()->DefaultLocale());
        if (!canonicalized.empty()) {
          StringImpl* impl =
              StringImpl::CreateStatic(base::as_chars(canonicalized.Span8()));
          return AtomicString(impl);
        }
        return AtomicString();
      })()));

  g_platform_language = &platform_language;
}

void OverrideUserPreferredLanguagesForTesting(
    const Vector<AtomicString>& override) {
  Vector<AtomicString>& canonicalized = PreferredLanguagesOverride();
  canonicalized.resize(0);
  canonicalized.reserve(override.size());
  for (const auto& lang : override)
    canonicalized.push_back(CanonicalizeLanguageIdentifier(lang));
  Locale::ResetDefaultLocale();
}

AtomicString DefaultLanguage() {
  Vector<AtomicString>& override = PreferredLanguagesOverride();
  if (!override.empty())
    return override[0];
  return PlatformLanguage();
}

Vector<AtomicString> UserPreferredLanguages() {
  Vector<AtomicString>& override = PreferredLanguagesOverride();
  if (!override.empty())
    return override;

  Vector<AtomicString> languages;
  languages.ReserveInitialCapacity(1);
  languages.push_back(PlatformLanguage());
  return languages;
}

wtf_size_t IndexOfBestMatchingLanguageInList(
    const AtomicString& language,
    const Vector<AtomicString>& language_list) {
  AtomicString language_without_locale_match;
  AtomicString language_match_but_not_locale;
  wtf_size_t language_without_locale_match_index = 0;
  wtf_size_t language_match_but_not_locale_match_index = 0;
  bool can_match_language_only =
      (language.length() == 2 ||
       (language.length() >= 3 && language[2] == '-'));

  for (wtf_size_t i = 0; i < language_list.size(); ++i) {
    String canonicalized_language_from_list =
        CanonicalizeLanguageIdentifier(language_list[i]);

    if (language == canonicalized_language_from_list)
      return i;

    if (can_match_language_only &&
        canonicalized_language_from_list.length() >= 2) {
      if (language[0] == canonicalized_language_from_list[0] &&
          language[1] == canonicalized_language_from_list[1]) {
        if (!language_without_locale_match.length() &&
            canonicalized_language_from_list.length() == 2) {
          language_without_locale_match = language_list[i];
          language_without_locale_match_index = i;
        }
        if (!language_match_but_not_locale.length() &&
            canonicalized_language_from_list.length() >= 3) {
          language_match_but_not_locale = language_list[i];
          language_match_but_not_locale_match_index = i;
        }
      }
    }
  }

  // If we have both a language-only match and a languge-but-not-locale match,
  // return the languge-only match as is considered a "better" match. For
  // example, if the list provided has both "en-GB" and "en" and the user
  // prefers "en-US" we will return "en".
  if (language_without_locale_match.length())
    return language_without_locale_match_index;

  if (language_match_but_not_locale.length())
    return language_match_but_not_locale_match_index;

  return language_list.size();
}

}  // namespace blink
