// Copyright (c) 2011 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/sync/glue/theme_util.h"

#include <string>

#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/extensions/extension_install_ui.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_updater.h"
#if defined(TOOLKIT_USES_GTK)
#include "chrome/browser/ui/gtk/gtk_theme_service.h"
#endif
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/protocol/theme_specifics.pb.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "googleurl/src/gurl.h"

namespace browser_sync {

const char kCurrentThemeClientTag[] = "current_theme";

namespace {

bool IsSystemThemeDistinctFromDefaultTheme() {
#if defined(TOOLKIT_USES_GTK)
  return true;
#else
  return false;
#endif
}

bool UseSystemTheme(Profile* profile) {
#if defined(TOOLKIT_USES_GTK)
  return GtkThemeService::GetFrom(profile)->UseGtkTheme();
#else
  return false;
#endif
}

}  // namespace

bool AreThemeSpecificsEqual(const sync_pb::ThemeSpecifics& a,
                            const sync_pb::ThemeSpecifics& b) {
  return AreThemeSpecificsEqualHelper(
      a, b, IsSystemThemeDistinctFromDefaultTheme());
}

bool AreThemeSpecificsEqualHelper(
    const sync_pb::ThemeSpecifics& a,
    const sync_pb::ThemeSpecifics& b,
    bool is_system_theme_distinct_from_default_theme) {
  if (a.use_custom_theme() != b.use_custom_theme()) {
    return false;
  }

  if (a.use_custom_theme()) {
    // We're using a custom theme, so simply compare IDs since those
    // are guaranteed unique.
    return a.custom_theme_id() == b.custom_theme_id();
  } else if (is_system_theme_distinct_from_default_theme) {
    // We're not using a custom theme, but we care about system
    // vs. default.
    return a.use_system_theme_by_default() == b.use_system_theme_by_default();
  } else {
    // We're not using a custom theme, and we don't care about system
    // vs. default.
    return true;
  }
}

namespace {

bool IsTheme(const Extension& extension) {
  return extension.is_theme();
}

}  // namespace

void SetCurrentThemeFromThemeSpecifics(
    const sync_pb::ThemeSpecifics& theme_specifics,
    Profile* profile) {
  DCHECK(profile);
  if (theme_specifics.use_custom_theme()) {
    // TODO(akalin): Figure out what to do about third-party themes
    // (i.e., those not on either Google gallery).
    std::string id(theme_specifics.custom_theme_id());
    GURL update_url(theme_specifics.custom_theme_update_url());
    VLOG(1) << "Applying theme " << id << " with update_url " << update_url;
    ExtensionServiceInterface* extensions_service =
        profile->GetExtensionService();
    CHECK(extensions_service);
    const Extension* extension = extensions_service->GetExtensionById(id, true);
    if (extension) {
      if (!extension->is_theme()) {
        VLOG(1) << "Extension " << id << " is not a theme; aborting";
        return;
      }
      if (!extensions_service->IsExtensionEnabled(id)) {
        VLOG(1) << "Theme " << id << " is not enabled; aborting";
        return;
      }
      // Get previous theme info before we set the new theme.
      std::string previous_theme_id;
      {
        const Extension* current_theme =
            ThemeServiceFactory::GetThemeForProfile(profile);
        if (current_theme) {
          DCHECK(current_theme->is_theme());
          previous_theme_id = current_theme->id();
        }
      }
      bool previous_use_system_theme = UseSystemTheme(profile);
      // An enabled theme extension with the given id was found, so
      // just set the current theme to it.
      ThemeServiceFactory::GetForProfile(profile)->SetTheme(extension);
      // Pretend the theme was just installed.
      ExtensionInstallUI::ShowThemeInfoBar(
          previous_theme_id, previous_use_system_theme,
          extension, profile);
    } else {
      // No extension with this id exists -- we must install it; we do
      // so by adding it as a pending extension and then triggering an
      // auto-update cycle.
      // Themes don't need to install silently as they just pop up an
      // informational dialog after installation instead of a
      // confirmation dialog.
      const bool kInstallSilently = false;
      const bool kEnableOnInstall = true;
      const bool kEnableIncognitoOnInstall = false;
      extensions_service->pending_extension_manager()->AddFromSync(
          id, update_url, &IsTheme,
          kInstallSilently, kEnableOnInstall, kEnableIncognitoOnInstall);
      extensions_service->CheckForUpdatesSoon();
    }
  } else if (theme_specifics.use_system_theme_by_default()) {
    ThemeServiceFactory::GetForProfile(profile)->SetNativeTheme();
  } else {
    ThemeServiceFactory::GetForProfile(profile)->UseDefaultTheme();
  }
}

bool UpdateThemeSpecificsOrSetCurrentThemeIfNecessary(
    Profile* profile, sync_pb::ThemeSpecifics* theme_specifics) {
  if (!theme_specifics->use_custom_theme() &&
      (ThemeServiceFactory::GetThemeForProfile(profile) ||
       (UseSystemTheme(profile) &&
        IsSystemThemeDistinctFromDefaultTheme()))) {
    GetThemeSpecificsFromCurrentTheme(profile, theme_specifics);
    return true;
  } else {
    SetCurrentThemeFromThemeSpecificsIfNecessary(*theme_specifics, profile);
    return false;
  }
}

void GetThemeSpecificsFromCurrentTheme(
    Profile* profile,
    sync_pb::ThemeSpecifics* theme_specifics) {
  DCHECK(profile);
  const Extension* current_theme =
      ThemeServiceFactory::GetThemeForProfile(profile);
  if (current_theme) {
    DCHECK(current_theme->is_theme());
  }
  GetThemeSpecificsFromCurrentThemeHelper(
      current_theme,
      IsSystemThemeDistinctFromDefaultTheme(),
      UseSystemTheme(profile),
      theme_specifics);
}

void GetThemeSpecificsFromCurrentThemeHelper(
    const Extension* current_theme,
    bool is_system_theme_distinct_from_default_theme,
    bool use_system_theme_by_default,
    sync_pb::ThemeSpecifics* theme_specifics) {
  bool use_custom_theme = (current_theme != NULL);
  theme_specifics->set_use_custom_theme(use_custom_theme);
  if (is_system_theme_distinct_from_default_theme) {
    theme_specifics->set_use_system_theme_by_default(
        use_system_theme_by_default);
  } else {
    DCHECK(!use_system_theme_by_default);
  }
  if (use_custom_theme) {
    DCHECK(current_theme);
    DCHECK(current_theme->is_theme());
    theme_specifics->set_custom_theme_name(current_theme->name());
    theme_specifics->set_custom_theme_id(current_theme->id());
    theme_specifics->set_custom_theme_update_url(
        current_theme->update_url().spec());
  } else {
    DCHECK(!current_theme);
    theme_specifics->clear_custom_theme_name();
    theme_specifics->clear_custom_theme_id();
    theme_specifics->clear_custom_theme_update_url();
  }
}

void SetCurrentThemeFromThemeSpecificsIfNecessary(
    const sync_pb::ThemeSpecifics& theme_specifics, Profile* profile) {
  DCHECK(profile);
  sync_pb::ThemeSpecifics old_theme_specifics;
  GetThemeSpecificsFromCurrentTheme(profile, &old_theme_specifics);
  if (!AreThemeSpecificsEqual(old_theme_specifics, theme_specifics)) {
    SetCurrentThemeFromThemeSpecifics(theme_specifics, profile);
  }
}

}  // namespace browser_sync