// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/controls/button/image_button_factory.h"

#include <memory>
#include <utility>

#include "base/memory/raw_ref.h"
#include "ui/base/models/image_model.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.h"
#include "ui/color/color_variant.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/gfx/vector_icon_utils.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/painter.h"

namespace views {

namespace {

class ColorTrackingVectorImageButton : public ImageButton {
 public:
  ColorTrackingVectorImageButton(PressedCallback callback,
                                 const gfx::VectorIcon& icon,
                                 int dip_size,
                                 ui::ColorId icon_color_id,
                                 ui::ColorId icon_disabled_color_id,
                                 ui::ColorId icon_hovered_color_id)
      : ImageButton(std::move(callback)),
        icon_(icon),
        dip_size_(dip_size),
        icon_color_id_(icon_color_id),
        icon_disabled_color_id_(icon_disabled_color_id),
        icon_hovered_color_id_(icon_hovered_color_id) {}

  // ImageButton:
  void OnThemeChanged() override {
    ImageButton::OnThemeChanged();
    const ui::ColorProvider* cp = GetColorProvider();
    const SkColor color = cp->GetColor(icon_color_id_);
    const SkColor disabled_color = cp->GetColor(icon_disabled_color_id_);
    SetImageFromVectorIconWithColor(
        this, *icon_, dip_size_,
        {color, disabled_color, icon_hovered_color_id_});
  }

 private:
  const raw_ref<const gfx::VectorIcon> icon_;
  int dip_size_;
  ui::ColorId icon_color_id_;
  ui::ColorId icon_disabled_color_id_;
  ui::ColorId icon_hovered_color_id_;
};

}  // namespace
std::unique_ptr<ImageButton> CreateVectorImageButtonWithNativeTheme(
    Button::PressedCallback callback,
    const gfx::VectorIcon& icon,
    std::optional<int> dip_size,
    ui::ColorId icon_color_id,
    ui::ColorId icon_disabled_color_id,
    ui::ColorId icon_hovered_color_id) {
  // We can't use `value_or` as that ALWAYS evaluates the false case, which is
  // undefined for some valid and commonly used Chrome vector icons.
  const int dip_size_value = dip_size.has_value()
                                 ? dip_size.value()
                                 : GetDefaultSizeOfVectorIcon(icon);

  auto button = std::make_unique<ColorTrackingVectorImageButton>(
      std::move(callback), icon, dip_size_value, icon_color_id,
      icon_disabled_color_id, icon_hovered_color_id);
  ConfigureVectorImageButton(button.get());
  return button;
}

std::unique_ptr<ImageButton> CreateVectorImageButton(
    Button::PressedCallback callback) {
  auto button = std::make_unique<ImageButton>(std::move(callback));
  ConfigureVectorImageButton(button.get());
  return button;
}

std::unique_ptr<ToggleImageButton> CreateVectorToggleImageButton(
    Button::PressedCallback callback) {
  auto button = std::make_unique<ToggleImageButton>(std::move(callback));
  ConfigureVectorImageButton(button.get());
  return button;
}

IconColors::IconColors(ui::ColorVariant color,
                       ui::ColorVariant disabled_color,
                       ui::ColorId hovered_color)
    : color(color),
      disabled_color(disabled_color),
      hovered_color(hovered_color) {}

IconColors::IconColors(const IconColors&) = default;
IconColors& IconColors::operator=(const IconColors&) = default;
IconColors::~IconColors() = default;

void ConfigureVectorImageButton(ImageButton* button) {
  InkDrop::Get(button)->SetMode(views::InkDropHost::InkDropMode::ON);
  button->SetHasInkDropActionOnClick(true);
  button->SetImageHorizontalAlignment(ImageButton::ALIGN_CENTER);
  button->SetImageVerticalAlignment(ImageButton::ALIGN_MIDDLE);
  button->SetBorder(CreateEmptyBorder(
      LayoutProvider::Get()->GetInsetsMetric(INSETS_VECTOR_IMAGE_BUTTON)));
}

void SetImageFromVectorIconWithColor(ImageButton* button,
                                     const gfx::VectorIcon& icon,
                                     int dip_size,
                                     IconColors colors) {
  const ui::ImageModel& normal_image =
      ui::ImageModel::FromVectorIcon(icon, colors.color, dip_size);
  const ui::ImageModel& disabled_image =
      ui::ImageModel::FromVectorIcon(icon, colors.disabled_color, dip_size);

  button->SetImageModel(Button::STATE_NORMAL, normal_image);
  button->SetImageModel(Button::STATE_DISABLED, disabled_image);

  // Set hovered/pressed images for high contrast mode support.
  const ui::ImageModel& hovered_image =
      ui::ImageModel::FromVectorIcon(icon, colors.hovered_color, dip_size);
  button->SetImageModel(Button::STATE_HOVERED, hovered_image);
  button->SetImageModel(Button::STATE_PRESSED, hovered_image);

  InkDrop::Get(button)->SetBaseColor(colors.color);
}

void SetImageFromVectorIconWithColor(ImageButton* button,
                                     const gfx::VectorIcon& icon,
                                     IconColors colors,
                                     std::optional<int> icon_size) {
  // A ternary must be used here instead of `value_or` to prevent a DCHECK when
  // the icon does not contain a size in it's definition.
  int dip_size = icon_size.has_value() ? icon_size.value()
                                       : GetDefaultSizeOfVectorIcon(icon);
  SetImageFromVectorIconWithColor(button, icon, dip_size, colors);
}

void SetToggledImageFromVectorIconWithColor(ToggleImageButton* button,
                                            const gfx::VectorIcon& icon,
                                            int dip_size,
                                            IconColors colors) {
  const ui::ImageModel& normal_image =
      ui::ImageModel::FromVectorIcon(icon, colors.color, dip_size);
  const ui::ImageModel& disabled_image =
      ui::ImageModel::FromVectorIcon(icon, colors.disabled_color, dip_size);

  button->SetToggledImageModel(Button::STATE_NORMAL, normal_image);
  button->SetToggledImageModel(Button::STATE_DISABLED, disabled_image);

  // Set hovered/pressed toggled images for high contrast mode support.
  const ui::ImageModel& hovered_image =
      ui::ImageModel::FromVectorIcon(icon, colors.hovered_color, dip_size);
  button->SetToggledImageModel(Button::STATE_HOVERED, hovered_image);
  button->SetToggledImageModel(Button::STATE_PRESSED, hovered_image);
}

void SetToggledImageFromVectorIconWithColor(ToggleImageButton* button,
                                            const gfx::VectorIcon& icon,
                                            IconColors colors,
                                            std::optional<int> icon_size) {
  // A ternary must be used here instead of `value_or` to prevent a DCHECK when
  // the icon does not contain a size in it's definition.
  int dip_size = icon_size.has_value() ? icon_size.value()
                                       : GetDefaultSizeOfVectorIcon(icon);
  SetToggledImageFromVectorIconWithColor(button, icon, dip_size, colors);
}

}  // namespace views
