// 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/ui/webui/options/font_settings_handler.h"

#include <stddef.h>

#include <string>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/i18n/rtl.h"
#include "base/macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/character_encoding.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/webui/options/font_settings_utils.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/font_list_async.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/web_ui.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_urls.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"

#if defined(OS_WIN)
#include "ui/gfx/font.h"
#include "ui/gfx/platform_font_win.h"
#endif

namespace {

// Returns the localized name of a font so that settings can find it within the
// list of system fonts. On Windows, the list of system fonts has names only
// for the system locale, but the pref value may be in the English name.
std::string MaybeGetLocalizedFontName(const std::string& font_name) {
#if defined(OS_WIN)
  gfx::Font font(font_name, 12);  // dummy font size
  return static_cast<gfx::PlatformFontWin*>(font.platform_font())->
      GetLocalizedFontName();
#else
  return font_name;
#endif
}

const char kAdvancedFontSettingsExtensionId[] =
    "caclkomlalccbpcdllchkeecicepbmbm";

}  // namespace


namespace options {

FontSettingsHandler::FontSettingsHandler()
    : extension_registry_observer_(this),
      weak_ptr_factory_(this) {
}

FontSettingsHandler::~FontSettingsHandler() {
}

void FontSettingsHandler::GetLocalizedValues(
    base::DictionaryValue* localized_strings) {
  DCHECK(localized_strings);

  static OptionsStringResource resources[] = {
    { "fontSettingsStandard",
      IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_STANDARD_LABEL },
    { "fontSettingsSerif",
      IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_SERIF_LABEL },
    { "fontSettingsSansSerif",
      IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_SANS_SERIF_LABEL },
    { "fontSettingsFixedWidth",
      IDS_FONT_LANGUAGE_SETTING_FONT_SELECTOR_FIXED_WIDTH_LABEL },
    { "fontSettingsMinimumSize",
      IDS_FONT_LANGUAGE_SETTING_MINIMUM_FONT_SIZE_TITLE },
    { "fontSettingsEncoding",
      IDS_FONT_LANGUAGE_SETTING_FONT_SUB_DIALOG_ENCODING_TITLE },
    { "fontSettingsSizeTiny",
      IDS_FONT_LANGUAGE_SETTING_FONT_SIZE_TINY },
    { "fontSettingsSizeHuge",
      IDS_FONT_LANGUAGE_SETTING_FONT_SIZE_HUGE },
    { "fontSettingsLoremIpsum",
      IDS_FONT_LANGUAGE_SETTING_LOREM_IPSUM },
    { "advancedFontSettingsOptions",
      IDS_FONT_LANGUAGE_SETTING_ADVANCED_FONT_SETTINGS_OPTIONS }
  };

  RegisterStrings(localized_strings, resources, arraysize(resources));
  RegisterTitle(localized_strings, "fontSettingsPage",
                IDS_FONT_LANGUAGE_SETTING_FONT_TAB_TITLE);
  localized_strings->SetString("fontSettingsPlaceholder",
      l10n_util::GetStringUTF16(
          IDS_FONT_LANGUAGE_SETTING_PLACEHOLDER));

  GURL install_url(extension_urls::GetWebstoreItemDetailURLPrefix());
  localized_strings->SetString("advancedFontSettingsInstall",
      l10n_util::GetStringFUTF16(
          IDS_FONT_LANGUAGE_SETTING_ADVANCED_FONT_SETTINGS_INSTALL,
          base::UTF8ToUTF16(
              install_url.Resolve(kAdvancedFontSettingsExtensionId).spec())));
}

void FontSettingsHandler::InitializeHandler() {
  Profile* profile = Profile::FromWebUI(web_ui());
  extension_registry_observer_.Add(extensions::ExtensionRegistry::Get(profile));
}

void FontSettingsHandler::InitializePage() {
  DCHECK(web_ui());
  SetUpStandardFontSample();
  SetUpSerifFontSample();
  SetUpSansSerifFontSample();
  SetUpFixedFontSample();
  SetUpMinimumFontSample();
  NotifyAdvancedFontSettingsAvailability();
}

void FontSettingsHandler::RegisterMessages() {
  // Perform validation for saved fonts.
  PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
  FontSettingsUtilities::ValidateSavedFonts(pref_service);

  // Register for preferences that we need to observe manually.
  font_encoding_.Init(prefs::kDefaultCharset, pref_service);

  standard_font_.Init(prefs::kWebKitStandardFontFamily,
                      pref_service,
                      base::Bind(&FontSettingsHandler::SetUpStandardFontSample,
                                 base::Unretained(this)));
  serif_font_.Init(prefs::kWebKitSerifFontFamily,
                   pref_service,
                   base::Bind(&FontSettingsHandler::SetUpSerifFontSample,
                              base::Unretained(this)));
  sans_serif_font_.Init(
      prefs::kWebKitSansSerifFontFamily,
      pref_service,
      base::Bind(&FontSettingsHandler::SetUpSansSerifFontSample,
                 base::Unretained(this)));

  base::Closure callback = base::Bind(
      &FontSettingsHandler::SetUpFixedFontSample, base::Unretained(this));

  fixed_font_.Init(prefs::kWebKitFixedFontFamily, pref_service, callback);
  default_fixed_font_size_.Init(prefs::kWebKitDefaultFixedFontSize,
                                pref_service, callback);
  default_font_size_.Init(
      prefs::kWebKitDefaultFontSize,
      pref_service,
      base::Bind(&FontSettingsHandler::OnWebKitDefaultFontSizeChanged,
                 base::Unretained(this)));
  minimum_font_size_.Init(
      prefs::kWebKitMinimumFontSize,
      pref_service,
      base::Bind(&FontSettingsHandler::SetUpMinimumFontSample,
                 base::Unretained(this)));

  web_ui()->RegisterMessageCallback("fetchFontsData",
      base::Bind(&FontSettingsHandler::HandleFetchFontsData,
                 base::Unretained(this)));
  web_ui()->RegisterMessageCallback("openAdvancedFontSettingsOptions",
      base::Bind(&FontSettingsHandler::HandleOpenAdvancedFontSettingsOptions,
                 base::Unretained(this)));
}

void FontSettingsHandler::OnExtensionLoaded(
    content::BrowserContext* browser_context,
    const extensions::Extension* extension) {
  NotifyAdvancedFontSettingsAvailability();
}

void FontSettingsHandler::OnExtensionUnloaded(
    content::BrowserContext* browser_context,
    const extensions::Extension* extension,
    extensions::UnloadedExtensionInfo::Reason reason) {
  NotifyAdvancedFontSettingsAvailability();
}

void FontSettingsHandler::HandleFetchFontsData(const base::ListValue* args) {
  content::GetFontListAsync(
      base::Bind(&FontSettingsHandler::FontsListHasLoaded,
                 weak_ptr_factory_.GetWeakPtr()));
}

void FontSettingsHandler::FontsListHasLoaded(
    scoped_ptr<base::ListValue> list) {
  // Selects the directionality for the fonts in the given list.
  for (size_t i = 0; i < list->GetSize(); i++) {
    base::ListValue* font;
    bool has_font = list->GetList(i, &font);
    DCHECK(has_font);
    base::string16 value;
    bool has_value = font->GetString(1, &value);
    DCHECK(has_value);
    bool has_rtl_chars = base::i18n::StringContainsStrongRTLChars(value);
    font->Append(new base::StringValue(has_rtl_chars ? "rtl" : "ltr"));
  }

  base::ListValue encoding_list;
  const std::vector<CharacterEncoding::EncodingInfo>* encodings;
  PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
  encodings = CharacterEncoding::GetCurrentDisplayEncodings(
      g_browser_process->GetApplicationLocale(),
      pref_service->GetString(prefs::kStaticEncodings),
      pref_service->GetString(prefs::kRecentlySelectedEncoding));
  DCHECK(encodings);
  DCHECK(!encodings->empty());

  std::vector<CharacterEncoding::EncodingInfo>::const_iterator it;
  for (it = encodings->begin(); it != encodings->end(); ++it) {
    base::ListValue* option = new base::ListValue();
    if (it->encoding_id) {
      int cmd_id = it->encoding_id;
      std::string encoding =
      CharacterEncoding::GetCanonicalEncodingNameByCommandId(cmd_id);
      base::string16 name = it->encoding_display_name;
      bool has_rtl_chars = base::i18n::StringContainsStrongRTLChars(name);
      option->Append(new base::StringValue(encoding));
      option->Append(new base::StringValue(name));
      option->Append(new base::StringValue(has_rtl_chars ? "rtl" : "ltr"));
    } else {
      // Add empty name/value to indicate a separator item.
      option->Append(new base::StringValue(std::string()));
      option->Append(new base::StringValue(std::string()));
    }
    encoding_list.Append(option);
  }

  base::ListValue selected_values;
  selected_values.Append(new base::StringValue(MaybeGetLocalizedFontName(
      standard_font_.GetValue())));
  selected_values.Append(new base::StringValue(MaybeGetLocalizedFontName(
      serif_font_.GetValue())));
  selected_values.Append(new base::StringValue(MaybeGetLocalizedFontName(
      sans_serif_font_.GetValue())));
  selected_values.Append(new base::StringValue(MaybeGetLocalizedFontName(
      fixed_font_.GetValue())));
  selected_values.Append(new base::StringValue(font_encoding_.GetValue()));

  web_ui()->CallJavascriptFunction("FontSettings.setFontsData",
                                   *list.get(), encoding_list,
                                   selected_values);
}

void FontSettingsHandler::SetUpStandardFontSample() {
  base::StringValue font_value(standard_font_.GetValue());
  base::FundamentalValue size_value(default_font_size_.GetValue());
  web_ui()->CallJavascriptFunction(
      "FontSettings.setUpStandardFontSample", font_value, size_value);
}

void FontSettingsHandler::SetUpSerifFontSample() {
  base::StringValue font_value(serif_font_.GetValue());
  base::FundamentalValue size_value(default_font_size_.GetValue());
  web_ui()->CallJavascriptFunction(
      "FontSettings.setUpSerifFontSample", font_value, size_value);
}

void FontSettingsHandler::SetUpSansSerifFontSample() {
  base::StringValue font_value(sans_serif_font_.GetValue());
  base::FundamentalValue size_value(default_font_size_.GetValue());
  web_ui()->CallJavascriptFunction(
      "FontSettings.setUpSansSerifFontSample", font_value, size_value);
}

void FontSettingsHandler::SetUpFixedFontSample() {
  base::StringValue font_value(fixed_font_.GetValue());
  base::FundamentalValue size_value(default_fixed_font_size_.GetValue());
  web_ui()->CallJavascriptFunction(
      "FontSettings.setUpFixedFontSample", font_value, size_value);
}

void FontSettingsHandler::SetUpMinimumFontSample() {
  base::FundamentalValue size_value(minimum_font_size_.GetValue());
  web_ui()->CallJavascriptFunction("FontSettings.setUpMinimumFontSample",
                                   size_value);
}

const extensions::Extension*
FontSettingsHandler::GetAdvancedFontSettingsExtension() {
  Profile* profile = Profile::FromWebUI(web_ui());
  ExtensionService* service =
      extensions::ExtensionSystem::Get(profile)->extension_service();
  if (!service->IsExtensionEnabled(kAdvancedFontSettingsExtensionId))
    return NULL;
  return service->GetInstalledExtension(kAdvancedFontSettingsExtensionId);
}

void FontSettingsHandler::NotifyAdvancedFontSettingsAvailability() {
  web_ui()->CallJavascriptFunction(
      "FontSettings.notifyAdvancedFontSettingsAvailability",
      base::FundamentalValue(GetAdvancedFontSettingsExtension() != NULL));
}

void FontSettingsHandler::HandleOpenAdvancedFontSettingsOptions(
    const base::ListValue* args) {
  const extensions::Extension* extension = GetAdvancedFontSettingsExtension();
  if (!extension)
    return;
  extensions::ExtensionTabUtil::OpenOptionsPage(extension,
      chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()));
}

void FontSettingsHandler::OnWebKitDefaultFontSizeChanged() {
  SetUpStandardFontSample();
  SetUpSerifFontSample();
  SetUpSansSerifFontSample();
}

}  // namespace options