// Copyright 2012 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_NATIVE_THEME_NATIVE_THEME_BASE_H_
#define UI_NATIVE_THEME_NATIVE_THEME_BASE_H_

#include <array>
#include <optional>

#include "base/component_export.h"
#include "base/containers/span.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkRect.h"
#include "ui/color/color_id.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/native_theme/native_theme.h"

namespace cc {
class PaintCanvas;
}

namespace ui {

class ColorProvider;
class NativeThemeBaseTest;

class COMPONENT_EXPORT(NATIVE_THEME) NativeThemeBase : public NativeTheme {
 public:
  NativeThemeBase(const NativeThemeBase&) = delete;
  NativeThemeBase& operator=(const NativeThemeBase&) = delete;

  // NativeTheme:
  gfx::Size GetPartSize(Part part,
                        State state,
                        const ExtraParams& extra_params) const override;
  float GetBorderRadiusForPart(Part part,
                               float width,
                               float height) const override;
  SkColor GetScrollbarThumbColor(
      const ColorProvider* color_provider,
      State state,
      const ScrollbarThumbExtraParams& extra_params) const override;

 protected:
  enum ControlColorId {
    kBorder,
    kDisabledBorder,
    kHoveredBorder,
    kPressedBorder,
    kAccent,
    kDisabledAccent,
    kHoveredAccent,
    kPressedAccent,
    kCheckboxBackground,
    kDisabledCheckboxBackground,
    kFill,
    kDisabledFill,
    kHoveredFill,
    kPressedFill,
    kLightenLayer,
    kProgressValue,
    kSlider,
    kDisabledSlider,
    kHoveredSlider,
    kPressedSlider,
    kSliderBorder,
    kHoveredSliderBorder,
    kPressedSliderBorder,
    kAutoCompleteBackground,
    kScrollbarArrowBackground,
    kScrollbarArrowBackgroundDisabled,
    kScrollbarArrowBackgroundHovered,
    kScrollbarArrowBackgroundPressed,
    kScrollbarArrow,
    kScrollbarArrowDisabled,
    kScrollbarArrowHovered,
    kScrollbarArrowPressed,
    // TODO(crbug.com/40242489): kScrollbarCorner overlaps with
    // NativeTheme::Part::kScrollbarCorner. Make ControlColorId a enum class
    // or remove the class completely in favor of ColorProvider colors.
    kScrollbarCornerControlColorId,
    kScrollbarTrack,
    kScrollbarThumb,
    kScrollbarThumbHovered,
    kScrollbarThumbPressed,
    kButtonBorder,
    kButtonDisabledBorder,
    kButtonHoveredBorder,
    kButtonPressedBorder,
    kButtonFill,
    kButtonDisabledFill,
    kButtonHoveredFill,
    kButtonPressedFill
  };

  static constexpr auto kButtonBorderColors =
      std::to_array({kButtonDisabledBorder, kButtonHoveredBorder, kButtonBorder,
                     kButtonPressedBorder});

  using NativeTheme::NativeTheme;
  ~NativeThemeBase() override;

  // NativeTheme:
  void PaintImpl(cc::PaintCanvas* canvas,
                 const ColorProvider* color_provider,
                 Part part,
                 State state,
                 const gfx::Rect& rect,
                 const ExtraParams& extra_params,
                 bool forced_colors,
                 bool dark_mode,
                 PreferredContrast contrast,
                 std::optional<SkColor> accent_color) const override;

  // Returns the size of a vertical scrollbar button. Horizontal scrollbars
  // transpose this value.
  //
  // NOTE: The width here is also assumed to be the track width, so should be
  // nonzero even if buttons should not be drawn.
  virtual gfx::Size GetVerticalScrollbarButtonSize() const;

  // Returns the size of a vertical scrollbar thumb. Horizontal scrollbars
  // transpose this value.
  virtual gfx::Size GetVerticalScrollbarThumbSize() const;

  // Returns the scrollbar arrow rect, given an arrow button rect of `rect`.
  virtual gfx::RectF GetArrowRect(const gfx::Rect& rect,
                                  Part part,
                                  State state) const;

  virtual SkColor GetControlColor(ControlColorId color_id,
                                  bool dark_mode,
                                  PreferredContrast contrast,
                                  const ColorProvider* color_provider) const;

  // Returns any custom color ID to use based on `state` and `extra_params`. If
  // this returns null, the default thumb color for the state will be used.
  virtual std::optional<ColorId> GetScrollbarThumbColorId(
      State state,
      const ScrollbarThumbExtraParams& extra_params) const;

