// Copyright 2025 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/gtk/os_settings_provider_gtk.h"

#include <glib-object.h>
#include <glib.h>

#include <string>

#include "base/functional/bind.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "ui/base/glib/scoped_gsignal.h"
#include "ui/gfx/color_utils.h"
#include "ui/gtk/gtk_compat.h"
#include "ui/gtk/gtk_util.h"
#include "ui/native_theme/native_theme.h"

namespace gtk {

OsSettingsProviderGtk::OsSettingsProviderGtk()
    : OsSettingsProvider(PriorityLevel::kProduction) {}

OsSettingsProviderGtk::~OsSettingsProviderGtk() = default;

ui::NativeTheme::PreferredColorScheme
OsSettingsProviderGtk::PreferredColorScheme() const {
  // The xdg-desktop-portal color-scheme preference (pushed in via
  // GtkUi::SetColorScheme) takes precedence when it expresses one.
  if (prefer_dark_) {
    return *prefer_dark_ ? ui::NativeTheme::PreferredColorScheme::kDark
                         : ui::NativeTheme::PreferredColorScheme::kLight;
  }

  // GTK has a dark mode setting called "gtk-application-prefer-dark-theme", but
  // this is really only used for themes that have a dark or light variant that
  // gets toggled based on this setting (eg. Adwaita).  Most dark themes do not
  // have a light variant and aren't affected by the setting.  Because of this,
  // experimentally check if the theme is dark by checking if the window
  // background color is dark.
  return color_utils::IsDark(GetBgColor({}))
             ? ui::NativeTheme::PreferredColorScheme::kDark
             : ui::NativeTheme::PreferredColorScheme::kLight;
}

ui::NativeTheme::PreferredContrast OsSettingsProviderGtk::PreferredContrast()
    const {
  // GTK doesn't have a native high contrast setting.  Rather, it's implied by
  // the theme name.  The only high contrast GTK themes that I know of are
  // HighContrast (GNOME) and ContrastHighInverse (MATE).  So infer the contrast
  // based on if the theme name contains both "high" and "contrast",
  // case-insensitive.
  const std::string theme_name =
      base::ToLowerASCII(GetGtkSettingsStringProperty(
          gtk_settings_get_default(), "gtk-theme-name"));
  const bool high_contrast =
      theme_name.contains("high") && theme_name.contains("contrast");
  return high_contrast ? ui::NativeTheme::PreferredContrast::kMore
                       : ui::NativeTheme::PreferredContrast::kNoPreference;
}

bool OsSettingsProviderGtk::PrefersOverlayScrollbars() const {
  gboolean overlay_scrolling = TRUE;
  GtkSettings* settings = gtk_settings_get_default();
  if (settings && g_object_class_find_property(G_OBJECT_GET_CLASS(settings),
                                               "gtk-overlay-scrolling")) {
    g_object_get(settings, "gtk-overlay-scrolling", &overlay_scrolling,
                 nullptr);
  }
  return overlay_scrolling;
}

base::TimeDelta OsSettingsProviderGtk::CaretBlinkInterval() const {
  // Default value for `gtk-cursor-blink-time` from
  // https://docs.gtk.org/gtk3/property.Settings.gtk-cursor-blink-time.html.
  gint cursor_blink_time = 1200;
  gboolean cursor_blink = TRUE;
  g_object_get(gtk_settings_get_default(), "gtk-cursor-blink-time",
               &cursor_blink_time, "gtk-cursor-blink", &cursor_blink, nullptr);
  return cursor_blink ? base::Milliseconds(cursor_blink_time / 2)
                      : base::TimeDelta();
}

std::optional<SkColor> OsSettingsProviderGtk::AccentColor() const {
  return accent_color_;
}

void OsSettingsProviderGtk::SetAccentColor(
    std::optional<SkColor> accent_color) {
  if (accent_color_ == accent_color) {
    return;
  }
  accent_color_ = accent_color;
  NotifyOnSettingsChanged();
}

void OsSettingsProviderGtk::SetColorScheme(std::optional<bool> prefer_dark) {
  if (prefer_dark_ == prefer_dark) {
    return;
  }
  prefer_dark_ = prefer_dark;
  NotifyOnSettingsChanged();
}

ScopedGSignal OsSettingsProviderGtk::ConnectSignal(const gchar* name) {
  return ScopedGSignal(gtk_settings_get_default(), name,
                       base::BindRepeating(&OsSettingsProviderGtk::OnSignal,
                                           base::Unretained(this)),
                       G_CONNECT_AFTER);
}

void OsSettingsProviderGtk::OnSignal(GtkSettings*, GtkParamSpec*) {
  NotifyOnSettingsChanged();
}

}  // namespace gtk
