// Copyright 2022 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/theme_tracking_image_view.h"

#include <memory>
#include <utility>

#include "base/functional/bind.h"
#include "components/vector_icons/vector_icons.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/models/image_model.h"
#include "ui/color/color_id.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/widget.h"

namespace views {

namespace {

constexpr int kImageSize = 16;

gfx::ImageSkia CreateTestImage(SkColor color) {
  SkBitmap bitmap;
  bitmap.allocN32Pixels(kImageSize, kImageSize);
  bitmap.eraseColor(color);
  return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
}

}  // namespace

class ThemeTrackingImageViewTest : public ViewsTestBase {
 public:
  void SetUp() override {
    ViewsTestBase::SetUp();
    widget_ = CreateTestWidget();
  }

  void TearDown() override {
    widget_.reset();
    ViewsTestBase::TearDown();
  }

  SkColor GetSimulatedBackgroundColor() const {
    return is_dark_ ? SK_ColorBLACK : SK_ColorWHITE;
  }

 protected:
  bool IsDarkMode() const { return is_dark_; }
  void SetIsDarkMode(bool is_dark) {
    is_dark_ = is_dark;
    if (view_)
      view_->OnThemeChanged();
  }

  void SetView(std::unique_ptr<ThemeTrackingImageView> view) {
    view_ = widget_->SetContentsView(std::move(view));
    view_->SetBounds(0, 0, kImageSize, kImageSize);
  }

  ThemeTrackingImageView* view() { return view_; }
  Widget* widget() { return widget_.get(); }

 private:
  std::unique_ptr<Widget> widget_;
  raw_ptr<ThemeTrackingImageView> view_ = nullptr;

  bool is_dark_ = false;
};

TEST_F(ThemeTrackingImageViewTest, CreateWithImageSkia) {
  gfx::ImageSkia light_image{CreateTestImage(SK_ColorRED)};
  gfx::ImageSkia dark_image{CreateTestImage(SK_ColorBLUE)};

  SetView(std::make_unique<ThemeTrackingImageView>(
      light_image, dark_image,
      base::BindRepeating(
          &ThemeTrackingImageViewTest::GetSimulatedBackgroundColor,
          base::Unretained(this))));

  ASSERT_FALSE(IsDarkMode());
  EXPECT_TRUE(gfx::test::AreBitmapsEqual(*view()->GetImage().bitmap(),
                                         *light_image.bitmap()));

  SetIsDarkMode(true);
  EXPECT_FALSE(gfx::test::AreBitmapsEqual(*view()->GetImage().bitmap(),
                                          *light_image.bitmap()));
  EXPECT_TRUE(gfx::test::AreBitmapsEqual(*view()->GetImage().bitmap(),
                                         *dark_image.bitmap()));
}

TEST_F(ThemeTrackingImageViewTest, CreateWithImageModel) {
  ui::ImageModel light_model{ui::ImageModel::FromVectorIcon(
      vector_icons::kSyncIcon, ui::kColorMenuIcon, kImageSize)};
  ui::ImageModel dark_model{ui::ImageModel::FromVectorIcon(
      vector_icons::kCallIcon, ui::kColorMenuIcon, kImageSize)};

  SetView(std::make_unique<ThemeTrackingImageView>(
      light_model, dark_model,
      base::BindRepeating(
          &ThemeTrackingImageViewTest::GetSimulatedBackgroundColor,
          base::Unretained(this))));

  ASSERT_FALSE(IsDarkMode());
  EXPECT_EQ(view()->GetImageModel(), light_model);

  SetIsDarkMode(true);
  EXPECT_NE(view()->GetImageModel(), light_model);
  EXPECT_EQ(view()->GetImageModel(), dark_model);
}

TEST_F(ThemeTrackingImageViewTest, SetLightImage) {
  ui::ImageModel light_model1{ui::ImageModel::FromVectorIcon(
      vector_icons::kSyncIcon, ui::kColorMenuIcon, kImageSize)};
  ui::ImageModel light_model2{ui::ImageModel::FromVectorIcon(
      vector_icons::kUsbIcon, ui::kColorMenuIcon, kImageSize)};
  ui::ImageModel dark_model{ui::ImageModel::FromVectorIcon(
      vector_icons::kCallIcon, ui::kColorMenuIcon, kImageSize)};

  SetView(std::make_unique<ThemeTrackingImageView>(
      light_model1, dark_model,
      base::BindRepeating(
          &ThemeTrackingImageViewTest::GetSimulatedBackgroundColor,
          base::Unretained(this))));

  ASSERT_FALSE(IsDarkMode());
  EXPECT_EQ(view()->GetImageModel(), light_model1);

  SetIsDarkMode(true);
  view()->SetLightImage(light_model2);
  // The image remains the one for dark mode.
  EXPECT_EQ(view()->GetImageModel(), dark_model);

  // Upon switching, the image is updated.
  SetIsDarkMode(false);
  EXPECT_EQ(view()->GetImageModel(), light_model2);

  // If light mode is currently on, the image is updated immediately.
  view()->SetLightImage(light_model1);
  EXPECT_EQ(view()->GetImageModel(), light_model1);
}

TEST_F(ThemeTrackingImageViewTest, SetDarkImage) {
  ui::ImageModel light_model{ui::ImageModel::FromVectorIcon(
      vector_icons::kSyncIcon, ui::kColorMenuIcon, kImageSize)};
  ui::ImageModel dark_model1{ui::ImageModel::FromVectorIcon(
      vector_icons::kUsbIcon, ui::kColorMenuIcon, kImageSize)};
  ui::ImageModel dark_model2{ui::ImageModel::FromVectorIcon(
      vector_icons::kCallIcon, ui::kColorMenuIcon, kImageSize)};

  SetView(std::make_unique<ThemeTrackingImageView>(
      light_model, dark_model1,
      base::BindRepeating(
          &ThemeTrackingImageViewTest::GetSimulatedBackgroundColor,
          base::Unretained(this))));

  ASSERT_FALSE(IsDarkMode());
  EXPECT_EQ(view()->GetImageModel(), light_model);

  SetIsDarkMode(true);
  EXPECT_EQ(view()->GetImageModel(), dark_model1);

  SetIsDarkMode(false);
  view()->SetDarkImage(dark_model2);
  EXPECT_EQ(view()->GetImageModel(), light_model);

  SetIsDarkMode(true);
  EXPECT_EQ(view()->GetImageModel(), dark_model2);

  view()->SetDarkImage(dark_model1);
  EXPECT_EQ(view()->GetImageModel(), dark_model1);
}

}  // namespace views