#pragma once

#include <FL/Fl_Widget.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Menu_.H>
#include <FL/Fl_Input_.H>
#include <FL/Fl_Browser_.H>
#include <FL/Fl_Choice.H>

#ifdef WIN32
#include <windows.h>
#endif

extern float g_scale;

inline int scale(int v) { return int(v * g_scale); }

inline void scaleWidgetFonts(Fl_Widget* w)
{
	if (!w) return;

	// Scale the widget's label
	w->labelsize(scale(w->labelsize()));

	// Widgets that also have textsize
	if (auto m = dynamic_cast<Fl_Menu_*>(w))
		m->textsize(scale(m->textsize()));
	else if (auto ch = dynamic_cast<Fl_Choice*>(w))
		ch->textsize(scale(ch->textsize()));
	else if (auto inp = dynamic_cast<Fl_Input_*>(w))
		inp->textsize(scale(inp->textsize()));
	else if (auto br = dynamic_cast<Fl_Browser_*>(w))
		br->textsize(scale(br->textsize()));

	// Recurse if this widget is a group
	if (auto g = dynamic_cast<Fl_Group*>(w)) {
		for (int i = 0; i < g->children(); ++i)
			scaleWidgetFonts(g->child(i));
	}
}

inline float computeDpiScale()
{
#ifdef WIN32
	auto fn = (UINT (WINAPI*)()) GetProcAddress(GetModuleHandleA("user32.dll"), "GetDpiForSystem");
	if (!fn) return 1.0f;
	return fn() / 96.0f;
#elif defined(__APPLE__)
	return 1.0f; // No idea how to do this on macOS or how to test it. TODO: Here it would be very nice, as mac usually has very high DPI.
#else // Linux
#ifdef __X11__
	// X11 method
	if (Display* d = XOpenDisplay(nullptr)) {
		Screen* s = DefaultScreenOfDisplay(d);
		if (s && s->mwidth > 0) {
			int w_px = s->width;
			int w_mm = s->mwidth;
			float dpi = float(w_px) / (w_mm / 25.4f);
			XCloseDisplay(d);
			return dpi / 96.0f;
		}
		XCloseDisplay(d);
	}
	// Wayland fallback via environment variables
	if (const char* scaleEnv = std::getenv("GDK_SCALE")) {
		float scale = std::atof(scaleEnv);
		if (scale > 0) return scale;
	}
	if (const char* scaleEnv2 = std::getenv("WAYLAND_SCALE")) {
		float scale = std::atof(scaleEnv2);
		if (scale > 0) return scale;
	}
	return 1.0f; // default Linux fallback
#else
	return 1.0f; // generic Linux fallback
#endif
	return 1.0f; // For unknown OS, generic fallback
#endif
}

inline void enableDpiAwareness()
{ // TODO: Does this break for Win<10?
#ifdef WIN32
	HMODULE user32 = LoadLibraryA("user32.dll");
	if (user32) {
		auto setContext = (BOOL(WINAPI*)(HANDLE)) GetProcAddress(user32, "SetProcessDpiAwarenessContext");

		if (setContext) {
			setContext((HANDLE)-4); // DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
			FreeLibrary(user32);
			return;
		}
		FreeLibrary(user32);
	}
#endif /* #ifdef WIN32 */
}
