// SPDX-License-Identifier: MPL-2.0

/*
 * Copyright (c) 2025 ozone10
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work from the win32-darkmode project:
 *  https://github.com/ysc3839/win32-darkmode
 *  which is covered by the MIT License.
 *  See LICENSE-win32-darkmode for more information.
 */


#include "StdAfx.h"

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif

#include "DarkMode.h"

#include "DarkModeHook.h"
#include "ModuleHelper.h"

#if defined(_MSC_VER) && _MSC_VER >= 1800
#pragma warning(disable : 4191)
#elif defined(__GNUC__)
#include <cwchar>
#endif

enum IMMERSIVE_HC_CACHE_MODE
{
	IHCM_USE_CACHED_VALUE,
	IHCM_REFRESH
};

// 1903 18362
enum class PreferredAppMode
{
	Default,
	AllowDark,
	ForceDark,
	ForceLight,
	Max
};

#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
static constexpr DWORD g_win10Build1903 = 18362;

enum WINDOWCOMPOSITIONATTRIB
{
	WCA_UNDEFINED = 0,
	WCA_NCRENDERING_ENABLED = 1,
	WCA_NCRENDERING_POLICY = 2,
	WCA_TRANSITIONS_FORCEDISABLED = 3,
	WCA_ALLOW_NCPAINT = 4,
	WCA_CAPTION_BUTTON_BOUNDS = 5,
	WCA_NONCLIENT_RTL_LAYOUT = 6,
	WCA_FORCE_ICONIC_REPRESENTATION = 7,
	WCA_EXTENDED_FRAME_BOUNDS = 8,
	WCA_HAS_ICONIC_BITMAP = 9,
	WCA_THEME_ATTRIBUTES = 10,
	WCA_NCRENDERING_EXILED = 11,
	WCA_NCADORNMENTINFO = 12,
	WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
	WCA_VIDEO_OVERLAY_ACTIVE = 14,
	WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
	WCA_DISALLOW_PEEK = 16,
	WCA_CLOAK = 17,
	WCA_CLOAKED = 18,
	WCA_ACCENT_POLICY = 19,
	WCA_FREEZE_REPRESENTATION = 20,
	WCA_EVER_UNCLOAKED = 21,
	WCA_VISUAL_OWNER = 22,
	WCA_HOLOGRAPHIC = 23,
	WCA_EXCLUDED_FROM_DDA = 24,
	WCA_PASSIVEUPDATEMODE = 25,
	WCA_USEDARKMODECOLORS = 26,
	WCA_LAST = 27
};

struct WINDOWCOMPOSITIONATTRIBDATA
{
	WINDOWCOMPOSITIONATTRIB Attrib;
	PVOID pvData;
	SIZE_T cbData;
};
#endif

using fnRtlGetNtVersionNumbers = void (WINAPI*)(LPDWORD major, LPDWORD minor, LPDWORD build);
#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
using fnSetWindowCompositionAttribute = BOOL (WINAPI*)(HWND hWnd, WINDOWCOMPOSITIONATTRIBDATA*);
#endif
// 1809 17763
#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
using fnShouldAppsUseDarkMode = auto (WINAPI*)() -> bool; // ordinal 132, is not reliable on 1903+
#endif
using fnAllowDarkModeForWindow = auto (WINAPI*)(HWND hWnd, bool allow) -> bool; // ordinal 133
#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
using fnAllowDarkModeForApp = auto (WINAPI*)(bool allow) -> bool; // ordinal 135, in 1809
#endif
using fnFlushMenuThemes = void (WINAPI*)(); // ordinal 136
#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
using fnIsDarkModeAllowedForWindow = auto (WINAPI*)(HWND hWnd) -> bool; // ordinal 137
#endif
using fnRefreshImmersiveColorPolicyState = void (WINAPI*)(); // ordinal 104
using fnGetIsImmersiveColorUsingHighContrast = auto (WINAPI*)(IMMERSIVE_HC_CACHE_MODE mode) -> bool; // ordinal 106
// 1903 18362
//using fnShouldSystemUseDarkMode = auto (WINAPI*)() -> bool; // ordinal 138
using fnSetPreferredAppMode = auto (WINAPI*)(PreferredAppMode appMode) -> PreferredAppMode; // ordinal 135, in 1903
//using fnIsDarkModeAllowedForApp = auto (WINAPI*)() -> bool; // ordinal 139

