#pragma once
#include <Windows.h>
#include <string>
#include <algorithm>
#include <shlwapi.h>
#include <vector>
#include <iomanip>
#include <functional>
#include <sstream>
#include "strcmp.h"
#include "version.h"
#include "module.h"
typedef version S_VERSION;
template <typename T> constexpr T Max (T l, T r) { return l > r ? l : r; }
template <typename T> constexpr T Max (T l, T m, T r) { return Max (Max (l, r), m); }
template <typename T> constexpr T Max (T l, T ml, T mr, T r) { return Max (Max (l, ml), Max (mr, r)); }
template <typename CharT> std::basic_string <CharT> replace_substring
(
	const std::basic_string <CharT> &str,
	const std::basic_string <CharT> &from,
	const std::basic_string <CharT> &to
)
{
	if (from.empty ()) return str;
	std::basic_string  <CharT> result;
	size_t pos = 0;
	size_t start_pos;
	while ((start_pos = str.find (from, pos)) != std::basic_string<CharT>::npos)
	{
		result.append (str, pos, start_pos - pos);
		result.append (to);
		pos = start_pos + from.length ();
	}
	result.append (str, pos, str.length () - pos);
	return result;
}
std::string GetProgramRootDirectoryA (HMODULE hModule hModule_DefaultParam)
{
	char path [MAX_PATH];
	if (GetModuleFileNameA (hModule, path, MAX_PATH))
	{
		std::string dir (path);
		size_t pos = dir.find_last_of ("\\/");
		if (pos != std::string::npos)
		{
			dir = dir.substr (0, pos);
		}
		return dir;
	}
	return "";
}
std::wstring GetProgramRootDirectoryW (HMODULE hModule hModule_DefaultParam)
{
	wchar_t path [MAX_PATH];
	if (GetModuleFileNameW (hModule, path, MAX_PATH))
	{
		std::wstring dir (path);
		size_t pos = dir.find_last_of (L"\\/");
		if (pos != std::wstring::npos)
		{
			dir = dir.substr (0, pos);
		}
		return dir;
	}
	return L"";
}
std::string EnsureTrailingSlash (const std::string &path)
{
	if (path.empty ()) return path;  // 空路径直接返回

	char lastChar = path.back ();
	if (lastChar == '\\' || lastChar == '/')
		return path;  // 已有分隔符，直接返回
					  // 根据系统或原路径格式添加适当的分隔符
	char separator = (path.find ('/') != std::string::npos) ? '/' : '\\';
	return path + separator;
}
std::wstring EnsureTrailingSlash (const std::wstring &path)
{
	if (path.empty ()) return path;

	wchar_t lastChar = path.back ();
	if (lastChar == L'\\' || lastChar == L'/')
		return path;

	wchar_t separator = (path.find (L'/') != std::wstring::npos) ? L'/' : L'\\';
	return path + separator;
}
bool IsFileExistsW (LPCWSTR filename)
{
	DWORD dwAttrib = GetFileAttributesW (filename);
	return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
bool IsFileExistsA (LPCSTR filename)
{
	DWORD dwAttrib = GetFileAttributesA (filename);
	return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
bool IsFileExists (LPWSTR filePath) { return IsFileExistsW (filePath); }
bool IsFileExists (LPCSTR filePath) { return IsFileExistsA (filePath); }
bool IsFileExists (const std::string &filePath) { return IsFileExistsA (filePath.c_str ()); }
bool IsFileExists (const std::wstring &filePath) { return IsFileExistsW (filePath.c_str ()); }
bool IsDirectoryExistsA (LPCSTR path)
{
	DWORD attributes = GetFileAttributesA (path);
	return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY));
}
bool IsDirectoryExistsW (LPCWSTR path)
{
	DWORD attributes = GetFileAttributesW (path);
	return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY));
}
bool IsDirectoryExists (const std::string &path) { return IsDirectoryExistsA (path.c_str ()); }
bool IsDirectoryExists (const std::wstring &path) { return IsDirectoryExistsW (path.c_str ()); }
bool IsDirectoryExists (LPCSTR path) { return IsDirectoryExistsA (path); }
bool IsDirectoryExists (LPCWSTR path) { return IsDirectoryExistsW (path); }
bool IsPathExistsW (LPCWSTR filename)
{
	DWORD dwAttrib = GetFileAttributesW (filename);
	return (dwAttrib != INVALID_FILE_ATTRIBUTES);
}
bool IsPathExistsA (LPCSTR filename)
{
	DWORD dwAttrib = GetFileAttributesA (filename);
	return (dwAttrib != INVALID_FILE_ATTRIBUTES);
}
bool IsPathExists (const std::string &path) { return IsPathExistsA (path.c_str ()); }
bool IsPathExists (const std::wstring &path) { return IsPathExistsW (path.c_str ()); }
bool IsPathExists (LPCSTR path) { return IsPathExistsA (path); }
bool IsPathExists (LPCWSTR path) { return IsPathExistsW (path); }
std::string NormalizePath (const std::string &path)
{
	if (!path.empty () && path.back () == '\\')
		return path.substr (0, path.size () - 1);
	return path.c_str ();
}
std::wstring NormalizePath (const std::wstring &path)
{
	if (!path.empty () && path.back () == L'\\')
		return path.substr (0, path.size () - 1);
	return path.c_str ();
}
std::vector <std::string> EnumSubdirectories (const std::string &directory, bool includeParentPath)
{
	std::vector<std::string> subdirs;
	std::string normPath = NormalizePath (directory);
	std::string searchPath = normPath + "\\*";
	WIN32_FIND_DATAA findData;
	HANDLE hFind = FindFirstFileA (searchPath.c_str (), &findData);
	if (hFind == INVALID_HANDLE_VALUE) return subdirs;
	do
	{
		// 过滤 "." 和 ".."
		if (strcmp (findData.cFileName, ".") == 0 || strcmp (findData.cFileName, "..") == 0)
			continue;
		// 判断是否为目录
		if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			if (includeParentPath)
				subdirs.push_back (normPath + "\\" + findData.cFileName);
			else
				subdirs.push_back (findData.cFileName);
		}
	} while (FindNextFileA (hFind, &findData));
	FindClose (hFind);
	return subdirs;
}
std::vector <std::wstring> EnumSubdirectories (const std::wstring &directory, bool includeParentPath)
{
	std::vector<std::wstring> subdirs;
	std::wstring normPath = NormalizePath (directory);
	std::wstring searchPath = normPath + L"\\*";
	WIN32_FIND_DATAW findData;
	HANDLE hFind = FindFirstFileW (searchPath.c_str (), &findData);
	if (hFind == INVALID_HANDLE_VALUE) return subdirs;
	do
	{
		if (wcscmp (findData.cFileName, L".") == 0 || wcscmp (findData.cFileName, L"..") == 0)
			continue;
		if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			if (includeParentPath)
				subdirs.push_back (normPath + L"\\" + findData.cFileName);
			else
				subdirs.push_back (findData.cFileName);
		}
	} while (FindNextFileW (hFind, &findData));
	FindClose (hFind);
	return subdirs;
}
std::string GetCurrentProgramPathA (HMODULE hModule hModule_DefaultParam)
{
	std::vector <CHAR> buf (Max <size_t> (MAX_PATH, GetModuleFileNameA (hModule, nullptr, 0)) + 1);
	GetModuleFileNameA (hModule, buf.data (), buf.capacity ());
	return buf.data ();
}
std::wstring GetCurrentProgramPathW (HMODULE hModule hModule_DefaultParam)
{
	std::vector <WCHAR> buf (Max <size_t> (MAX_PATH, GetModuleFileNameW (hModule, nullptr, 0)) + 1);
	GetModuleFileNameW (hModule, buf.data (), buf.capacity ());
	return buf.data ();
}
std::string GetCurrentProgramNameA (HMODULE hModule hModule_DefaultParam) { return PathFindFileNameA (GetCurrentProgramPathA (hModule).c_str ()); }
std::wstring GetCurrentProgramNameW (HMODULE hModule hModule_DefaultParam) { return PathFindFileNameW (GetCurrentProgramPathW (hModule).c_str ()); }
S_VERSION GetExeFileVersion (LPCSTR lpszFilePath)
{
	S_VERSION ver (0);
	DWORD dummy;
	DWORD size = GetFileVersionInfoSizeA (lpszFilePath, &dummy);
	std::vector <BYTE> pVersionInfo (size);
	if (!GetFileVersionInfoA (lpszFilePath, 0, size, pVersionInfo.data ()))
	{
		return ver;
	}
	VS_FIXEDFILEINFO* pFileInfo = nullptr;
	UINT len = 0;
	if (!VerQueryValueA (pVersionInfo.data (), "\\", (LPVOID *)&pFileInfo, &len))
	{
		return ver;
	}
	if (len == 0 || pFileInfo == nullptr)
	{
		return ver;
	}
	ver = S_VERSION (
		HIWORD (pFileInfo->dwFileVersionMS),
		LOWORD (pFileInfo->dwFileVersionMS),
		HIWORD (pFileInfo->dwFileVersionLS),
		LOWORD (pFileInfo->dwFileVersionLS)
	);
	return ver;
}
S_VERSION GetExeFileVersion (LPCWSTR lpswFilePath)
{
	S_VERSION ver (0);
	DWORD dummy;
	DWORD size = GetFileVersionInfoSizeW (lpswFilePath, &dummy);
	std::vector <BYTE> pVersionInfo (size);
	if (!GetFileVersionInfoW (lpswFilePath, 0, size, pVersionInfo.data ()))
	{
		return ver;
	}
	VS_FIXEDFILEINFO* pFileInfo = nullptr;
	UINT len = 0;
	if (!VerQueryValueA (pVersionInfo.data (), "\\", (LPVOID *)&pFileInfo, &len))
	{
		return ver;
	}
	if (len == 0 || pFileInfo == nullptr)
	{
		return ver;
	}
	ver = S_VERSION (
		HIWORD (pFileInfo->dwFileVersionMS),
		LOWORD (pFileInfo->dwFileVersionMS),
		HIWORD (pFileInfo->dwFileVersionLS),
		LOWORD (pFileInfo->dwFileVersionLS)
	);
	return ver;
}
S_VERSION GetExeFileVersion (std::wstring objswFilePath)
{
	return GetExeFileVersion (objswFilePath.c_str ());
}
S_VERSION GetExeFileVersion (std::string objszFilePath)
{
	return GetExeFileVersion (objszFilePath.c_str ());
}
// 设置当前进程的环境变量RunPath和ProgramPath
void SetupInstanceEnvironment (HMODULE hModule hModule_DefaultParam)
{
	// 设置RunPath为当前工作目录（无结尾反斜杠）
	std::vector <WCHAR> currentDir (Max <size_t> (GetCurrentDirectoryW (0, nullptr), MAX_PATH) + 1);
	DWORD len = GetCurrentDirectoryW (currentDir.capacity (), currentDir.data ());
	if (len > 0)
	{
		std::wstring runPath (currentDir.data ());
		if (!runPath.empty () && (runPath.back () == L'\\' || runPath.back () == L'/'))
		{
			runPath.pop_back ();
		}
		SetEnvironmentVariableW (L"RunPath", runPath.c_str ());
	}
	// 设置ProgramPath为程序所在目录（无结尾反斜杠）
	std::vector <WCHAR> modulePath (Max <size_t> (GetModuleFileNameW (hModule, nullptr, 0), MAX_PATH) + 1);
	len = GetModuleFileNameW (hModule, modulePath.data (), MAX_PATH);
	if (len > 0 && len < MAX_PATH)
	{
		wchar_t* lastSlash = wcsrchr (modulePath.data (), L'\\');
		if (!lastSlash) lastSlash = wcsrchr (modulePath.data (), L'/');
		if (lastSlash) *lastSlash = L'\0';
		std::wstring programPath (modulePath.data ());
		if (!programPath.empty () && (programPath.back () == L'\\' || programPath.back () == L'/'))
		{
			programPath.pop_back ();
		}
		SetEnvironmentVariableW (L"ProgramPath", programPath.c_str ());
	}
}
// 处理宽字符串环境变量展开
std::wstring ProcessEnvVars (const std::wstring &input)
{
	DWORD requiredSize = ExpandEnvironmentStringsW (input.c_str (), nullptr, 0);
	if (requiredSize == 0) return input;
	std::wstring buffer (requiredSize, L'\0');
	if (!ExpandEnvironmentStringsW (input.c_str (), &buffer [0], requiredSize))
	{
		return input;
	}
	buffer.resize (requiredSize - 1); // 去除终止空字符
	return buffer.c_str ();
}
std::wstring ProcessEnvVars (LPCWSTR input)
{
	return ProcessEnvVars (std::wstring (input));
}
// 处理ANSI字符串环境变量展开
std::string ProcessEnvVars (const std::string &input)
{
	DWORD requiredSize = ExpandEnvironmentStringsA (input.c_str (), nullptr, 0);
	if (requiredSize == 0) return input;
	std::string buffer (requiredSize, '\0');
	if (!ExpandEnvironmentStringsA (input.c_str (), &buffer [0], requiredSize))
	{
		return input;
	}
	buffer.resize (requiredSize - 1); // 去除终止空字符
	return buffer.c_str ();
}
std::string ProcessEnvVars (LPCSTR input)
{
	return ProcessEnvVars (std::string (input));
}
std::string GetCurrentDirectoryA ()
{
	std::vector <CHAR> buf (Max <size_t> (GetCurrentDirectoryA (0, nullptr), MAX_PATH) + 1);
	GetCurrentDirectoryA (buf.size (), buf.data ());
	return buf.data ();
}
std::wstring GetCurrentDirectoryW ()
{
	std::vector <WCHAR> buf (Max <size_t> (GetCurrentDirectoryW (0, nullptr), MAX_PATH) + 1);
	GetCurrentDirectoryW (buf.size (), buf.data ());
	return buf.data ();
}
std::wstring GetFileDirectoryW (const std::wstring &filePath)
{
	std::vector <WCHAR> buf (filePath.capacity () + 1);
	lstrcpyW (buf.data (), filePath.c_str ());
	PathRemoveFileSpecW (buf.data ());
	return buf.data ();
}
std::string GetFileDirectoryA (const std::string &filePath)
{
	std::vector <CHAR> buf (filePath.capacity () + 1);
	lstrcpyA (buf.data (), filePath.c_str ());
	PathRemoveFileSpecA (buf.data ());
	return buf.data ();
}
size_t EnumerateFilesW (const std::wstring &directory, const std::wstring &filter,
	std::vector <std::wstring> &outFiles, bool recursive = false)
{
	std::wstring searchPath = directory;
	if (!searchPath.empty () && searchPath.back () != L'\\')
	{
		searchPath += L'\\';
	}
	searchPath += filter;
	WIN32_FIND_DATAW findData;
	HANDLE hFind = FindFirstFileW (searchPath.c_str (), &findData);
	if (hFind != INVALID_HANDLE_VALUE)
	{
		do {
			if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
			{
				outFiles.push_back (directory + L"\\" + findData.cFileName);
			}
		} while (FindNextFileW (hFind, &findData));
		FindClose (hFind);
	}
	if (recursive) {
		std::wstring subDirSearchPath = directory + L"\\*";
		hFind = FindFirstFileW (subDirSearchPath.c_str (), &findData);
		if (hFind != INVALID_HANDLE_VALUE)
		{
			do {
				if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
					wcscmp (findData.cFileName, L".") != 0 && wcscmp (findData.cFileName, L"..") != 0)
				{
					EnumerateFilesW (directory + L"\\" + findData.cFileName, filter, outFiles, true);
				}
			} while (FindNextFileW (hFind, &findData));
			FindClose (hFind);
		}
	}
	return outFiles.size ();
}
// 检查是否为 Windows 设备名（大小写不敏感）
bool IsReservedName (const std::wstring &name)
{
	static const wchar_t* reserved [] = {
		L"CON", L"PRN", L"AUX", L"NUL", L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9",
		L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9"
	};
	std::wstring upperName = StringToUpper (name);
	for (const auto& res : reserved)
	{
		if (upperName == res || (upperName.rfind (res, 0) == 0 && upperName.length () > wcslen (res) && upperName [wcslen (res)] == L'.'))
		{
			return true;
		}
	}
	return false;
}
// Windows 文件命名规范检查 (Unicode)
bool IsValidWindowsNameW (LPCWSTR name)
{
	if (!name || !*name) return false;
	std::wstring wname (name);
	if (wname.find_first_of (L"<>:\"/\\|?*") != std::wstring::npos) return false;
	if (IsReservedName (wname)) return false;
	if (wname.back () == L' ' || wname.back () == L'.') return false;
	return true;
}
// Windows 文件命名规范检查 (ANSI)
bool IsValidWindowsNameA (LPCSTR name)
{
	if (!name || !*name) return false;
	std::string str (name);
	if (str.find_first_of ("<>:\"/\\|?*") != std::string::npos) return false;

	// 转换 ANSI 到宽字符
	int len = MultiByteToWideChar (CP_ACP, 0, name, -1, NULL, 0);
	if (len <= 0) return false;
	std::wstring wname (len - 1, L'\0');
	MultiByteToWideChar (CP_ACP, 0, name, -1, &wname [0], len);
	if (IsReservedName (wname)) return false;
	if (str.back () == ' ' || str.back () == '.') return false;
	return true;
}
bool IsValidWindowsName (LPCSTR name) { return IsValidWindowsNameA (name); }
bool IsValidWindowsName (LPCWSTR name) { return IsValidWindowsNameW (name); }
bool IsValidWindowsName (const std::wstring &name) { return IsValidWindowsName (name.c_str ()); }
bool IsValidWindowsName (const std::string &name) { return IsValidWindowsName (name.c_str ()); }
std::wstring GetRootFolderNameFromFilePath (const std::wstring &lpFilePath)
{
	std::vector <WCHAR> szPath (Max <size_t> (lpFilePath.length (), MAX_PATH) + 1);
	if (!PathCanonicalizeW (szPath.data (), lpFilePath.c_str ())) return L"";
	if (PathRemoveFileSpecW (szPath.data ()) == FALSE) return L"";
	LPCWSTR pszFolder = PathFindFileNameW (szPath.data ());
	if (*pszFolder != L'\0') return std::wstring (pszFolder);
	WCHAR rootName [3] = {szPath [0], L':', L'\0'};
	return std::wstring (rootName);
}
std::wstring GetSafeTimestampForFilename ()
{
	::FILETIME ft;
	GetSystemTimeAsFileTime (&ft);
	SYSTEMTIME st;
	FileTimeToSystemTime (&ft, &st);
	std::wstringstream wss;
	wss << std::setfill (L'0')
		<< st.wYear
		<< std::setw (2) << st.wMonth
		<< std::setw (2) << st.wDay << L"_"
		<< std::setw (2) << st.wHour
		<< std::setw (2) << st.wMinute
		<< std::setw (2) << st.wSecond
		<< std::setw (3) << st.wMilliseconds;
	return wss.str ();
}
size_t EnumFiles (
	const std::wstring &lpDir,
	const std::wstring &lpFilter,
	std::vector <std::wstring> &aszOutput,
	bool bOutputWithPath = false,
	bool bSortByLetter = false,
	bool bIncludeSubDir = false
) {
	if (!bIncludeSubDir) aszOutput.clear ();
	std::vector<std::wstring> filters;
	size_t start = 0;
	while (start < lpFilter.length ())
	{
		size_t pos = lpFilter.find (L'\\', start);
		if (pos == std::wstring::npos) pos = lpFilter.length ();
		filters.emplace_back (lpFilter.substr (start, pos - start));
		start = pos + 1;
	}

	std::function <void (const std::wstring &, std::wstring)> enumDir;
	enumDir = [&] (const std::wstring &physicalPath, std::wstring relativePath)
	{
		WIN32_FIND_DATAW ffd;
		HANDLE hFind = FindFirstFileW ((physicalPath + L"\\*").c_str (), &ffd);
		if (hFind == INVALID_HANDLE_VALUE) return;
		do {
			if (wcscmp (ffd.cFileName, L".") == 0 ||
				wcscmp (ffd.cFileName, L"..") == 0) continue;
			const bool isDir = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
			const std::wstring newPhysical = physicalPath + L"\\" + ffd.cFileName;
			std::wstring newRelative = relativePath;
			if (isDir) {
				if (bIncludeSubDir) {
					newRelative += ffd.cFileName;
					newRelative += L"\\";
					enumDir (newPhysical, newRelative);
				}
			}
			else
			{
				for (const auto &filter : filters)
				{
					if (PathMatchSpecW (ffd.cFileName, filter.c_str ()))
					{
						aszOutput.push_back
						(
							bOutputWithPath ? newPhysical : (relativePath + ffd.cFileName)
						);
						break;
					}
				}
			}
		} while (FindNextFileW (hFind, &ffd));
		FindClose (hFind);
	};
	enumDir (lpDir, L"");
	if (bSortByLetter) std::sort (aszOutput.begin (), aszOutput.end ());
	return aszOutput.size ();
}
std::wstring GetRelativePath (
	const std::wstring &pszBaseDir,
	const std::wstring &pszFullPath,
	DWORD cchRelative
) {
	std::vector <WCHAR> szBase (Max <size_t> (pszBaseDir.length (), pszFullPath.length (), MAX_PATH) + 1);
	wcscpy_s (szBase.data (), MAX_PATH, pszBaseDir.c_str ());
	if (szBase [wcslen (szBase.data ()) - 1] != L'\\')
	{
		wcscat_s (szBase.data (), MAX_PATH, L"\\");
	}
	std::vector <WCHAR> buf (Max <size_t> (MAX_PATH, szBase.size ()) + 1);
	BOOL res = PathRelativePathToW (
		buf.data (),
		szBase.data (),
		FILE_ATTRIBUTE_DIRECTORY,
		pszFullPath.c_str (),
		FILE_ATTRIBUTE_NORMAL
	);
	if (res) return buf.data ();
	else return L"";
}
size_t EnumDirectory (
	const std::wstring &lpDir,
	std::vector<std::wstring> &aszOutput,
	bool bOutputWithPath = false,
	bool bSortByLetter = false,
	bool bIncludeSubDir = false
) {
	aszOutput.clear ();
	std::function <void (const std::wstring &, const std::wstring &)> enumDir;
	enumDir = [&] (const std::wstring &physicalPath, const std::wstring &relativePath) {
		WIN32_FIND_DATAW ffd;
		HANDLE hFind = FindFirstFileW ((physicalPath + L"\\*").c_str (), &ffd);
		if (hFind == INVALID_HANDLE_VALUE) return;
		do
		{
			const std::wstring name = ffd.cFileName;
			if (name == L"." || name == L"..") continue;
			const bool isDir = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
			std::wstring newPhysical = physicalPath + L"\\" + name;
			std::wstring newRelative = relativePath + name;
			if (isDir)
			{
				if (bIncludeSubDir) enumDir (newPhysical, newRelative + L"\\");
				if (bOutputWithPath) aszOutput.push_back (newPhysical);
				else aszOutput.push_back (newRelative);
			}
		} while (FindNextFileW (hFind, &ffd));
		FindClose (hFind);
	};
	enumDir (lpDir, L"");
	if (bSortByLetter) std::sort (aszOutput.begin (), aszOutput.end ());
	return aszOutput.size ();
}