  // Returns the amount a hovered or pressed scrollbar part should contrast with
  // the normal version of that part. Used when there is a custom scrollbar part
  // color to try and mimic the default behavior.
  virtual float GetScrollbarPartContrastRatioForState(State state) const;

  virtual void PaintFrameTopArea(
      cc::PaintCanvas* canvas,
      State state,
      const gfx::Rect& rect,
      const FrameTopAreaExtraParams& extra_params) const;

  virtual void PaintMenuPopupBackground(
      cc::PaintCanvas* canvas,
      const ColorProvider* color_provider,
      const gfx::Size& size,
      const MenuBackgroundExtraParams& extra_params) const;

  virtual void PaintMenuSeparator(
      cc::PaintCanvas* canvas,
      const ColorProvider* color_provider,
      State state,
      const gfx::Rect& rect,
      const MenuSeparatorExtraParams& extra_params) const;

  // Paints arrow buttons for scrollbars and inner spin buttons.
  virtual void PaintArrowButton(
      cc::PaintCanvas* canvas,
      const ColorProvider* color_provider,
      const gfx::Rect& rect,
      Part part,
      State state,
      bool forced_colors,
      bool dark_mode,
      PreferredContrast contrast,
      const ScrollbarArrowExtraParams& extra_params) const;

  virtual void PaintScrollbarThumb(
      cc::PaintCanvas* canvas,
      const ColorProvider* color_provider,
      Part part,
      State state,
      const gfx::Rect& rect,
      const ScrollbarThumbExtraParams& extra_params
#if BUILDFLAG(ARKWEB_SCROLLBAR)
      , SkColor scrollbar_color
#endif // ARKWEB_SCROLLBAR
      ) const;

  virtual void PaintScrollbarTrack(
      cc::PaintCanvas* canvas,
      const ColorProvider* color_provider,
      Part part,
      State state,
      const ScrollbarTrackExtraParams& extra_params,
      const gfx::Rect& rect,
      bool forced_colors,
      PreferredContrast contrast) const;

  virtual void PaintScrollbarCorner(
      cc::PaintCanvas* canvas,
      const ColorProvider* color_provider,
      State state,
      const gfx::Rect& rect,
      const ScrollbarTrackExtraParams& extra_params) const;

  // Selects a color from `colors` based on `state`, then returns the physical
  // color. `colors` must be in the same order as the actual `State` enum.
  SkColor GetControlColorForState(base::span<const ControlColorId, 4> colors,
                                  State state,
                                  bool dark_mode,
                                  PreferredContrast contrast,
                                  const ColorProvider* color_provider) const;

  SkColor GetScrollbarArrowBackgroundColor(
      const ScrollbarArrowExtraParams& extra_params,
      State state,
      bool dark_mode,
      PreferredContrast contrast,
      const ColorProvider* color_provider) const;

  SkColor GetScrollbarArrowForegroundColor(
      SkColor bg_color,
      const ScrollbarArrowExtraParams& extra_params,
      State state,
      bool dark_mode,
      PreferredContrast contrast,
      const ColorProvider* color_provider) const;

  // For disabled controls, lightens the background so the translucent disabled
  // color works regardless of what it's over.
  void PaintLightenLayer(cc::PaintCanvas* canvas,
                         const ColorProvider* color_provider,
                         const SkRect& skrect,
                         State state,
                         float border_radius,
                         bool dark_mode,
                         PreferredContrast contrast) const;

  // Paints arrows for scrollbars and inner spin buttons.
  void PaintArrow(cc::PaintCanvas* canvas,
                  const gfx::Rect& rect,
                  Part part,
                  State state,
                  SkColor color) const;

 private:
  friend class NativeThemeBaseTest;

  static constexpr auto kBorderColors =
      std::to_array({kDisabledBorder, kHoveredBorder, kBorder, kPressedBorder});
  static constexpr auto kAccentColors =
      std::to_array({kDisabledAccent, kHoveredAccent, kAccent, kPressedAccent});
  static constexpr auto kCheckboxBackgroundColors =
      std::to_array({kDisabledCheckboxBackground, kCheckboxBackground,
                     kCheckboxBackground, kCheckboxBackground});
  static constexpr auto kFillColors =
      std::to_array({kDisabledFill, kHoveredFill, kFill, kPressedFill});
  static constexpr auto kSliderColors =
      std::to_array({kDisabledSlider, kHoveredSlider, kSlider, kPressedSlider});
  static constexpr auto kSliderBorderColors =
      std::to_array({kDisabledBorder, kHoveredSliderBorder, kSliderBorder,
                     kPressedSliderBorder});