#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
static fnSetWindowCompositionAttribute pfSetWindowCompositionAttribute = nullptr;
static fnShouldAppsUseDarkMode pfShouldAppsUseDarkMode = nullptr;
#endif
static fnAllowDarkModeForWindow pfAllowDarkModeForWindow = nullptr;
#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
static fnAllowDarkModeForApp pfAllowDarkModeForApp = nullptr;
#endif
static fnFlushMenuThemes pfFlushMenuThemes = nullptr;
#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
static fnIsDarkModeAllowedForWindow pfIsDarkModeAllowedForWindow = nullptr;
#endif
static fnRefreshImmersiveColorPolicyState pfRefreshImmersiveColorPolicyState = nullptr;
static fnGetIsImmersiveColorUsingHighContrast pfGetIsImmersiveColorUsingHighContrast = nullptr;
// 1903 18362
//static fnShouldSystemUseDarkMode pfShouldSystemUseDarkMode = nullptr;
static fnSetPreferredAppMode pfSetPreferredAppMode = nullptr;

bool g_darkModeSupported = false;
bool g_darkModeEnabled = false;
static DWORD g_buildNumber = 0;

[[nodiscard]] static bool ShouldAppsUseDarkMode()
{
#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
	if (g_buildNumber < g_win10Build1903)
	{
		if (pfShouldAppsUseDarkMode == nullptr)
		{
			return false;
		}
		return pfShouldAppsUseDarkMode();
	}
	else
#endif
	{
		return true;
	}
}

bool dmlib::win32api::AllowDarkModeForWindow(HWND hWnd, bool allow)
{
	if (g_darkModeSupported && (pfAllowDarkModeForWindow != nullptr))
	{
		return pfAllowDarkModeForWindow(hWnd, allow);
	}
	return false;
}

bool dmlib::win32api::IsHighContrast()
{
	HIGHCONTRASTW highContrast{};
	highContrast.cbSize = sizeof(HIGHCONTRASTW);
	if (::SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(HIGHCONTRASTW), &highContrast, FALSE) == TRUE)
	{
		return (highContrast.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
	}
	return false;
}

#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
void dmlib::win32api::SetTitleBarThemeColor(HWND hWnd, BOOL dark)
{
	if (g_buildNumber < g_win10Build1903)
	{
		::SetPropW(hWnd, L"UseImmersiveDarkModeColors", reinterpret_cast<HANDLE>(static_cast<intptr_t>(dark)));
	}
	else if (pfSetWindowCompositionAttribute != nullptr)
	{
		WINDOWCOMPOSITIONATTRIBDATA data{ WCA_USEDARKMODECOLORS, &dark, sizeof(dark) };
		pfSetWindowCompositionAttribute(hWnd, &data);
	}
}

void dmlib::win32api::RefreshTitleBarThemeColor(HWND hWnd)
{
	BOOL dark = FALSE;
	if (pfIsDarkModeAllowedForWindow != nullptr && pfShouldAppsUseDarkMode != nullptr)
	{
		if (pfIsDarkModeAllowedForWindow(hWnd) && pfShouldAppsUseDarkMode() && !IsHighContrast())
		{
			dark = TRUE;
		}
	}

	SetTitleBarThemeColor(hWnd, dark);
}
#endif

