using System;
using System.Collections.Generic;
using System.Linq;
using Silk.NET.Windowing;
namespace AcDream.App.Rendering;
///
/// #391 (user-directed, 2026-08-13): the ONE source of the resolutions the
/// client offers. Production enumerates the display's real mode list
/// () and curates it to modern
/// widescreen formats that fit the desktop; the Config dropdown, and later
/// the fullscreen mode-switch validation (#376/#388), read the same catalog
/// so an offered mode is by construction a supported one.
///
/// Retail deviation, register-rowed with #391: retail listed the
/// adapter's complete enumeration including 4:3 legacy modes and authored
/// 800x600 as the Config default
/// (gmConfigUI::InitOptions SetDefaultValue(0x03200258)). We curate
/// deliberately — modern formats only — and the Defaults value becomes the
/// desktop's own mode (always present in the curated list).
///
/// Write-once static owner: the catalog is immutable hardware truth
/// captured at startup on the windowing thread (the same shape as the
/// platform facts GraphicalHostPlatformServices owns). Fixture,
/// UI-Studio, and headless callers never install one and fall back to
/// DisplaySettings.AvailableResolutions at the consuming seam.
///
internal static class DisplayModeCatalog
{
private static IReadOnlyList? _resolutions;
private static string? _desktopResolution;
/// The curated list, or null when no catalog was installed
/// (fixture/headless callers — consumers fall back to the static
/// preset ladder).
public static IReadOnlyList? Resolutions => _resolutions;
/// The desktop's current mode as a "WxH" string — the Config
/// Resolution row's Defaults value in production (see the class doc for
/// why this replaces retail's authored 800x600). Null when no catalog
/// was installed.
public static string? DesktopResolution => _desktopResolution;
/// Captures the catalog from the window's monitor at startup.
/// A null monitor or an empty curated result leaves the catalog
/// uninstalled (consumers keep the static fallback). Safe to call once
/// per process launch; a repeat call overwrites with equally-fresh
/// hardware truth.
public static void InstallFromWindow(IWindow window)
{
ArgumentNullException.ThrowIfNull(window);
IMonitor? monitor = window.Monitor;
if (monitor is null)
return;
VideoMode current = monitor.VideoMode;
if (current.Resolution is not { } desktop || desktop.X <= 0 || desktop.Y <= 0)
return;
IEnumerable<(int W, int H)> modes = monitor
.GetAllVideoModes()
.Select(m => m.Resolution)
.Where(r => r.HasValue)
.Select(r => (r!.Value.X, r.Value.Y));
IReadOnlyList curated = Curate(modes, (desktop.X, desktop.Y));
if (curated.Count == 0)
return;
_resolutions = curated;
_desktopResolution = $"{desktop.X}x{desktop.Y}";
}
/// Test seam: clears the installed catalog.
internal static void ResetForTests()
{
_resolutions = null;
_desktopResolution = null;
}
///
/// The pure curation rule (#391): keep a mode iff
/// - it is a modern widescreen format (16:9, 16:10, or ultrawide 21:9 /
/// 32:9, matched with a small tolerance so 1366x768 and friends pass),
/// - it is at least 1280 wide (no legacy-era sizes), and
/// - it fits the desktop (a windowed pick larger than the desktop can
/// only silently clamp — if it cannot exist, it is not offered).
/// The desktop mode itself is always included even if its aspect is
/// unusual (it is by definition displayable), refresh-rate duplicates
/// collapse to one WxH entry, and the list sorts ascending by width then
/// height so the dropdown reads naturally.
///
internal static IReadOnlyList Curate(
IEnumerable<(int W, int H)> modes,
(int W, int H) desktop)
{
// The modern aspect families, as width/height ratios.
ReadOnlySpan modernAspects =
[
16f / 9f,
16f / 10f,
21f / 9f,
32f / 9f,
];
var keep = new SortedSet<(int W, int H)>(
Comparer<(int W, int H)>.Create(static (a, b) =>
a.W != b.W ? a.W.CompareTo(b.W) : a.H.CompareTo(b.H)));
foreach ((int w, int h) in modes)
{
if (w <= 0 || h <= 0)
continue;
if (w > desktop.W || h > desktop.H)
continue;
if ((w, h) == desktop)
{
keep.Add((w, h));
continue;
}
if (w < 1280)
continue;
float aspect = w / (float)h;
bool modern = false;
foreach (float family in modernAspects)
{
// ±2.5% covers the near-miss members of a family (1366x768 is
// 1.7786 vs 16:9's 1.7778; 3440x1440 is 2.3889 vs 21:9's
// 2.3333, a 2.4% miss — while a real 4:3 (1.3333) or 5:4
// (1.25) stays an order of magnitude outside every family).
if (MathF.Abs(aspect - family) <= family * 0.025f)
{
modern = true;
break;
}
}
if (modern)
keep.Add((w, h));
}
return keep.Select(static m => $"{m.W}x{m.H}").ToArray();
}
}