#include "sys/glib/dark_theme.h"

#include <array>
#include <cstring>
#include <gio/gio.h>
#include <memory>
#include <string_view>

namespace glib {

/* deleters */
template<typename T>
struct g_object_del {
	void operator()(T *p) const { ::g_object_unref(p); };
};

template<typename T>
struct g_variant_del {
	void operator()(T *p) const { ::g_variant_unref(p); };
};

template<typename T>
struct g_malloc_del {
	void operator()(T *p) const { ::g_free(p); };
};

template<typename T>
using GObjectPtr = std::unique_ptr<T, g_object_del<T>>;

template<typename T>
using GVariantPtr = std::unique_ptr<T, g_variant_del<T>>;

template<typename T>
using GMallocPtr = std::unique_ptr<T, g_malloc_del<T>>;

/* not really "glib" but GNOME-related enough */
bool IsGTKThemeDark(const std::string_view str)
{
	/* if that doesn't exist, use the GTK theme and check for some known
	 * suffixes. if one is found, return
	 *
	 * XXX probably better to use case folding here */
	static constexpr std::array<std::string_view, 3> suffixes = {
	    "-dark",   /* Adwaita-dark */
	    "-Dark",   /* Arc-Dark */
	    "-Darker", /* Arc-Darker */
	};

	for (const auto &suffix : suffixes) {
		if (str.size() < suffix.size())
			continue;

		if (std::equal(str.data() + str.size() - suffix.length(), str.data() + str.size(), suffix.begin(),
		               suffix.end()))
			return true;
	}

	return false;
}

bool IsInDarkTheme()
{
	GObjectPtr<GSettings> settings(::g_settings_new("org.gnome.desktop.interface"));
	if (!settings)
		return false;

	{
		/* first attempt to get the colorscheme */
		GVariantPtr<GVariant> val(::g_settings_get_value(settings.get(), "color-scheme"));
		if (!val)
			return false;

		/* this is free'd upon deconstruction of the GVariantPtr */
		gsize size;
		const gchar *str = ::g_variant_get_string(val.get(), &size);
		if (!str)
			return false;

		bool success = !std::strncmp(str, "prefer-dark", size);

		if (success)
			return true;
	}

	{

		GVariantPtr<GVariant> gtk_theme(::g_settings_get_value(settings.get(), "gtk-theme"));
		if (!gtk_theme)
			return false;

		gsize size;
		const gchar *str = ::g_variant_get_string(gtk_theme.get(), &size);
		if (!str)
			return false;

		if (IsGTKThemeDark({str, size}))
			return true;
	}

	/* welp, we tried */
	return false;
}

} // namespace glib