bool dmlib::win32api::IsColorSchemeChangeMessage(LPARAM lParam)
{
	bool isMsg = false;
	if ((lParam != 0) // NULL
		&& (_wcsicmp(reinterpret_cast<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0))
	{
		isMsg = true;
	}

	if (isMsg)
	{
		if (pfRefreshImmersiveColorPolicyState != nullptr)
		{
			pfRefreshImmersiveColorPolicyState();
		}

		if (pfGetIsImmersiveColorUsingHighContrast != nullptr)
		{
			pfGetIsImmersiveColorUsingHighContrast(IHCM_REFRESH);
		}
	}

	return isMsg;
}

bool dmlib::win32api::IsColorSchemeChangeMessage(UINT uMsg, LPARAM lParam)
{
	if (uMsg == WM_SETTINGCHANGE)
	{
		return dmlib::win32api::IsColorSchemeChangeMessage(lParam);
	}
	return false;
}

void dmlib::win32api::AllowDarkModeForApp(bool allow)
{
	if (pfSetPreferredAppMode != nullptr)
	{
		pfSetPreferredAppMode(allow ? PreferredAppMode::ForceDark : PreferredAppMode::Default);
	}
#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
	else if (pfAllowDarkModeForApp != nullptr)
	{
		pfAllowDarkModeForApp(allow);
	}
#endif
}

static void FlushMenuThemes()
{
	if (pfFlushMenuThemes != nullptr)
	{
		pfFlushMenuThemes();
	}
}

#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
static constexpr DWORD g_win10Build = 17763;
#else
static constexpr DWORD g_win10Build = 19044; // 21H2 latest LTSC, 22H2 19045 latest GA
#endif
static constexpr DWORD g_win11Build = 22000;

bool dmlib::win32api::IsWindows10() // or later OS version
{
	return (g_buildNumber >= g_win10Build);
}

bool dmlib::win32api::IsWindows11() // or later OS version
{
	return (g_buildNumber >= g_win11Build);
}

[[nodiscard]] static bool CheckBuildNumber(DWORD buildNumber)
{
#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
	static constexpr size_t nWin10Builds = 8;
	// Windows 10 builds { 1809, 1903, 1909, 2004, 20H2, 21H1, 21H2, 22H2 }
	static constexpr DWORD win10Builds[nWin10Builds] = { 17763, 18362, 18363, 19041, 19042, 19043, 19044, 19045 };

	// Windows 10 any version >= 22H2 and Windows 11
	if ((buildNumber >= win10Builds[nWin10Builds - 1])) // || buildNumber > g_win11Build
	{
		return true;
	}

	for (size_t i = 0; i < nWin10Builds; ++i)
	{
		if (buildNumber == win10Builds[i])
		{
			return true;
		}
	}
	return false;
#else
	return (buildNumber >= g_win10Build); // || buildNumber > g_win11Build
#endif
}

DWORD dmlib::win32api::GetWindowsBuildNumber()
{
	return g_buildNumber;
}

void dmlib::win32api::InitDarkMode()
{
	static bool isInit = false;
	if (isInit)
	{
		return;
	}

	fnRtlGetNtVersionNumbers RtlGetNtVersionNumbers = nullptr;
	HMODULE hNtdll = ::GetModuleHandleW(L"ntdll.dll");
	if (hNtdll != nullptr && loadFn(hNtdll, RtlGetNtVersionNumbers, "RtlGetNtVersionNumbers"))
	{
		DWORD major = 0;
		DWORD minor = 0;
		RtlGetNtVersionNumbers(&major, &minor, &g_buildNumber);
		g_buildNumber &= ~0xF0000000;
		if (major == 10 && minor == 0 && CheckBuildNumber(g_buildNumber))
		{
			const ModuleHandle moduleUxtheme(L"uxtheme.dll");
			if (moduleUxtheme.isLoaded())
			{
				const HMODULE& hUxtheme = moduleUxtheme.get();

				bool ptrFnOrd135NotNullptr = false;
#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
				bool ptrFnOrd132NotNullptr = true;
				if (g_buildNumber < g_win10Build1903)
				{
					ptrFnOrd132NotNullptr = loadFn(hUxtheme, pfShouldAppsUseDarkMode, 132);
					ptrFnOrd135NotNullptr = loadFn(hUxtheme, pfAllowDarkModeForApp, 135);
				}
				else
#endif
				{
					ptrFnOrd135NotNullptr = loadFn(hUxtheme, pfSetPreferredAppMode, 135);
				}

				if (ptrFnOrd135NotNullptr
#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
					&& ptrFnOrd132NotNullptr
#endif
#if defined(_DARKMODELIB_USE_SCROLLBAR_FIX) && (_DARKMODELIB_USE_SCROLLBAR_FIX > 0)
					&& dmlib::hook::LoadOpenNcThemeData(hUxtheme)
#endif
					&& loadFn(hUxtheme, pfRefreshImmersiveColorPolicyState, 104)
					&& loadFn(hUxtheme, pfAllowDarkModeForWindow, 133)
					&& loadFn(hUxtheme, pfFlushMenuThemes, 136))
				{
					g_darkModeSupported = true;
				}

				loadFn(hUxtheme, pfGetIsImmersiveColorUsingHighContrast, 106);
#if defined(_DARKMODELIB_ALLOW_OLD_OS) && (_DARKMODELIB_ALLOW_OLD_OS > 0)
				static constexpr DWORD build2004 = 19041;
				if (g_buildNumber < build2004 && g_darkModeSupported)
				{
					if (loadFn(hUxtheme, pfIsDarkModeAllowedForWindow, 137))
					{
						HMODULE hUser32 = GetModuleHandleW(L"user32.dll");
						if (hUser32 != nullptr)
						{
							loadFn(hUser32, pfSetWindowCompositionAttribute, "SetWindowCompositionAttribute");
						}
					}
				}
#endif
				isInit = true;
			}
		}
	}
}

void dmlib::win32api::SetDarkMode(bool useDark, [[maybe_unused]] bool fixDarkScrollbar)
{
	if (g_darkModeSupported)
	{
		dmlib::win32api::AllowDarkModeForApp(useDark);
		FlushMenuThemes();
#if defined(_DARKMODELIB_USE_SCROLLBAR_FIX) && (_DARKMODELIB_USE_SCROLLBAR_FIX > 0)
		if (fixDarkScrollbar)
		{
			FixDarkScrollBar();
		}
#endif
		g_darkModeEnabled = useDark && ShouldAppsUseDarkMode() && !dmlib::win32api::IsHighContrast();
	}
}
