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

#ifndef UI_COLOR_COLOR_PROVIDER_KEY_H_
#define UI_COLOR_COLOR_PROVIDER_KEY_H_

#include <optional>
#include <tuple>

#include "base/component_export.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/color/system_theme.h"

#if BUILDFLAG(IS_ANDROID)
#include "base/android/jni_weak_ref.h"
#include "base/android/scoped_java_ref.h"
#endif

namespace color_utils {
struct HSL;
}

namespace ui {

class ColorProvider;

// All the information needed to seed the creation of a `ColorProvider`.
// Equivalient `ColorProviderKey`s are guaranteed to generate the same colors.
struct COMPONENT_EXPORT(COLOR_PROVIDER_KEY) ColorProviderKey {
  enum class ColorMode {
    kLight,
    kDark,
  };
  enum class ContrastMode {
    kNormal,
    kHigh,
  };
  // A ForcedColors value other than `kNone` overrides various colors to produce
  // particular visual themes, usually for high contrast.
  enum class ForcedColors {
    // Default behavior.
    kNone,

    // Some colors are forced to match the underlying system colors.
    kSystem,

    // Some colors are forced to match various color themes.
    kDusk,      // Mimics Win 11 "Dusk"
    kDesert,    // Mimics Win 11 "Desert"
    kNightSky,  // Mimics Win 11 "Night Sky"
    kAquatic,   // Mimics Win 11 "Aquatic"
    kWhite,     // Mimics Win 10 "High Contrast White"
  };
  enum class FrameType {
    // Chrome renders the browser frame.
    kChromium,
    // Native system renders the browser frame. Currently GTK only.
    kNative,
  };
  // The style in which Chrome-rendered frames are painted. This only applies
  // for the kChromium frame type.
  enum class FrameStyle {
    // Paints the default Chrome frame.
    kDefault,
    // Paints an emulated system style frame.
    kSystem,
    // Paints a glass style frame (e.g. macOS Liquid Glass).
    kGlass,
  };
  // The type of color palette that is generated.
  enum class SchemeVariant {
    kTonalSpot,
    kNeutral,
    kVibrant,
    kExpressive,
  };
  // The source of the color used to generate the material color palette.
  enum class UserColorSource {
    kBaseline,
    kGrayscale,
    kAccent,
  };

  class COMPONENT_EXPORT(COLOR_PROVIDER_KEY) InitializerSupplier {
   public:
    InitializerSupplier();
    // Adds any mixers necessary to represent this supplier.
    virtual void AddColorMixers(ColorProvider* provider,
                                const ColorProviderKey& key) const = 0;

   protected:
    virtual ~InitializerSupplier();
  };

  // Threadsafe not because ColorProviderManager requires it but because a
  // concrete subclass does.
  class COMPONENT_EXPORT(COLOR_PROVIDER_KEY) ThemeInitializerSupplier
      : public InitializerSupplier,
        public base::RefCountedThreadSafe<ThemeInitializerSupplier> {
   public:  // NOLINT(whitespace/blank_line): Adding a newline causes Redundant
            // blank line. Without the newline, we get whitespace/blankline.
    enum class ThemeType {
      kExtension,
      kAutogenerated,
      kNativeX11,
    };

    explicit ThemeInitializerSupplier(ThemeType theme_type);

    virtual bool GetColor(int id, SkColor* color) const = 0;
    virtual bool GetTint(int id, color_utils::HSL* hsl) const = 0;
    virtual bool GetDisplayProperty(int id, int* result) const = 0;
    virtual bool HasCustomImage(int id) const = 0;

    ThemeType get_theme_type() const { return theme_type_; }

   protected:
    ~ThemeInitializerSupplier() override = default;

   private:
    friend class base::RefCountedThreadSafe<ThemeInitializerSupplier>;

    ThemeType theme_type_;
  };

  ColorProviderKey();
  ColorProviderKey(const ColorProviderKey&);
  ColorProviderKey& operator=(const ColorProviderKey&);
  ColorProviderKey(ColorProviderKey&&);
  ColorProviderKey& operator=(ColorProviderKey&&);
  ~ColorProviderKey();

