#ifndef DISPLAY_MODE_MANAGER_H_
#define DISPLAY_MODE_MANAGER_H_

#include <Windows.h>

#include <optional>
#include <string>
#include <vector>

namespace mpv {

struct DisplayMode {
  DWORD width;
  DWORD height;
  DWORD refresh_rate;
};

// Identifiers for a display target in the DisplayConfig API.
struct DisplayConfigId {
  LUID adapter_id;
  UINT32 id;
};

// Windows-runner-internal boundary for deterministic crash-recovery tests.
// Production uses the Win32/registry implementation in display_mode_manager.cpp.
class DisplayRecoveryBackend {
 public:
  virtual ~DisplayRecoveryBackend() = default;

  virtual bool RecordExists() const = 0;
  virtual bool ReadDWORD(const wchar_t* value_name, DWORD& value) = 0;
  virtual bool ReadString(const wchar_t* value_name, std::wstring& value) = 0;
  virtual bool WriteDWORD(const wchar_t* value_name, DWORD value) = 0;
  virtual bool WriteString(const wchar_t* value_name, const std::wstring& value) = 0;
  virtual bool IsDevicePresent(const std::wstring& device_name) const = 0;
  virtual bool RestoreMode(const std::wstring& device_name, DWORD width, DWORD height, DWORD refresh_rate) = 0;
  virtual bool RestoreHDR(const std::wstring& device_name, bool enabled) = 0;
  virtual bool ClearMarker(const wchar_t* value_name) = 0;
  virtual bool DeleteRecord() = 0;
};

// Manages Windows display mode switching (refresh rate, HDR) for video playback.
// Pure Win32 utility — no mpv or Flutter dependency.
//
// References:
//   ChangeDisplaySettingsExW:
//   https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-changedisplaysettingsexw
//   EnumDisplaySettingsW:
//   https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaysettingsw
//   DisplayConfigGetDeviceInfo:
//   https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-displayconfiggetdeviceinfo
//   DisplayConfigSetDeviceInfo:
//   https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-displayconfigsetdeviceinfo
//   Kodi impl: xbmc/platform/win32/DisplayUtilsWin32.cpp, xbmc/platform/win32/WIN32Util.cpp
class DisplayModeManager {
 public:
  DisplayModeManager();
  ~DisplayModeManager();

  // --- Refresh rate / resolution ---

  // Enumerate available display modes for the monitor containing the window.
  std::vector<DisplayMode> EnumerateDisplayModes(HWND window);

  // Get the current display mode.
  DisplayMode GetCurrentMode(HWND window);

  // Save the current mode for later restoration.
  void SaveOriginalMode(HWND window);

  // Change the display mode (refresh rate and/or resolution).
  // Uses CDS_FULLSCREEN flag. Implements Kodi's Win8+ workaround for 24/48/60Hz.
  // Returns true on success.
  bool SetDisplayMode(HWND window, DWORD width, DWORD height, DWORD refresh_rate);

  // Restore the previously saved display mode.
  bool RestoreOriginalMode(HWND window);

  // Returns true if a mode change has been applied (and not yet restored).
  bool IsModeChanged() const { return mode_changed_; }

  // --- HDR ---

  // Check if the display supports HDR (not just ACM/WCG).
  // Uses advancedColorSupported && !wideColorEnforced (pre-24H2)
  // or highDynamicRangeSupported (Win11 24H2+).
  bool IsHDRSupported(HWND window);

  // Check if HDR is currently enabled.
  bool IsHDREnabled(HWND window);

  // Save the current HDR state for later restoration.
  void SaveOriginalHDRState(HWND window);

  // Enable or disable system HDR.
  // Saves/restores DEVMODEW around the toggle (Windows changes display mode on HDR state change).
  // Uses SET_HDR_STATE (type 16) on Win11 24H2+, SET_ADVANCED_COLOR_STATE (type 10) on older.
  bool SetHDREnabled(HWND window, bool enabled);

  // Restore the previously saved HDR state.
  bool RestoreOriginalHDRState(HWND window);

  // Returns true if an HDR state change has been applied (and not yet restored).
  bool IsHDRChanged() const { return hdr_changed_; }

  // --- Crash recovery ---

  // Persist a complete original followed by its operation marker. A failed
  // non-live recovery remains retryable, but durably permits a later
  // conflicting request of the same kind to replace it. Conflicting originals
  // are staged in an inactive slot. A persisted handoff keeps both originals
  // recoverable across the selector switch and OS call; success confirms the
  // new slot, while OS failure rolls back the prior selector and eligibility.
  // Reusing the same original revokes permission before it becomes live.
  // These runner-internal seams make the crash ordering deterministic in tests.
  static bool PrepareModeRecovery(
      DisplayRecoveryBackend& backend, const std::wstring& device_name, DWORD width, DWORD height, DWORD refresh_rate);
  static bool PrepareHDRRecovery(DisplayRecoveryBackend& backend, const std::wstring& device_name, bool enabled);

  // Check for and recover from a prior crash that left display settings
  // changed. Successful operation markers and their takeover dispositions are
  // cleared independently. Failed operations remain recovery-first until a
  // conflicting same-kind preparation consumes their persisted disposition.
  static bool RecoverIfNeeded();
  static bool RecoverIfNeeded(DisplayRecoveryBackend& backend);
#if defined(PLEZY_DISPLAY_MODE_MANAGER_TESTING)
  // Exercise persisted lifecycle exits and per-operation live ownership without
  // touching a real display or registry.
  static bool CompleteRecoveryOperationForTesting(DisplayRecoveryBackend& backend, bool mode);
  static bool FinalizePreparedRecoveryForTesting(DisplayRecoveryBackend& backend, bool mode, bool os_apply_succeeded);
  static bool RecoverIfNeededForTesting(DisplayRecoveryBackend& backend, bool mode_is_live, bool hdr_is_live);
#endif

 private:
  friend class Win32DisplayRecoveryBackend;

  // Get the GDI device name for the monitor containing the window.
  static std::wstring GetMonitorDeviceName(HWND window);

  // Get the DisplayConfig target ID for a given GDI device name.
  // Follows Kodi's GetDisplayTargetId pattern.
  static std::optional<DisplayConfigId> GetDisplayTargetId(const std::wstring& gdi_device_name);

  // Get all active display config paths (with retry for ERROR_INSUFFICIENT_BUFFER).
  static std::vector<DISPLAYCONFIG_PATH_INFO> GetDisplayConfigPaths();

  // Check if running on Win11 24H2 or newer.
  static bool IsWin11_24H2OrNewer();

  // Toggle HDR via DisplayConfig (version-dispatched).
  static LONG SetHDRStateForTarget(const DisplayConfigId& target, bool enabled);

  // Stored original mode for restoration.
  std::wstring original_device_name_;
  DEVMODEW original_devmode_ = {};
  bool mode_changed_ = false;

  // Stored original HDR state for restoration.
  std::wstring original_hdr_device_name_;
  bool original_hdr_enabled_ = false;
  bool hdr_changed_ = false;
};

}  // namespace mpv

#endif  // DISPLAY_MODE_MANAGER_H_
