// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/themes/theme_helper.h"

#include "base/feature_list.h"
#include "base/memory/ref_counted_memory.h"
#include "base/no_destructor.h"
#include "build/build_config.h"
#include "chrome/browser/themes/browser_theme_pack.h"
#include "chrome/browser/themes/custom_theme_supplier.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/grit/theme_resources.h"
#include "components/grit/components_scaled_resources.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image.h"
#include "ui/native_theme/native_theme.h"

#if BUILDFLAG(IS_LINUX)
#include "ui/linux/linux_ui.h"
#endif

namespace {

using TP = ThemeProperties;

// The default theme if we've gone to the theme gallery and installed the
// "Default" theme. We have to detect this case specifically. (By the time we
// realize we've installed the default theme, we already have an extension
// unpacked on the filesystem.)
constexpr char kDefaultThemeGalleryID[] = "hkacjpbfdknhflllbcmjibkdeoafencn";

}  // namespace

const char ThemeHelper::kDefaultThemeID[] = "";

// static
bool ThemeHelper::IsExtensionTheme(const CustomThemeSupplier* theme_supplier) {
  return theme_supplier && theme_supplier->get_theme_type() ==
                               ui::ColorProviderKey::ThemeInitializerSupplier::
                                   ThemeType::kExtension;
}

// static
bool ThemeHelper::IsAutogeneratedTheme(
    const CustomThemeSupplier* theme_supplier) {
  return theme_supplier && theme_supplier->get_theme_type() ==
                               ui::ColorProviderKey::ThemeInitializerSupplier::
                                   ThemeType::kAutogenerated;
}

// static
bool ThemeHelper::IsDefaultTheme(const CustomThemeSupplier* theme_supplier) {
  if (!theme_supplier) {
    return true;
  }

  using Type = ui::ColorProviderKey::ThemeInitializerSupplier::ThemeType;
  if (theme_supplier->get_theme_type() != Type::kExtension) {
    return false;
  }

  const std::string& id = theme_supplier->extension_id();
  return id == kDefaultThemeID || id == kDefaultThemeGalleryID;
}

// static
bool ThemeHelper::IsCustomTheme(const CustomThemeSupplier* theme_supplier) {
  return IsExtensionTheme(theme_supplier) ||
         IsAutogeneratedTheme(theme_supplier);
}

// static
bool ThemeHelper::HasCustomImage(int id,
                                 const CustomThemeSupplier* theme_supplier) {
  return BrowserThemePack::IsPersistentImageID(id) && theme_supplier &&
         theme_supplier->HasCustomImage(id);
}

ThemeHelper::ThemeHelper() = default;

ThemeHelper::~ThemeHelper() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}

int ThemeHelper::GetDisplayProperty(
    int id,
    const CustomThemeSupplier* theme_supplier) const {
  int result = 0;
  if (theme_supplier && theme_supplier->GetDisplayProperty(id, &result)) {
    return result;
  }

  return GetDefaultDisplayProperty(id);
}

scoped_refptr<base::RefCountedMemory> ThemeHelper::GetRawData(
    int id,
    const CustomThemeSupplier* theme_supplier,
    ui::ResourceScaleFactor scale_factor) const {
  // Check to see whether we should substitute some images.
  int ntp_alternate =
      GetDisplayProperty(TP::NTP_LOGO_ALTERNATE, theme_supplier);
  if (id == IDR_PRODUCT_LOGO && ntp_alternate != 0) {
    id = IDR_PRODUCT_LOGO_WHITE;
  }

  scoped_refptr<base::RefCountedMemory> data;
  if (theme_supplier) {
    data = theme_supplier->GetRawData(id, scale_factor);
  }
  if (!data) {
    data =
        ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
            id, ui::k100Percent);
  }

  return data;
}

color_utils::HSL ThemeHelper::GetTint(
    int id,
    bool incognito,
    const CustomThemeSupplier* theme_supplier) const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  color_utils::HSL hsl;
  if (theme_supplier && theme_supplier->GetTint(id, &hsl)) {
    return hsl;
  }

  return TP::GetDefaultTint(id, incognito, UseDarkModeColors(theme_supplier));
}

gfx::ImageSkia* ThemeHelper::GetImageSkiaNamed(
    int id,
    bool incognito,
    const CustomThemeSupplier* theme_supplier) const {
  gfx::Image image = GetImageNamed(id, incognito, theme_supplier);
  if (image.IsEmpty()) {
    return nullptr;
  }
  // TODO(pkotwicz): Remove this const cast.  The gfx::Image interface returns
  // its images const. GetImageSkiaNamed() also should but has many callsites.
  return const_cast<gfx::ImageSkia*>(image.ToImageSkia());
}

bool ThemeHelper::ShouldUseNativeFrame(
    const CustomThemeSupplier* theme_supplier) const {
  return false;
}

int ThemeHelper::GetDefaultDisplayProperty(int id) const {
  switch (id) {
    case TP::NTP_BACKGROUND_ALIGNMENT:
      return TP::ALIGN_CENTER;

    case TP::NTP_BACKGROUND_TILING:
      return TP::NO_REPEAT;

    case TP::NTP_LOGO_ALTERNATE:
      return 0;

    case TP::SHOULD_FILL_BACKGROUND_TAB_COLOR:
      return 1;

    default:
      return -1;
  }
}

// static
bool ThemeHelper::UseDarkModeColors(const CustomThemeSupplier* theme_supplier) {
  // Dark mode is disabled for custom themes so they apply atop a predictable
  // state.
  return !IsCustomTheme(theme_supplier) &&
         theme_supplier->GetNativeTheme()->preferred_color_scheme() ==
             ui::NativeTheme::PreferredColorScheme::kDark;
}

gfx::Image ThemeHelper::GetImageNamed(
    int id,
    bool incognito,
    const CustomThemeSupplier* theme_supplier) const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  int adjusted_id = id;
  if (incognito) {
    if (id == IDR_THEME_FRAME) {
      adjusted_id = IDR_THEME_FRAME_INCOGNITO;
    } else if (id == IDR_THEME_FRAME_INACTIVE) {
      adjusted_id = IDR_THEME_FRAME_INCOGNITO_INACTIVE;
    }
  }

  gfx::Image image;
  if (theme_supplier) {
    image = theme_supplier->GetImageNamed(adjusted_id);
  }

  if (image.IsEmpty()) {
    image = ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
        adjusted_id);
  }

  return image;
}