  ColorMode color_mode = ColorMode::kLight;
  ContrastMode contrast_mode = ContrastMode::kNormal;
  ForcedColors forced_colors = ForcedColors::kNone;
  SystemTheme system_theme = SystemTheme::kDefault;
  FrameType frame_type = FrameType::kChromium;
  FrameStyle frame_style = FrameStyle::kDefault;
  UserColorSource user_color_source = UserColorSource::kAccent;
  std::optional<SkColor> user_color = std::nullopt;
  std::optional<SchemeVariant> scheme_variant = std::nullopt;
  scoped_refptr<ThemeInitializerSupplier> custom_theme = nullptr;
  // Only dereferenced when populating the ColorMixer. After that, used to
  // compare addresses during lookup.
  raw_ptr<InitializerSupplier, AcrossTasksDanglingUntriaged> app_controller =
      nullptr;  // unowned
  size_t system_theme_version = 0;

  // TODO(crbug.com/537023567): Populate the hash and context from
  // WindowAndroid.
#if BUILDFLAG(IS_ANDROID)
  int64_t context_hash = 0;
  JavaObjectWeakGlobalRef context;
#endif

  bool operator==(const ColorProviderKey& other) const {
    auto* lhs_app_controller = app_controller.get();
    auto* rhs_app_controller = other.app_controller.get();
#if BUILDFLAG(IS_ANDROID)
    return std::tie(color_mode, contrast_mode, forced_colors, system_theme,
                    frame_type, frame_style, user_color_source, user_color,
                    scheme_variant, custom_theme, lhs_app_controller,
                    system_theme_version, context_hash) ==
           std::tie(other.color_mode, other.contrast_mode, other.forced_colors,
                    other.system_theme, other.frame_type, other.frame_style,
                    other.user_color_source, other.user_color,
                    other.scheme_variant, other.custom_theme,
                    rhs_app_controller, other.system_theme_version,
                    other.context_hash);
#else
    return std::tie(color_mode, contrast_mode, forced_colors, system_theme,
                    frame_type, frame_style, user_color_source, user_color,
                    scheme_variant, custom_theme, lhs_app_controller,
                    system_theme_version) ==
           std::tie(other.color_mode, other.contrast_mode, other.forced_colors,
                    other.system_theme, other.frame_type, other.frame_style,
                    other.user_color_source, other.user_color,
                    other.scheme_variant, other.custom_theme,
                    rhs_app_controller, other.system_theme_version);
#endif
  }

  bool operator<(const ColorProviderKey& other) const {
    auto* lhs_app_controller = app_controller.get();
    auto* rhs_app_controller = other.app_controller.get();
#if BUILDFLAG(IS_ANDROID)
    return std::tie(color_mode, contrast_mode, forced_colors, system_theme,
                    frame_type, frame_style, user_color_source, user_color,
                    scheme_variant, custom_theme, lhs_app_controller,
                    system_theme_version, context_hash) <
           std::tie(other.color_mode, other.contrast_mode, other.forced_colors,
                    other.system_theme, other.frame_type, other.frame_style,
                    other.user_color_source, other.user_color,
                    other.scheme_variant, other.custom_theme,
                    rhs_app_controller, other.system_theme_version,
                    other.context_hash);
#else
    return std::tie(color_mode, contrast_mode, forced_colors, system_theme,
                    frame_type, frame_style, user_color_source, user_color,
                    scheme_variant, custom_theme, lhs_app_controller,
                    system_theme_version) <
           std::tie(other.color_mode, other.contrast_mode, other.forced_colors,
                    other.system_theme, other.frame_type, other.frame_style,
                    other.user_color_source, other.user_color,
                    other.scheme_variant, other.custom_theme,
                    rhs_app_controller, other.system_theme_version);
#endif
  }
};

}  // namespace ui

#endif  // UI_COLOR_COLOR_PROVIDER_KEY_H_