  static SkPath PathForArrow(const gfx::RectF& rect, Part part);

  // Like `GetControlColorForState()`; however, if `accent_color` is non-null
  // and `state` is not `kDisabled`, overrides the default colors with computed
  // ones based on `accent_color`.
  SkColor GetAccentOrControlColorForState(
      std::optional<SkColor> accent_color,
      base::span<const ControlColorId, 4> colors,
      State state,
      bool dark_mode,
      PreferredContrast contrast,
      const ColorProvider* color_provider) const;

  // Adjusts custom scrollbar button/thumb colors to meet contrast minima. When
  // `state` is hovered or pressed, `color` (if present) will be adjusted to
  // contrast with the normal state. If `bg_color` is present, also attempts to
  // ensure `color` maintains visible contrast with it.
  std::optional<SkColor> GetContrastingColorForScrollbarPart(
      std::optional<SkColor> color,
      std::optional<SkColor> bg_color,
      State state) const;

  void PaintCheckbox(cc::PaintCanvas* canvas,
                     const ColorProvider* color_provider,
                     State state,
                     const gfx::Rect& rect,
                     const ButtonExtraParams& extra_params,
                     bool dark_mode,
                     PreferredContrast contrast,
                     std::optional<SkColor> accent_color) const;

  void PaintInnerSpinButton(cc::PaintCanvas* canvas,
                            const ColorProvider* color_provider,
                            State state,
                            gfx::Rect rect,
                            const InnerSpinButtonExtraParams& extra_params,
                            bool forced_colors,
                            bool dark_mode,
                            PreferredContrast contrast) const;

  void PaintMenuList(cc::PaintCanvas* canvas,
                     const ColorProvider* color_provider,
                     State state,
                     const gfx::Rect& rect,
                     const MenuListExtraParams& extra_params,
                     bool dark_mode,
                     PreferredContrast contrast) const;

  void PaintProgressBar(cc::PaintCanvas* canvas,
                        const ColorProvider* color_provider,
                        State state,
                        const gfx::Rect& rect,
                        const ProgressBarExtraParams& extra_params,
                        bool dark_mode,
                        PreferredContrast contrast,
                        std::optional<SkColor> accent_color) const;

  void PaintButton(cc::PaintCanvas* canvas,
                   const ColorProvider* color_provider,
                   State state,
                   const gfx::Rect& rect,
                   const ButtonExtraParams& extra_params,
                   bool dark_mode,
                   PreferredContrast contrast) const;

  void PaintRadio(cc::PaintCanvas* canvas,
                  const ColorProvider* color_provider,
                  State state,
                  const gfx::Rect& rect,
                  const ButtonExtraParams& extra_params,
                  bool dark_mode,
                  PreferredContrast contrast,
                  std::optional<SkColor> accent_color) const;

  void PaintSliderTrack(cc::PaintCanvas* canvas,
                        const ColorProvider* color_provider,
                        State state,
                        const gfx::Rect& rect,
                        const SliderExtraParams& extra_params,
                        bool dark_mode,
                        PreferredContrast contrast,
                        std::optional<SkColor> accent_color) const;

  void PaintSliderThumb(cc::PaintCanvas* canvas,
                        const ColorProvider* color_provider,
                        State state,
                        const gfx::Rect& rect,
                        const SliderExtraParams& extra_params,
                        bool dark_mode,
                        PreferredContrast contrast,
                        std::optional<SkColor> accent_color) const;

  void PaintTextField(cc::PaintCanvas* canvas,
                      const ColorProvider* color_provider,
                      State state,
                      const gfx::Rect& rect,
                      const TextFieldExtraParams& extra_params,
                      bool dark_mode,
                      PreferredContrast contrast) const;

  // Draws the common elements of checkboxes and radio buttons. Returns the
  // rectangle within which any additional decorations should be drawn, or empty
  // if none.
  SkRect PaintCheckboxRadioCommon(cc::PaintCanvas* canvas,
                                  const ColorProvider* color_provider,
                                  State state,
                                  const gfx::Rect& rect,
                                  const ButtonExtraParams& extra_params,
                                  bool is_checkbox,
                                  float border_radius,
                                  bool dark_mode,
                                  PreferredContrast contrast,
                                  std::optional<SkColor> accent_color) const;
};

}  // namespace ui

#endif  // UI_NATIVE_THEME_NATIVE_THEME_BASE_H_