static DWORD CALLBACK ProgressRoutine (
	LARGE_INTEGER TotalFileSize,
	LARGE_INTEGER TotalBytesTransferred,
	LARGE_INTEGER /*StreamSize*/,
	LARGE_INTEGER /*StreamBytesTransferred*/,
	DWORD /*dwStreamNumber*/,
	DWORD /*dwCallbackReason*/,
	HANDLE /*hSourceFile*/,
	HANDLE /*hDestinationFile*/,
	LPVOID lpData
) {
	auto *pCallback = reinterpret_cast <std::function <void (int)> *> (lpData);
	if (pCallback && *pCallback)
	{
		int progress = static_cast <int> (
			(TotalBytesTransferred.QuadPart * 100) / TotalFileSize.QuadPart
			);
		(*pCallback) (progress);
	}
	return PROGRESS_CONTINUE;
}
bool RenameFileW (
	const std::wstring &lpSrcPath,
	const std::wstring &lpDestPath,
	std::function <void (int)> fProgress = nullptr
) {
	LPPROGRESS_ROUTINE pRoutine = nullptr;
	LPVOID pData = nullptr;
	if (fProgress)
	{
		pRoutine = ProgressRoutine;
		pData = &fProgress;
	}
	DWORD flags = MOVEFILE_COPY_ALLOWED;
	BOOL ok = MoveFileWithProgressW (
		lpSrcPath.c_str (),
		lpDestPath.c_str (),
		pRoutine,
		pData,
		flags
	);
	return ok != FALSE;
}
bool RenameFileA (
	const std::string &lpSrcPath,
	const std::string &lpDestPath,
	std::function <void (int)> fProgress = nullptr
) {
	LPPROGRESS_ROUTINE pRoutine = nullptr;
	LPVOID pData = nullptr;
	if (fProgress)
	{
		pRoutine = ProgressRoutine;
		pData = &fProgress;
	}
	DWORD flags = MOVEFILE_COPY_ALLOWED;
	BOOL ok = MoveFileWithProgressA (
		lpSrcPath.c_str (),
		lpDestPath.c_str (),
		pRoutine,
		pData,
		flags
	);
	return ok != FALSE;
}
bool RenameFileW (const std::wstring &lpSrcDir, const std::wstring &lpSrcName, const std::wstring &lpDestName, std::function <void (int)> fProgress = nullptr)
{
	struct BuildTask
	{
		LPWSTR src = nullptr, dest = nullptr;
		~BuildTask ()
		{
			if (src != nullptr)
			{
				delete [] src;
				src = nullptr;
			}
			if (dest != nullptr)
			{
				delete [] dest;
				dest = nullptr;
			}
		}
	};
	BuildTask bt;
	bt.src = new WCHAR [lpSrcDir.length () + lpSrcName.length () + 2];
	bt.dest = new WCHAR [lpSrcDir.length () + lpDestName.length () + 2];
	PathCombineW (bt.src, lpSrcDir.c_str (), lpSrcName.c_str ());
	PathCombineW (bt.dest, lpSrcDir.c_str (), lpDestName.c_str ());
	return RenameFileW (bt.src, bt.dest, fProgress);
}
bool RenameFileA (const std::string &lpSrcDir, const std::string &lpSrcName, const std::string &lpDestName, std::function <void (int)> fProgress = nullptr)
{
	struct BuildTask
	{
		LPSTR src = nullptr, dest = nullptr;
		~BuildTask ()
		{
			if (src != nullptr)
			{
				delete [] src;
				src = nullptr;
			}
			if (dest != nullptr)
			{
				delete [] dest;
				dest = nullptr;
			}
		}
	};
	BuildTask bt;
	bt.src = new CHAR [lpSrcDir.length () + lpSrcName.length () + 2];
	bt.dest = new CHAR [lpSrcDir.length () + lpDestName.length () + 2];
	PathCombineA (bt.src, lpSrcDir.c_str (), lpSrcName.c_str ());
	PathCombineA (bt.dest, lpSrcDir.c_str (), lpDestName.c_str ());
	return RenameFileA (bt.src, bt.dest, fProgress);
}
bool RenameFile (const std::wstring &lpSrcPath, const std::wstring &lpDestPath, std::function <void (int)> fProgress = nullptr)
{
	return RenameFileW (lpSrcPath, lpDestPath, fProgress);
}
bool RenameFile (const std::string &lpSrcPath, const std::string &lpDestPath, std::function <void (int)> fProgress = nullptr)
{
	return RenameFileA (lpSrcPath, lpDestPath, fProgress);
}
bool RenameFile (const std::wstring &lpSrcDir, const std::wstring &lpSrcName, const std::wstring &lpDestName, std::function <void (int)> fProgress = nullptr)
{
	return RenameFileW (lpSrcDir, lpSrcName, lpDestName, fProgress);
}
bool RenameFile (const std::string &lpSrcDir, const std::string &lpSrcName, const std::string &lpDestName, std::function <void (int)> fProgress = nullptr)
{
	return RenameFileA (lpSrcDir, lpSrcName, lpDestName, fProgress);
}
bool RenameDirectoryW (
	const std::wstring &lpSrcPath,
	const std::wstring &lpDestPath,
	std::function <void (int)> fProgress = nullptr
) {
	LPPROGRESS_ROUTINE pRoutine = nullptr;
	LPVOID pData = nullptr;
	if (fProgress)
	{
		pRoutine = ProgressRoutine;
		pData = &fProgress;
	}
	DWORD flags = MOVEFILE_COPY_ALLOWED;
	BOOL ok = MoveFileWithProgressW (
		lpSrcPath.c_str (),
		lpDestPath.c_str (),
		pRoutine,
		pData,
		flags
	);
	return ok != FALSE;
}
bool RenameDirectoryA (
	const std::string &lpSrcPath,
	const std::string &lpDestPath,
	std::function <void (int)> fProgress = nullptr
) {
	LPPROGRESS_ROUTINE pRoutine = nullptr;
	LPVOID pData = nullptr;
	if (fProgress)
	{
		pRoutine = ProgressRoutine;
		pData = &fProgress;
	}
	DWORD flags = MOVEFILE_COPY_ALLOWED;
	BOOL ok = MoveFileWithProgressA (
		lpSrcPath.c_str (),
		lpDestPath.c_str (),
		pRoutine,
		pData,
		flags
	);
	return ok != FALSE;
}
bool RenameDirectoryW (
	const std::wstring &lpParentDir,
	const std::wstring &lpSrcName,
	const std::wstring &lpDestName,
	std::function <void (int)> fProgress = nullptr
) {
	struct PathBuilder
	{
		LPWSTR src = nullptr;
		LPWSTR dest = nullptr;
		~PathBuilder ()
		{
			delete [] src;
			delete [] dest;
		}
	} pb;
	pb.src = new WCHAR [lpParentDir.length () + lpSrcName.length () + 2];
	pb.dest = new WCHAR [lpParentDir.length () + lpDestName.length () + 2];
	PathCombineW (pb.src, lpParentDir.c_str (), lpSrcName.c_str ());
	PathCombineW (pb.dest, lpParentDir.c_str (), lpDestName.c_str ());
	return RenameDirectoryW (pb.src, pb.dest, fProgress);
}
bool RenameDirectoryA (
	const std::string &lpParentDir,
	const std::string &lpSrcName,
	const std::string &lpDestName,
	std::function <void (int)> fProgress = nullptr
) {
	struct PathBuilder
	{
		LPSTR src = nullptr;
		LPSTR dest = nullptr;
		~PathBuilder ()
		{
			delete [] src;
			delete [] dest;
		}
	} pb;
	pb.src = new CHAR [lpParentDir.length () + lpSrcName.length () + 2];
	pb.dest = new CHAR [lpParentDir.length () + lpDestName.length () + 2];
	PathCombineA (pb.src, lpParentDir.c_str (), lpSrcName.c_str ());
	PathCombineA (pb.dest, lpParentDir.c_str (), lpDestName.c_str ());
	return RenameDirectoryA (pb.src, pb.dest, fProgress);
}
bool RenameDirectory (
	const std::wstring &src,
	const std::wstring &dst,
	std::function <void (int)> fProgress = nullptr
) {
	return RenameDirectoryW (src, dst, fProgress);
}
bool RenameDirectory (
	const std::string &src,
	const std::string &dst,
	std::function <void (int)> fProgress = nullptr
) {
	return RenameDirectoryA (src, dst, fProgress);
}
bool RenameDirectory (
	const std::wstring &parentDir,
	const std::wstring &srcName,
	const std::wstring &dstName,
	std::function <void (int)> fProgress = nullptr
) {
	return RenameDirectoryW (parentDir, srcName, dstName, fProgress);
}
bool RenameDirectory (
	const std::string &parentDir,
	const std::string &srcName,
	const std::string &dstName,
	std::function <void (int)> fProgress = nullptr
) {
	return RenameDirectoryA (parentDir, srcName, dstName, fProgress);
}
std::wstring CombinePath (const std::wstring &left, const std::wstring &right)
{
	std::vector <WCHAR> buf (left.capacity () + right.capacity () + 2);
	PathCombineW (buf.data (), left.c_str (), right.c_str ());
	return buf.data ();
}
#ifdef PathCommonPrefix
#undef PathCommonPrefix
#endif
std::wstring PathCommonPrefix (const std::wstring &file1, const std::wstring &file2)
{
	std::vector <WCHAR> buf (Max <size_t> (file1.capacity (), file2.capacity (), MAX_PATH) + 2);
	PathCommonPrefixW (file1.c_str (), file2.c_str (), buf.data ());
	return buf.data ();
}
#undef GetFullPathName
std::wstring GetFullPathName (const std::wstring &lpFileName)
{
	if (lpFileName.empty ()) return L"";
	DWORD length = GetFullPathNameW (lpFileName.c_str (), 0, nullptr, nullptr);
	if (length == 0) return L"";
	std::vector <WCHAR> buffer (length + 1, L'\0');
	DWORD result = GetFullPathNameW (lpFileName.c_str (), length, buffer.data (), nullptr);
	if (result == 0) return L"";
	return std::wstring (buffer.data (), result);
}
bool PathEquals (const std::wstring &path1, const std::wstring &path2)
{
	size_t maxlen = Max <size_t> (path1.capacity () + 1, path2.capacity () + 1, MAX_PATH);
	std::vector <WCHAR> buf1 (maxlen), buf2 (maxlen);
	PathCanonicalizeW (buf1.data (), path1.c_str ());
	PathCanonicalizeW (buf2.data (), path2.c_str ());
	return IsNormalizeStringEquals (buf1.data (), buf2.data ());
}