// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/spellchecker/spellcheck_service.h"

#include <ostream>

#include "base/command_line.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/supports_user_data.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/spellcheck/browser/pref_names.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/test/test_browser_thread.h"
#include "testing/gtest/include/gtest/gtest.h"

struct TestCase {
  TestCase(const std::string& accept_languages,
           const std::string& unsplit_spellcheck_dictionaries,
           const std::string& unsplit_expected_languages,
           const std::string& unsplit_expected_languages_used_for_spellcheck)
      : accept_languages(accept_languages),
        spellcheck_dictionaries(
            base::SplitString(unsplit_spellcheck_dictionaries,
                              ",",
                              base::TRIM_WHITESPACE,
                              base::SPLIT_WANT_ALL)) {
    std::vector<std::string> languages =
        base::SplitString(unsplit_expected_languages, ",",
                          base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);

    std::vector<std::string> used_for_spellcheck =
        base::SplitString(unsplit_expected_languages_used_for_spellcheck, ",",
                          base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);

    SpellcheckService::Dictionary dictionary;
    for (const auto& language : languages) {
      dictionary.language = language;
      dictionary.used_for_spellcheck =
          std::find(used_for_spellcheck.begin(), used_for_spellcheck.end(),
                    language) != used_for_spellcheck.end();
      expected_dictionaries.push_back(dictionary);
    }
  }

  ~TestCase() {}

  std::string accept_languages;
  std::vector<std::string> spellcheck_dictionaries;
  std::vector<SpellcheckService::Dictionary> expected_dictionaries;
};

bool operator==(const SpellcheckService::Dictionary& lhs,
                const SpellcheckService::Dictionary& rhs) {
  return lhs.language == rhs.language &&
         lhs.used_for_spellcheck == rhs.used_for_spellcheck;
}

std::ostream& operator<<(std::ostream& out,
                         const SpellcheckService::Dictionary& dictionary) {
  out << "{\"" << dictionary.language << "\", used_for_spellcheck="
      << (dictionary.used_for_spellcheck ? "true " : "false") << "}";
  return out;
}

std::ostream& operator<<(std::ostream& out, const TestCase& test_case) {
  out << "prefs::kAcceptLanguages=[" << test_case.accept_languages
      << "], prefs::kSpellCheckDictionaries=["
      << base::JoinString(test_case.spellcheck_dictionaries, ",")
      << "], expected=[";
  for (const auto& dictionary : test_case.expected_dictionaries) {
    out << dictionary << ",";
  }
  out << "]";
  return out;
}

class SpellcheckServiceUnitTest : public testing::TestWithParam<TestCase> {
 public:
  SpellcheckServiceUnitTest()
      : ui_thread_(content::BrowserThread::UI, &message_loop_) {
    user_prefs::UserPrefs::Set(&context_, &prefs_);
  }
  ~SpellcheckServiceUnitTest() override {}

  void SetUp() override {
    prefs()->registry()->RegisterListPref(
        spellcheck::prefs::kSpellCheckDictionaries);
    prefs()->registry()->RegisterStringPref(prefs::kAcceptLanguages,
                                            std::string());
  }

  base::SupportsUserData* context() { return &context_; }
  TestingPrefServiceSimple* prefs() { return &prefs_; }

 private:
  struct : public base::SupportsUserData {
  } context_;
  TestingPrefServiceSimple prefs_;
  base::MessageLoop message_loop_;
  content::TestBrowserThread ui_thread_;

  DISALLOW_COPY_AND_ASSIGN(SpellcheckServiceUnitTest);
};

INSTANTIATE_TEST_CASE_P(
    TestCases,
    SpellcheckServiceUnitTest,
    testing::Values(
        TestCase("en,aa", "aa", "", ""),
        TestCase("en,en-JP,fr,aa", "fr", "fr", "fr"),
        TestCase("en,en-JP,fr,zz,en-US", "fr", "fr,en-US", "fr"),
        TestCase("en,en-US,en-GB", "en-GB", "en-US,en-GB", "en-GB"),
        TestCase("en,en-US,en-AU", "en-AU", "en-US,en-AU", "en-AU"),
        TestCase("en,en-US,en-AU", "en-US", "en-US,en-AU", "en-US"),
        TestCase("en,en-US", "en-US", "en-US", "en-US"),
        TestCase("en,en-US,fr", "en-US", "en-US,fr", "en-US"),
        TestCase("en,fr,en-US,en-AU", "en-US,fr", "fr,en-US,en-AU", "fr,en-US"),
        TestCase("en-US,en", "en-US", "en-US", "en-US"),
        TestCase("hu-HU,hr-HR", "hr", "hu,hr", "hr")));

TEST_P(SpellcheckServiceUnitTest, GetDictionaries) {
  prefs()->SetString(prefs::kAcceptLanguages, GetParam().accept_languages);
  base::ListValue spellcheck_dictionaries;
  spellcheck_dictionaries.AppendStrings(GetParam().spellcheck_dictionaries);
  prefs()->Set(spellcheck::prefs::kSpellCheckDictionaries,
               spellcheck_dictionaries);

  std::vector<SpellcheckService::Dictionary> dictionaries;
  SpellcheckService::GetDictionaries(context(), &dictionaries);

  EXPECT_EQ(GetParam().expected_dictionaries, dictionaries);
}
