/***********************************************************************
THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY
DEVELOPER: Zihan Chen(vczh)
***********************************************************************/
#include "VlppOS.h"
#include "Vlpp.h"

/***********************************************************************
.\FILESYSTEM.WINDOWS.CPP
***********************************************************************/
/***********************************************************************
Author: Zihan Chen (vczh)
Licensed under https://github.com/vczh-libraries/License
***********************************************************************/

#include <Windows.h>
#include <Shlwapi.h>

#ifndef VCZH_MSVC
static_assert(false, "Do not build this file for non-Windows applications.");
#endif

#pragma comment(lib, "Shlwapi.lib")

namespace vl
{
	namespace stream
	{
		extern Ptr<IFileStreamImpl>		CreateOSFileStreamImpl(const WString& fileName, FileStream::AccessRight accessRight);
	}

	namespace filesystem
	{
		using namespace collections;
		using namespace stream;

/***********************************************************************
WindowsFileSystemImpl
***********************************************************************/

		class WindowsFileSystemImpl : public feature_injection::FeatureImpl<IFileSystemImpl>
		{
		public:
			void Initialize(WString& fullPath) const override
			{
				{
					Array<wchar_t> buffer(fullPath.Length() + 1);
					wcscpy_s(&buffer[0], fullPath.Length() + 1, fullPath.Buffer());
					FilePath::NormalizeDelimiters(buffer);
					fullPath = &buffer[0];
				}

				if (fullPath != L"")
				{
					if (fullPath.Length() < 2 || fullPath[1] != L':')
					{
						wchar_t buffer[MAX_PATH + 1] = { 0 };
						auto result = GetCurrentDirectory(sizeof(buffer) / sizeof(*buffer), buffer);
						if (result > MAX_PATH + 1 || result == 0)
						{
							throw ArgumentException(L"Failed to call GetCurrentDirectory.", L"vl::filesystem::FilePath::Initialize", L"");
						}
						fullPath = WString(buffer) + L"\\" + fullPath;
					}
					{
						wchar_t buffer[MAX_PATH + 1] = { 0 };
						if (fullPath.Length() == 2 && fullPath[1] == L':')
						{
							fullPath += L"\\";
						}
						auto result = GetFullPathName(fullPath.Buffer(), sizeof(buffer) / sizeof(*buffer), buffer, NULL);
						if (result > MAX_PATH + 1 || result == 0)
						{
							throw ArgumentException(L"The path is illegal.", L"vl::filesystem::FilePath::FilePath", L"_filePath");
						}

						{
							wchar_t shortPath[MAX_PATH + 1];
							wchar_t longPath[MAX_PATH + 1];
							if (GetShortPathName(buffer, shortPath, MAX_PATH) > 0)
							{
								if (GetLongPathName(shortPath, longPath, MAX_PATH) > 0)
								{
									memcpy(buffer, longPath, sizeof(buffer));
								}
							}
						}
						fullPath = buffer;
					}
				}

				FilePath::TrimLastDelimiter(fullPath);
			}

			bool IsFile(const WString& fullPath) const override
			{
				WIN32_FILE_ATTRIBUTE_DATA info;
				BOOL result = GetFileAttributesEx(fullPath.Buffer(), GetFileExInfoStandard, &info);
				if (!result) return false;
				return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
			}

			bool IsFolder(const WString& fullPath) const override
			{
				WIN32_FILE_ATTRIBUTE_DATA info;
				BOOL result = GetFileAttributesEx(fullPath.Buffer(), GetFileExInfoStandard, &info);
				if (!result) return false;
				return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
			}

			bool IsRoot(const WString& fullPath) const override
			{
				return fullPath == L"";
			}

			WString GetRelativePathFor(const WString& fromPath, const WString& toPath) const override
			{
				if (fromPath.Length() == 0 || toPath.Length() == 0 || fromPath[0] != toPath[0])
				{
					return toPath;
				}

				wchar_t buffer[MAX_PATH + 1] = { 0 };
				PathRelativePathTo(
					buffer,
					fromPath.Buffer(),
					(IsFolder(fromPath) ? FILE_ATTRIBUTE_DIRECTORY : 0),
					toPath.Buffer(),
					(IsFolder(toPath) ? FILE_ATTRIBUTE_DIRECTORY : 0)
				);
				return buffer;
			}

			bool FileDelete(const FilePath& filePath) const override
			{
				return DeleteFile(filePath.GetFullPath().Buffer()) != 0;
			}

			bool FileRename(const FilePath& filePath, const WString& newName) const override
			{
				WString oldFileName = filePath.GetFullPath();
				WString newFileName = (filePath.GetFolder() / newName).GetFullPath();
				return MoveFile(oldFileName.Buffer(), newFileName.Buffer()) != 0;
			}

			bool GetFolders(const FilePath& folderPath, collections::List<Folder>& folders) const override
			{
				if (folderPath.IsRoot())
				{
					auto bufferSize = GetLogicalDriveStrings(0, nullptr);
					if (bufferSize > 0)
					{
						Array<wchar_t> buffer(bufferSize);
						if (GetLogicalDriveStrings((DWORD)buffer.Count(), &buffer[0]) > 0)
						{
							auto begin = &buffer[0];
							auto end = begin + buffer.Count();
							while (begin < end && *begin)
							{
								WString driveString = begin;
								begin += driveString.Length() + 1;
								folders.Add(Folder(FilePath(driveString)));
							}
							return true;
						}
					}
					return false;
				}
				else
				{
					if (!IsFolder(folderPath.GetFullPath())) return false;
					WIN32_FIND_DATA findData;
					HANDLE findHandle = INVALID_HANDLE_VALUE;

					while (true)
					{
						if (findHandle == INVALID_HANDLE_VALUE)
						{
							WString searchPath = (folderPath / L"*").GetFullPath();
							findHandle = FindFirstFile(searchPath.Buffer(), &findData);
							if (findHandle == INVALID_HANDLE_VALUE)
							{
								break;
							}
						}
						else
						{
							BOOL result = FindNextFile(findHandle, &findData);
							if (result == 0)
							{
								FindClose(findHandle);
								break;
							}
						}

						if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
						{
							if (wcscmp(findData.cFileName, L".") != 0 && wcscmp(findData.cFileName, L"..") != 0)
							{
								folders.Add(Folder(folderPath / findData.cFileName));
							}
						}
					}
					return true;
				}
			}

			bool GetFiles(const FilePath& folderPath, collections::List<File>& files) const override
			{
				if (IsRoot(folderPath.GetFullPath()))
				{
					return true;
				}
				if (!IsFolder(folderPath.GetFullPath())) return false;
				WIN32_FIND_DATA findData;
				HANDLE findHandle = INVALID_HANDLE_VALUE;

				while (true)
				{
					if (findHandle == INVALID_HANDLE_VALUE)
					{
						WString searchPath = (folderPath / L"*").GetFullPath();
						findHandle = FindFirstFile(searchPath.Buffer(), &findData);
						if (findHandle == INVALID_HANDLE_VALUE)
						{
							break;
						}
					}
					else
					{
						BOOL result = FindNextFile(findHandle, &findData);
						if (result == 0)
						{
							FindClose(findHandle);
							break;
						}
					}

					if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
					{
						files.Add(File(folderPath / findData.cFileName));
					}
				}
				return true;
			}

			bool CreateFolder(const FilePath& folderPath) const override
			{
				return CreateDirectory(folderPath.GetFullPath().Buffer(), NULL) != 0;
			}

			bool DeleteFolder(const FilePath& folderPath) const override
			{
				return RemoveDirectory(folderPath.GetFullPath().Buffer()) != 0;
			}

			bool FolderRename(const FilePath& folderPath, const WString& newName) const override
			{
				WString oldFileName = folderPath.GetFullPath();
				WString newFileName = (folderPath.GetFolder() / newName).GetFullPath();
				return MoveFile(oldFileName.Buffer(), newFileName.Buffer()) != 0;
			}

			Ptr<stream::IFileStreamImpl> GetFileStreamImpl(const WString& fileName, stream::FileStream::AccessRight accessRight) const override
			{
				return stream::CreateOSFileStreamImpl(fileName, accessRight);
			}
		};

		IFileSystemImpl* GetOSFileSystemImpl()
		{
			static WindowsFileSystemImpl osFileSystemImpl;
			return &osFileSystemImpl;
		}
	}
}


/***********************************************************************
.\HTTPUTILITY.WINDOWS.CPP
***********************************************************************/
/***********************************************************************
Author: Zihan Chen (vczh)
Licensed under https://github.com/vczh-libraries/License
***********************************************************************/

#include <winhttp.h>

#ifndef VCZH_MSVC
static_assert(false, "Do not build this file for non-Windows applications.");
#endif

#pragma comment(lib, "WinHttp.lib")

namespace vl
{
	using namespace collections;

/***********************************************************************
HttpRequest
***********************************************************************/

	bool HttpRequest::SetHost(const WString& inputQuery)
	{
		if (method == L"")
		{
			method = L"GET";
		}

		server = L"";
		query = L"";
		port = 0;
		secure = false;

		{
			if (server == L"")
			{
				if (inputQuery.Length() > 7)
				{
					WString protocol = inputQuery.Sub(0, 8);
					if (_wcsicmp(protocol.Buffer(), L"https://") == 0)
					{
						const wchar_t* reading = inputQuery.Buffer() + 8;
						const wchar_t* index1 = wcschr(reading, L':');
						const wchar_t* index2 = wcschr(reading, L'/');
						if (index2)
						{
							query = index2;
							server = WString::CopyFrom(reading, (index1 ? index1 : index2) - reading);
							port = INTERNET_DEFAULT_HTTPS_PORT;
							secure = true;
							if (index1)
							{
								auto portString = WString::CopyFrom(index1 + 1, index2 - index1 - 1);
								port = _wtoi(portString.Buffer());
							}
							return true;
						}
					}
				}
			}
			if (server == L"")
			{
				if (inputQuery.Length() > 6)
				{
					WString protocol = inputQuery.Sub(0, 7);
					if (_wcsicmp(protocol.Buffer(), L"http://") == 0)
					{
						const wchar_t* reading = inputQuery.Buffer() + 7;
						const wchar_t* index1 = wcschr(reading, L':');
						const wchar_t* index2 = wcschr(reading, L'/');
						if (index2)
						{
							query = index2;
							server = WString::CopyFrom(reading, (index1 ? index1 : index2) - reading);
							port = INTERNET_DEFAULT_HTTP_PORT;
							if (index1)
							{
								auto portString = WString::CopyFrom(index1 + 1, index2 - index1 - 1);
								port = _wtoi(portString.Buffer());
							}
							return true;
						}
					}
				}
			}
		}
		return false;
	}

	void HttpRequest::SetBodyUtf8(const WString& bodyString)
	{
		vint utf8Size = WideCharToMultiByte(CP_UTF8, 0, bodyString.Buffer(), (int)bodyString.Length(), NULL, 0, NULL, NULL);
		char* utf8 = new char[utf8Size + 1];
		ZeroMemory(utf8, utf8Size + 1);
		WideCharToMultiByte(CP_UTF8, 0, bodyString.Buffer(), (int)bodyString.Length(), utf8, (int)utf8Size, NULL, NULL);

		body.Resize(utf8Size);
		memcpy(&body[0], utf8, utf8Size);
		delete[] utf8;
	}

/***********************************************************************
HttpResponse
***********************************************************************/

	WString HttpResponse::GetBodyUtf8()
	{
		WString response;
		char* utf8 = &body[0];
		vint totalSize = body.Count();
		vint utf16Size = MultiByteToWideChar(CP_UTF8, 0, utf8, (int)totalSize, NULL, 0);
		wchar_t* utf16 = new wchar_t[utf16Size + 1];
		ZeroMemory(utf16, (utf16Size + 1) * sizeof(wchar_t));
		MultiByteToWideChar(CP_UTF8, 0, utf8, (int)totalSize, utf16, (int)utf16Size);
		response = utf16;
		delete[] utf16;
		return response;
	}

/***********************************************************************
Utilities
***********************************************************************/

	struct BufferPair
	{
		char*		buffer;
		vint		length;

		BufferPair()
			:buffer(0)
			, length(0)
		{
		}

		BufferPair(char* _buffer, vint _length)
			:buffer(_buffer)
			, length(_length)
		{
		}
	};

	bool HttpQuery(const HttpRequest& request, HttpResponse& response)
	{
		// initialize
		response.statusCode = -1;
		HINTERNET internet = NULL;
		HINTERNET connectedInternet = NULL;
		HINTERNET requestInternet = NULL;
		BOOL httpResult = FALSE;
		DWORD error = 0;
		List<LPCWSTR> acceptTypes;
		List<BufferPair> availableBuffers;

		// access http
		internet = WinHttpOpen(L"vczh", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
		error = GetLastError();
		if (!internet) goto CLEANUP;

		// connect
		connectedInternet = WinHttpConnect(internet, request.server.Buffer(), (int)request.port, 0);
		error = GetLastError();
		if (!connectedInternet) goto CLEANUP;

		// open request
		// TODO: (enumerable) Linq:Select
		for (vint i = 0; i < request.acceptTypes.Count(); i++)
		{
			acceptTypes.Add(request.acceptTypes.Get(i).Buffer());
		}
		acceptTypes.Add(nullptr);
		requestInternet = WinHttpOpenRequest(connectedInternet, request.method.Buffer(), request.query.Buffer(), NULL, WINHTTP_NO_REFERER, &acceptTypes[0], (request.secure ? WINHTTP_FLAG_SECURE : 0));
		error = GetLastError();
		if (!requestInternet) goto CLEANUP;

		// authentication, cookie and request
		if (request.username != L"" && request.password != L"")
		{
			WinHttpSetCredentials(requestInternet, WINHTTP_AUTH_TARGET_SERVER, WINHTTP_AUTH_SCHEME_BASIC, request.username.Buffer(), request.password.Buffer(), NULL);
		}
		if (request.contentType != L"")
		{
			httpResult = WinHttpAddRequestHeaders(requestInternet, (L"Content-type:" + request.contentType).Buffer(), -1, WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD);
		}
		if (request.cookie != L"")
		{
			WinHttpAddRequestHeaders(requestInternet, (L"Cookie:" + request.cookie).Buffer(), -1, WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD);
		}

		// extra headers
		for (int i = 0; i < request.extraHeaders.Count(); i++)
		{
			WString key = request.extraHeaders.Keys()[i];
			WString value = request.extraHeaders.Values().Get(i);
			WinHttpAddRequestHeaders(requestInternet, (key + L":" + value).Buffer(), -1, WINHTTP_ADDREQ_FLAG_REPLACE | WINHTTP_ADDREQ_FLAG_ADD);
		}

		if (request.body.Count() > 0)
		{
			httpResult = WinHttpSendRequest(requestInternet, WINHTTP_NO_ADDITIONAL_HEADERS, 0, (LPVOID)&request.body.Get(0), (int)request.body.Count(), (int)request.body.Count(), NULL);
		}
		else
		{
			httpResult = WinHttpSendRequest(requestInternet, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, NULL);
		}
		error = GetLastError();
		if (httpResult == FALSE) goto CLEANUP;

		// receive response
		httpResult = WinHttpReceiveResponse(requestInternet, NULL);
		error = GetLastError();
		if (httpResult != TRUE) goto CLEANUP;

		// read response status code
		{
			DWORD headerLength = sizeof(DWORD);
			DWORD statusCode = 0;
			httpResult = WinHttpQueryHeaders(requestInternet, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &statusCode, &headerLength, WINHTTP_NO_HEADER_INDEX);
			error = GetLastError();
			if (httpResult == FALSE) goto CLEANUP;
			response.statusCode = statusCode;
		}
		// read respons cookie
		{
			DWORD headerLength = sizeof(DWORD);
			httpResult = WinHttpQueryHeaders(requestInternet, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &headerLength, WINHTTP_NO_HEADER_INDEX);
			error = GetLastError();
			if (error == ERROR_INSUFFICIENT_BUFFER)
			{
				wchar_t* rawHeader = new wchar_t[headerLength / sizeof(wchar_t)];
				ZeroMemory(rawHeader, headerLength);
				httpResult = WinHttpQueryHeaders(requestInternet, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, rawHeader, &headerLength, WINHTTP_NO_HEADER_INDEX);

				const wchar_t* cookieStart = wcsstr(rawHeader, L"Cookie:");
				if (cookieStart)
				{
					const wchar_t* cookieEnd = wcsstr(cookieStart, L";");
					if (cookieEnd)
					{
						response.cookie = WString::CopyFrom(cookieStart + 7, cookieEnd - cookieStart - 7);
					}
				}
				delete[] rawHeader;
			}
		}

		// read response body
		while (true)
		{
			DWORD bytesAvailable = 0;
			BOOL queryDataAvailableResult = WinHttpQueryDataAvailable(requestInternet, &bytesAvailable);
			error = GetLastError();
			if (queryDataAvailableResult == TRUE && bytesAvailable != 0)
			{
				char* utf8 = new char[bytesAvailable];
				DWORD bytesRead = 0;
				BOOL readDataResult = WinHttpReadData(requestInternet, utf8, bytesAvailable, &bytesRead);
				error = GetLastError();
				if (readDataResult == TRUE)
				{
					availableBuffers.Add(BufferPair(utf8, bytesRead));
				}
				else
				{
					delete[] utf8;
				}
			}
			else
			{
				break;
			}
		}

		{
			// concatincate response body
			vint totalSize = 0;
			for (auto p : availableBuffers)
			{
				totalSize += p.length;
			}
			response.body.Resize(totalSize);
			if (totalSize > 0)
			{
				char* utf8 = new char[totalSize];
				{
					char* temp = utf8;
					for (auto p : availableBuffers)
					{
						memcpy(temp, p.buffer, p.length);
						temp += p.length;
					}
				}
				memcpy(&response.body[0], utf8, totalSize);
				delete[] utf8;
			}
			for (auto p : availableBuffers)
			{
				delete[] p.buffer;
			}
		}
	CLEANUP:
		if (requestInternet) WinHttpCloseHandle(requestInternet);
		if (connectedInternet) WinHttpCloseHandle(connectedInternet);
		if (internet) WinHttpCloseHandle(internet);
		return response.statusCode != -1;
	}

	WString UrlEncodeQuery(const WString& query)
	{
		vint utf8Size = WideCharToMultiByte(CP_UTF8, 0, query.Buffer(), (int)query.Length(), NULL, 0, NULL, NULL);
		char* utf8 = new char[utf8Size + 1];
		ZeroMemory(utf8, utf8Size + 1);
		WideCharToMultiByte(CP_UTF8, 0, query.Buffer(), (int)query.Length(), utf8, (int)utf8Size, NULL, NULL);

		wchar_t* encoded = new wchar_t[utf8Size * 3 + 1];
		ZeroMemory(encoded, (utf8Size * 3 + 1) * sizeof(wchar_t));
		wchar_t* writing = encoded;
		for (vint i = 0; i < utf8Size; i++)
		{
			unsigned char x = (unsigned char)utf8[i];
			if (L'a' <= x && x <= 'z' || L'A' <= x && x <= L'Z' || L'0' <= x && x <= L'9')
			{
				writing[0] = x;
				writing += 1;
			}
			else
			{
				writing[0] = L'%';
				writing[1] = L"0123456789ABCDEF"[x / 16];
				writing[2] = L"0123456789ABCDEF"[x % 16];
				writing += 3;
			}
		}

		WString result = encoded;
		delete[] encoded;
		delete[] utf8;
		return result;
	}
}


/***********************************************************************
.\LOCALE.WINDOWS.CPP
***********************************************************************/
/***********************************************************************
Author: Zihan Chen (vczh)
Licensed under https://github.com/vczh-libraries/License
***********************************************************************/


#ifndef VCZH_MSVC
static_assert(false, "Do not build this file for non-Windows applications.");
#endif

namespace vl
{
	using namespace collections;

/***********************************************************************
Locale Helper Functions
***********************************************************************/

	SYSTEMTIME DateTimeToSystemTime(const DateTime& dateTime)
	{
		SYSTEMTIME systemTime;
		systemTime.wYear = (WORD)dateTime.year;
		systemTime.wMonth = (WORD)dateTime.month;
		systemTime.wDayOfWeek = (WORD)dateTime.dayOfWeek;
		systemTime.wDay = (WORD)dateTime.day;
		systemTime.wHour = (WORD)dateTime.hour;
		systemTime.wMinute = (WORD)dateTime.minute;
		systemTime.wSecond = (WORD)dateTime.second;
		systemTime.wMilliseconds = (WORD)dateTime.milliseconds;
		return systemTime;
	}

	BOOL CALLBACK Locale_EnumLocalesProcEx(
		_In_  LPWSTR lpLocaleString,
		_In_  DWORD dwFlags,
		_In_  LPARAM lParam
	)
	{
		((List<Locale>*)lParam)->Add(Locale(lpLocaleString));
		return TRUE;
	}

	BOOL CALLBACK Locale_EnumDateFormatsProcExEx(
		_In_  LPWSTR lpDateFormatString,
		_In_  CALID CalendarID,
		_In_  LPARAM lParam
	)
	{
		((List<WString>*)lParam)->Add(lpDateFormatString);
		return TRUE;
	}

	BOOL CALLBACK EnumTimeFormatsProcEx(
		_In_  LPWSTR lpTimeFormatString,
		_In_  LPARAM lParam
	)
	{
		((List<WString>*)lParam)->Add(lpTimeFormatString);
		return TRUE;
	}

	WString Transform(const WString& localeName, const WString& input, DWORD flag)
	{
		int length = LCMapStringEx(localeName.Buffer(), flag, input.Buffer(), (int)input.Length() + 1, NULL, 0, NULL, NULL, NULL);
		Array<wchar_t> buffer(length);
		LCMapStringEx(localeName.Buffer(), flag, input.Buffer(), (int)input.Length() + 1, &buffer[0], (int)buffer.Count(), NULL, NULL, NULL);
		return &buffer[0];
	}

	DWORD TranslateNormalization(Locale::Normalization normalization)
	{
		DWORD result = 0;
		if (normalization & Locale::IgnoreCase) result |= NORM_IGNORECASE;
		if (normalization & Locale::IgnoreCaseLinguistic) result |= NORM_IGNORECASE | NORM_LINGUISTIC_CASING;
		if (normalization & Locale::IgnoreKanaType) result |= NORM_IGNOREKANATYPE;
		if (normalization & Locale::IgnoreNonSpace) result |= NORM_IGNORENONSPACE;
		if (normalization & Locale::IgnoreSymbol) result |= NORM_IGNORESYMBOLS;
		if (normalization & Locale::IgnoreWidth) result |= NORM_IGNOREWIDTH;
		if (normalization & Locale::DigitsAsNumbers) result |= SORT_DIGITSASNUMBERS;
		if (normalization & Locale::StringSoft) result |= SORT_STRINGSORT;
		return result;
	}

/***********************************************************************
WindowsLocaleImpl
***********************************************************************/

	class WindowsLocaleImpl : public feature_injection::FeatureImpl<ILocaleImpl>
	{
	public:
		Locale Invariant() const override
		{
			return Locale(LOCALE_NAME_INVARIANT);
		}

		Locale SystemDefault() const override
		{
			wchar_t buffer[LOCALE_NAME_MAX_LENGTH + 1] = { 0 };
			GetSystemDefaultLocaleName(buffer, LOCALE_NAME_MAX_LENGTH);
			return Locale(buffer);
		}

		Locale UserDefault() const override
		{
			wchar_t buffer[LOCALE_NAME_MAX_LENGTH + 1] = { 0 };
			GetUserDefaultLocaleName(buffer, LOCALE_NAME_MAX_LENGTH);
			return Locale(buffer);
		}

		void Enumerate(collections::List<Locale>& locales) const override
		{
			EnumSystemLocalesEx(&Locale_EnumLocalesProcEx, LOCALE_ALL, (LPARAM)&locales, NULL);
		}

		void GetShortDateFormats(const WString& localeName, collections::List<WString>& formats) const override
		{
			EnumDateFormatsExEx(&Locale_EnumDateFormatsProcExEx, localeName.Buffer(), DATE_SHORTDATE, (LPARAM)&formats);
		}

		void GetLongDateFormats(const WString& localeName, collections::List<WString>& formats) const override
		{
			EnumDateFormatsExEx(&Locale_EnumDateFormatsProcExEx, localeName.Buffer(), DATE_LONGDATE, (LPARAM)&formats);
		}

		void GetYearMonthDateFormats(const WString& localeName, collections::List<WString>& formats) const override
		{
			EnumDateFormatsExEx(&Locale_EnumDateFormatsProcExEx, localeName.Buffer(), DATE_YEARMONTH, (LPARAM)&formats);
		}

		void GetLongTimeFormats(const WString& localeName, collections::List<WString>& formats) const override
		{
			EnumTimeFormatsEx(&EnumTimeFormatsProcEx, localeName.Buffer(), 0, (LPARAM)&formats);
		}

		void GetShortTimeFormats(const WString& localeName, collections::List<WString>& formats) const override
		{
			EnumTimeFormatsEx(&EnumTimeFormatsProcEx, localeName.Buffer(), TIME_NOSECONDS, (LPARAM)&formats);
		}

		WString FormatDate(const WString& localeName, const WString& format, DateTime date) const override
		{
			SYSTEMTIME st = DateTimeToSystemTime(date);
			int length = GetDateFormatEx(localeName.Buffer(), 0, &st, format.Buffer(), NULL, 0, NULL);
			if (length == 0) return L"";
			Array<wchar_t> buffer(length);
			GetDateFormatEx(localeName.Buffer(), 0, &st, format.Buffer(), &buffer[0], (int)buffer.Count(), NULL);
			return &buffer[0];
		}

		WString FormatTime(const WString& localeName, const WString& format, DateTime time) const override
		{
			SYSTEMTIME st = DateTimeToSystemTime(time);
			int length = GetTimeFormatEx(localeName.Buffer(), 0, &st, format.Buffer(), NULL, 0);
			if (length == 0) return L"";
			Array<wchar_t> buffer(length);
			GetTimeFormatEx(localeName.Buffer(), 0, &st, format.Buffer(), &buffer[0], (int)buffer.Count());
			return &buffer[0];
		}

		WString FormatNumber(const WString& localeName, const WString& number) const override
		{
			int length = GetNumberFormatEx(localeName.Buffer(), 0, number.Buffer(), NULL, NULL, 0);
			if (length == 0) return L"";
			Array<wchar_t> buffer(length);
			GetNumberFormatEx(localeName.Buffer(), 0, number.Buffer(), NULL, &buffer[0], (int)buffer.Count());
			return &buffer[0];
		}

		WString FormatCurrency(const WString& localeName, const WString& currency) const override
		{
			int length = GetCurrencyFormatEx(localeName.Buffer(), 0, currency.Buffer(), NULL, NULL, 0);
			if (length == 0) return L"";
			Array<wchar_t> buffer(length);
			GetCurrencyFormatEx(localeName.Buffer(), 0, currency.Buffer(), NULL, &buffer[0], (int)buffer.Count());
			return &buffer[0];
		}

		WString GetShortDayOfWeekName(const WString& localeName, vint dayOfWeek) const override
		{
			return FormatDate(localeName, L"ddd", DateTime::FromDateTime(2000, 1, 2 + dayOfWeek));
		}

		WString GetLongDayOfWeekName(const WString& localeName, vint dayOfWeek) const override
		{
			return FormatDate(localeName, L"dddd", DateTime::FromDateTime(2000, 1, 2 + dayOfWeek));
		}

		WString GetShortMonthName(const WString& localeName, vint month) const override
		{
			return FormatDate(localeName, L"MMM", DateTime::FromDateTime(2000, month, 1));
		}

		WString GetLongMonthName(const WString& localeName, vint month) const override
		{
			return FormatDate(localeName, L"MMMM", DateTime::FromDateTime(2000, month, 1));
		}

		WString ToLower(const WString& localeName, const WString& str) const override
		{
			return Transform(localeName, str, LCMAP_LOWERCASE);
		}

		WString ToUpper(const WString& localeName, const WString& str) const override
		{
			return Transform(localeName, str, LCMAP_UPPERCASE);
		}

		WString ToLinguisticLower(const WString& localeName, const WString& str) const override
		{
			return Transform(localeName, str, LCMAP_LOWERCASE | LCMAP_LINGUISTIC_CASING);
		}

		WString ToLinguisticUpper(const WString& localeName, const WString& str) const override
		{
			return Transform(localeName, str, LCMAP_UPPERCASE | LCMAP_LINGUISTIC_CASING);
		}

		vint Compare(const WString& localeName, const WString& s1, const WString& s2, Locale::Normalization normalization) const override
		{
			switch (CompareStringEx(localeName.Buffer(), TranslateNormalization(normalization), s1.Buffer(), (int)s1.Length(), s2.Buffer(), (int)s2.Length(), NULL, NULL, NULL))
			{
			case CSTR_LESS_THAN: return -1;
			case CSTR_GREATER_THAN: return 1;
			default: return 0;
			}
		}

		vint CompareOrdinal(const WString& s1, const WString& s2) const override
		{
			switch (CompareStringOrdinal(s1.Buffer(), (int)s1.Length(), s2.Buffer(), (int)s2.Length(), FALSE))
			{
			case CSTR_LESS_THAN: return -1;
			case CSTR_GREATER_THAN: return 1;
			default: return 0;
			}
		}

		vint CompareOrdinalIgnoreCase(const WString& s1, const WString& s2) const override
		{
			switch (CompareStringOrdinal(s1.Buffer(), (int)s1.Length(), s2.Buffer(), (int)s2.Length(), TRUE))
			{
			case CSTR_LESS_THAN: return -1;
			case CSTR_GREATER_THAN: return 1;
			default: return 0;
			}
		}

		collections::Pair<vint, vint> FindFirst(const WString& localeName, const WString& text, const WString& find, Locale::Normalization normalization) const override
		{
			int length = 0;
			int result = FindNLSStringEx(localeName.Buffer(), FIND_FROMSTART | TranslateNormalization(normalization), text.Buffer(), (int)text.Length(), find.Buffer(), (int)find.Length(), &length, NULL, NULL, NULL);
			return result == -1 ? collections::Pair<vint, vint>(-1, 0) : collections::Pair<vint, vint>(result, length);
		}

		collections::Pair<vint, vint> FindLast(const WString& localeName, const WString& text, const WString& find, Locale::Normalization normalization) const override
		{
			int length = 0;
			int result = FindNLSStringEx(localeName.Buffer(), FIND_FROMEND | TranslateNormalization(normalization), text.Buffer(), (int)text.Length(), find.Buffer(), (int)find.Length(), &length, NULL, NULL, NULL);
			return result == -1 ? collections::Pair<vint, vint>(-1, 0) : collections::Pair<vint, vint>(result, length);
		}

		bool StartsWith(const WString& localeName, const WString& text, const WString& find, Locale::Normalization normalization) const override
		{
			int result = FindNLSStringEx(localeName.Buffer(), FIND_STARTSWITH | TranslateNormalization(normalization), text.Buffer(), (int)text.Length(), find.Buffer(), (int)find.Length(), NULL, NULL, NULL, NULL);
			return result != -1;
		}

		bool EndsWith(const WString& localeName, const WString& text, const WString& find, Locale::Normalization normalization) const override
		{
			int result = FindNLSStringEx(localeName.Buffer(), FIND_ENDSWITH | TranslateNormalization(normalization), text.Buffer(), (int)text.Length(), find.Buffer(), (int)find.Length(), NULL, NULL, NULL, NULL);
			return result != -1;
		}
	};

	ILocaleImpl* GetOSLocaleImpl()
	{
		static WindowsLocaleImpl windowsLocaleImpl;
		return &windowsLocaleImpl;
	}

/***********************************************************************
Locale (Windows Specific)
***********************************************************************/

	WString Locale::ToFullWidth(const WString& str)const
	{
		return Transform(localeName, str, LCMAP_FULLWIDTH);
	}

	WString Locale::ToHalfWidth(const WString& str)const
	{
		return Transform(localeName, str, LCMAP_HALFWIDTH);
	}

	WString Locale::ToHiragana(const WString& str)const
	{
		return Transform(localeName, str, LCMAP_HIRAGANA);
	}

	WString Locale::ToKatagana(const WString& str)const
	{
		return Transform(localeName, str, LCMAP_KATAKANA);
	}

	WString Locale::ToSimplifiedChinese(const WString& str)const
	{
		return Transform(localeName, str, LCMAP_SIMPLIFIED_CHINESE);
	}

	WString Locale::ToTraditionalChinese(const WString& str)const
	{
		return Transform(localeName, str, LCMAP_TRADITIONAL_CHINESE);
	}

	WString Locale::ToTileCase(const WString& str)const
	{
		return Transform(localeName, str, LCMAP_TITLECASE);
	}
}


/***********************************************************************
.\THREADING.WINDOWS.CPP
***********************************************************************/
/***********************************************************************
Author: Zihan Chen (vczh)
Licensed under https://github.com/vczh-libraries/License
***********************************************************************/


#ifndef VCZH_MSVC
static_assert(false, "Do not build this file for non-Windows applications.");
#endif

namespace vl
{
	using namespace threading_internal;
	using namespace collections;

/***********************************************************************
WaitableObject
***********************************************************************/

	namespace threading_internal
	{
		struct WaitableData
		{
			HANDLE			handle;

			WaitableData(HANDLE _handle)
				:handle(_handle)
			{
			}
		};
	}

	WaitableObject::WaitableObject()
		:waitableData(0)
	{
	}

	void WaitableObject::SetData(threading_internal::WaitableData* data)
	{
		waitableData=data;
	}

	bool WaitableObject::IsCreated()
	{
		return waitableData!=0;
	}

	bool WaitableObject::Wait()
	{
		return WaitForTime(INFINITE);
	}

	bool WaitableObject::WaitForTime(vint ms)
	{
		if(IsCreated())
		{
			if(WaitForSingleObject(waitableData->handle, (DWORD)ms)==WAIT_OBJECT_0)
			{
				return true;
			}
		}
		return false;
	}

	bool WaitableObject::WaitAll(WaitableObject** objects, vint count)
	{
		Array<HANDLE> handles(count);
		for(vint i=0;i<count;i++)
		{
			handles[i]=objects[i]->waitableData->handle;
		}
		DWORD result=WaitForMultipleObjects((DWORD)count, &handles[0], TRUE, INFINITE);
		return result==WAIT_OBJECT_0 || result==WAIT_ABANDONED_0;

	}

	bool WaitableObject::WaitAllForTime(WaitableObject** objects, vint count, vint ms)
	{
		Array<HANDLE> handles(count);
		for(vint i=0;i<count;i++)
		{
			handles[i]=objects[i]->waitableData->handle;
		}
		DWORD result=WaitForMultipleObjects((DWORD)count, &handles[0], TRUE, (DWORD)ms);
		return result==WAIT_OBJECT_0 || result==WAIT_ABANDONED_0;
	}

	vint WaitableObject::WaitAny(WaitableObject** objects, vint count, bool* abandoned)
	{
		Array<HANDLE> handles(count);
		for(vint i=0;i<count;i++)
		{
			handles[i]=objects[i]->waitableData->handle;
		}
		DWORD result=WaitForMultipleObjects((DWORD)count, &handles[0], FALSE, INFINITE);
		if(WAIT_OBJECT_0 <= result && result<WAIT_OBJECT_0+count)
		{
			*abandoned=false;
			return result-WAIT_OBJECT_0;
		}
		else if(WAIT_ABANDONED_0 <= result && result<WAIT_ABANDONED_0+count)
		{
			*abandoned=true;
			return result-WAIT_ABANDONED_0;
		}
		else
		{
			return -1;
		}
	}

	vint WaitableObject::WaitAnyForTime(WaitableObject** objects, vint count, vint ms, bool* abandoned)
	{
		Array<HANDLE> handles(count);
		for(vint i=0;i<count;i++)
		{
			handles[i]=objects[i]->waitableData->handle;
		}
		DWORD result=WaitForMultipleObjects((DWORD)count, &handles[0], FALSE, (DWORD)ms);
		if(WAIT_OBJECT_0 <= result && result<WAIT_OBJECT_0+count)
		{
			*abandoned=false;
			return result-WAIT_OBJECT_0;
		}
		else if(WAIT_ABANDONED_0 <= result && result<WAIT_ABANDONED_0+count)
		{
			*abandoned=true;
			return result-WAIT_ABANDONED_0;
		}
		else
		{
			return -1;
		}
	}

/***********************************************************************
Thread
***********************************************************************/

	namespace threading_internal
	{
		struct ThreadData : public WaitableData
		{
			DWORD						id;

			ThreadData()
				:WaitableData(NULL)
			{
				id=-1;
			}
		};

		class ProceduredThread : public Thread
		{
		private:
			Thread::ThreadProcedure		procedure;
			void*						argument;
			bool						deleteAfterStopped;

		protected:
			void Run()
			{
				bool deleteAfterStopped = this->deleteAfterStopped;
				ThreadLocalStorage::FixStorages();
				try
				{
					procedure(this, argument);
					threadState=Thread::Stopped;
					ThreadLocalStorage::ClearStorages();
				}
				catch (...)
				{
					ThreadLocalStorage::ClearStorages();
					throw;
				}
				if(deleteAfterStopped)
				{
					delete this;
				}
			}
		public:
			ProceduredThread(Thread::ThreadProcedure _procedure, void* _argument, bool _deleteAfterStopped)
				:procedure(_procedure)
				,argument(_argument)
				,deleteAfterStopped(_deleteAfterStopped)
			{
			}
		};

		class LambdaThread : public Thread
		{
		private:
			Func<void()>				procedure;
			bool						deleteAfterStopped;

		protected:
			void Run()
			{
				bool deleteAfterStopped = this->deleteAfterStopped;
				ThreadLocalStorage::FixStorages();
				try
				{
					procedure();
					threadState=Thread::Stopped;
					ThreadLocalStorage::ClearStorages();
				}
				catch (...)
				{
					ThreadLocalStorage::ClearStorages();
					throw;
				}
				if(deleteAfterStopped)
				{
					delete this;
				}
			}
		public:
			LambdaThread(const Func<void()>& _procedure, bool _deleteAfterStopped)
				:procedure(_procedure)
				,deleteAfterStopped(_deleteAfterStopped)
			{
			}
		};
	}

	void InternalThreadProc(Thread* thread)
	{
		thread->Run();
	}

	DWORD WINAPI InternalThreadProcWrapper(LPVOID lpParameter)
	{
		InternalThreadProc((Thread*)lpParameter);
		return 0;
	}

	Thread::Thread()
	{
		internalData=new ThreadData;
		internalData->handle=CreateThread(NULL, 0, InternalThreadProcWrapper, this, CREATE_SUSPENDED, &internalData->id);
		threadState=Thread::NotStarted;
		SetData(internalData);
	}

	Thread::~Thread()
	{
		if (internalData)
		{
			Stop();
			CloseHandle(internalData->handle);
			delete internalData;
		}
	}

	Thread* Thread::CreateAndStart(ThreadProcedure procedure, void* argument, bool deleteAfterStopped)
	{
		if(procedure)
		{
			Thread* thread=new ProceduredThread(procedure, argument, deleteAfterStopped);
			if(thread->Start())
			{
				return thread;
			}
			else
			{
				delete thread;
			}
		}
		return 0;
	}

	Thread* Thread::CreateAndStart(const Func<void()>& procedure, bool deleteAfterStopped)
	{
		Thread* thread=new LambdaThread(procedure, deleteAfterStopped);
		if(thread->Start())
		{
			return thread;
		}
		else
		{
			delete thread;
		}
		return 0;
	}

	void Thread::Sleep(vint ms)
	{
		::Sleep((DWORD)ms);
	}

	
	vint Thread::GetCPUCount()
	{
		SYSTEM_INFO info;
		GetSystemInfo(&info);
		return info.dwNumberOfProcessors;
	}

	vint Thread::GetCurrentThreadId()
	{
		return (vint)::GetCurrentThreadId();
	}

	bool Thread::Start()
	{
		if(threadState==Thread::NotStarted && internalData->handle!=NULL)
		{
			if(ResumeThread(internalData->handle)!=-1)
			{
				threadState=Thread::Running;
				return true;
			}
		}
		return false;
	}

	bool Thread::Stop()
	{
		if(internalData->handle!=NULL)
		{
			if (SuspendThread(internalData->handle) != -1)
			{
				threadState=Thread::Stopped;
				return true;
			}
		}
		return false;
	}

	Thread::ThreadState Thread::GetState()
	{
		return threadState;
	}

	void Thread::SetCPU(vint index)
	{
		SetThreadAffinityMask(internalData->handle, ((vint)1 << index));
	}

/***********************************************************************
Mutex
***********************************************************************/

	namespace threading_internal
	{
		struct MutexData : public WaitableData
		{
			MutexData(HANDLE _handle)
				:WaitableData(_handle)
			{
			}
		};
	}

	Mutex::Mutex()
		:internalData(0)
	{
	}

	Mutex::~Mutex()
	{
		if(internalData)
		{
			CloseHandle(internalData->handle);
			delete internalData;
		}
	}

	bool Mutex::Create(bool owned, const WString& name)
	{
		if(IsCreated())return false;
		BOOL aOwned=owned?TRUE:FALSE;
		LPCTSTR aName=name==L""?NULL:name.Buffer();
		HANDLE handle=CreateMutex(NULL, aOwned, aName);
		if(handle)
		{
			internalData=new MutexData(handle);
			SetData(internalData);
		}
		return IsCreated();
	}

	bool Mutex::Open(bool inheritable, const WString& name)
	{
		if(IsCreated())return false;
		BOOL aInteritable=inheritable?TRUE:FALSE;
		HANDLE handle=OpenMutex(SYNCHRONIZE, aInteritable, name.Buffer());
		if(handle)
		{
			internalData=new MutexData(handle);
			SetData(internalData);
		}
		return IsCreated();
	}

	bool Mutex::Release()
	{
		if(IsCreated())
		{
			return ReleaseMutex(internalData->handle)!=0;
		}
		return false;
	}

/***********************************************************************
Semaphore
***********************************************************************/

	namespace threading_internal
	{
		struct SemaphoreData : public WaitableData
		{
			SemaphoreData(HANDLE _handle)
				:WaitableData(_handle)
			{
			}
		};
	}

	Semaphore::Semaphore()
		:internalData(0)
	{
	}

	Semaphore::~Semaphore()
	{
		if(internalData)
		{
			CloseHandle(internalData->handle);
			delete internalData;
		}
	}

	bool Semaphore::Create(vint initialCount, vint maxCount, const WString& name)
	{
		if(IsCreated())return false;
		LONG aInitial=(LONG)initialCount;
		LONG aMax=(LONG)maxCount;
		LPCTSTR aName=name==L""?NULL:name.Buffer();
		HANDLE handle=CreateSemaphore(NULL, aInitial, aMax, aName);
		if(handle)
		{
			internalData=new SemaphoreData(handle);
			SetData(internalData);
		}
		return IsCreated();
	}

	bool Semaphore::Open(bool inheritable, const WString& name)
	{
		if(IsCreated())return false;
		BOOL aInteritable=inheritable?TRUE:FALSE;
		HANDLE handle=OpenSemaphore(SYNCHRONIZE, aInteritable, name.Buffer());
		if(handle)
		{
			internalData=new SemaphoreData(handle);
			SetData(internalData);
		}
		return IsCreated();
	}

	bool Semaphore::Release()
	{
		if(IsCreated())
		{
			return Release(1)!=-1;
		}
		return false;
	}

	vint Semaphore::Release(vint count)
	{
		if(IsCreated())
		{
			LONG previous=-1;
			if(ReleaseSemaphore(internalData->handle, (LONG)count, &previous)!=0)
			{
				return (vint)previous;
			}
		}
		return -1;
	}

/***********************************************************************
EventObject
***********************************************************************/

	namespace threading_internal
	{
		struct EventData : public WaitableData
		{
			EventData(HANDLE _handle)
				:WaitableData(_handle)
			{
			}
		};
	}

	EventObject::EventObject()
		:internalData(0)
	{
	}

	EventObject::~EventObject()
	{
		if(internalData)
		{
			CloseHandle(internalData->handle);
			delete internalData;
		}
	}

	bool EventObject::CreateAutoUnsignal(bool signaled, const WString& name)
	{
		if(IsCreated())return false;
		BOOL aSignaled=signaled?TRUE:FALSE;
		LPCTSTR aName=name==L""?NULL:name.Buffer();
		HANDLE handle=CreateEvent(NULL, FALSE, aSignaled, aName);
		if(handle)
		{
			internalData=new EventData(handle);
			SetData(internalData);
		}
		return IsCreated();
	}

	bool EventObject::CreateManualUnsignal(bool signaled, const WString& name)
	{
		if(IsCreated())return false;
		BOOL aSignaled=signaled?TRUE:FALSE;
		LPCTSTR aName=name==L""?NULL:name.Buffer();
		HANDLE handle=CreateEvent(NULL, TRUE, aSignaled, aName);
		if(handle)
		{
			internalData=new EventData(handle);
			SetData(internalData);
		}
		return IsCreated();
	}

	bool EventObject::Open(bool inheritable, const WString& name)
	{
		if(IsCreated())return false;
		BOOL aInteritable=inheritable?TRUE:FALSE;
		HANDLE handle=OpenEvent(SYNCHRONIZE, aInteritable, name.Buffer());
		if(handle)
		{
			internalData=new EventData(handle);
			SetData(internalData);
		}
		return IsCreated();
	}

	bool EventObject::Signal()
	{
		if(IsCreated())
		{
			return SetEvent(internalData->handle)!=0;
		}
		return false;
	}

	bool EventObject::Unsignal()
	{
		if(IsCreated())
		{
			return ResetEvent(internalData->handle)!=0;
		}
		return false;
	}

/***********************************************************************
ThreadPoolLite
***********************************************************************/

		DWORD WINAPI ThreadPoolQueueFunc(void* argument)
		{
			auto proc=Ptr((Func<void()>*)argument);
			ThreadLocalStorage::FixStorages();
			try
			{
				(*proc.Obj())();
				ThreadLocalStorage::ClearStorages();
			}
			catch (...)
			{
				ThreadLocalStorage::ClearStorages();
			}
			return 0;
		}

		ThreadPoolLite::ThreadPoolLite()
		{
		}

		ThreadPoolLite::~ThreadPoolLite()
		{
		}

		bool ThreadPoolLite::Queue(void(*proc)(void*), void* argument)
		{
			return Queue([=]() {proc(argument); });
		}

		bool ThreadPoolLite::Queue(const Func<void()>& proc)
		{
			Func<void()>* p=new Func<void()>(proc);
			if(QueueUserWorkItem(&ThreadPoolQueueFunc, p, WT_EXECUTEDEFAULT))
			{
				return true;
			}
			else
			{
				delete p;
				return false;
			}
		}

/***********************************************************************
CriticalSection
***********************************************************************/

	namespace threading_internal
	{
		struct CriticalSectionData
		{
			CRITICAL_SECTION		criticalSection;
		};
	}

	CriticalSection::Scope::Scope(CriticalSection& _criticalSection)
		:criticalSection(&_criticalSection)
	{
		criticalSection->Enter();
	}

	CriticalSection::Scope::~Scope()
	{
		criticalSection->Leave();
	}
			
	CriticalSection::CriticalSection()
	{
		internalData=new CriticalSectionData;
		InitializeCriticalSection(&internalData->criticalSection);
	}

	CriticalSection::~CriticalSection()
	{
		DeleteCriticalSection(&internalData->criticalSection);
		delete internalData;
	}

	bool CriticalSection::TryEnter()
	{
		return TryEnterCriticalSection(&internalData->criticalSection)!=0;
	}

	void CriticalSection::Enter()
	{
		EnterCriticalSection(&internalData->criticalSection);
	}

	void CriticalSection::Leave()
	{
		LeaveCriticalSection(&internalData->criticalSection);
	}

/***********************************************************************
ReaderWriterLock
***********************************************************************/

	namespace threading_internal
	{
		struct ReaderWriterLockData
		{
			SRWLOCK			lock;
		};
	}

	ReaderWriterLock::ReaderScope::ReaderScope(ReaderWriterLock& _lock)
		:lock(&_lock)
	{
		lock->EnterReader();
	}

	ReaderWriterLock::ReaderScope::~ReaderScope()
	{
		lock->LeaveReader();
	}

	ReaderWriterLock::WriterScope::WriterScope(ReaderWriterLock& _lock)
		:lock(&_lock)
	{
		lock->EnterWriter();
	}

	ReaderWriterLock::WriterScope::~WriterScope()
	{
		lock->LeaveWriter();
	}

	ReaderWriterLock::ReaderWriterLock()
		:internalData(new threading_internal::ReaderWriterLockData)
	{
		InitializeSRWLock(&internalData->lock);
	}

	ReaderWriterLock::~ReaderWriterLock()
	{
		delete internalData;
	}

	bool ReaderWriterLock::TryEnterReader()
	{
		return TryAcquireSRWLockShared(&internalData->lock)!=0;
	}

	void ReaderWriterLock::EnterReader()
	{
		AcquireSRWLockShared(&internalData->lock);
	}

	void ReaderWriterLock::LeaveReader()
	{
		ReleaseSRWLockShared(&internalData->lock);
	}

	bool ReaderWriterLock::TryEnterWriter()
	{
		return TryAcquireSRWLockExclusive(&internalData->lock)!=0;
	}

	void ReaderWriterLock::EnterWriter()
	{
		AcquireSRWLockExclusive(&internalData->lock);
	}

	void ReaderWriterLock::LeaveWriter()
	{
		ReleaseSRWLockExclusive(&internalData->lock);
	}

/***********************************************************************
ConditionVariable
***********************************************************************/

	namespace threading_internal
	{
		struct ConditionVariableData
		{
			CONDITION_VARIABLE			variable;
		};
	}

	ConditionVariable::ConditionVariable()
		:internalData(new threading_internal::ConditionVariableData)
	{
		InitializeConditionVariable(&internalData->variable);
	}

	ConditionVariable::~ConditionVariable()
	{
		delete internalData;
	}

	bool ConditionVariable::SleepWith(CriticalSection& cs)
	{
		return SleepConditionVariableCS(&internalData->variable, &cs.internalData->criticalSection, INFINITE)!=0;
	}

	bool ConditionVariable::SleepWithForTime(CriticalSection& cs, vint ms)
	{
		return SleepConditionVariableCS(&internalData->variable, &cs.internalData->criticalSection, (DWORD)ms)!=0;
	}

	bool ConditionVariable::SleepWithReader(ReaderWriterLock& lock)
	{
		return SleepConditionVariableSRW(&internalData->variable, &lock.internalData->lock, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED)!=0;
	}

	bool ConditionVariable::SleepWithReaderForTime(ReaderWriterLock& lock, vint ms)
	{
		return SleepConditionVariableSRW(&internalData->variable, &lock.internalData->lock, (DWORD)ms, CONDITION_VARIABLE_LOCKMODE_SHARED)!=0;
	}

	bool ConditionVariable::SleepWithWriter(ReaderWriterLock& lock)
	{
		return SleepConditionVariableSRW(&internalData->variable, &lock.internalData->lock, INFINITE, 0)!=0;
	}

	bool ConditionVariable::SleepWithWriterForTime(ReaderWriterLock& lock, vint ms)
	{
		return SleepConditionVariableSRW(&internalData->variable, &lock.internalData->lock, (DWORD)ms, 0)!=0;
	}

	void ConditionVariable::WakeOnePending()
	{
		WakeConditionVariable(&internalData->variable);
	}

	void ConditionVariable::WakeAllPendings()
	{
		WakeAllConditionVariable(&internalData->variable);
	}

/***********************************************************************
ThreadLocalStorage
***********************************************************************/

#define KEY ((DWORD&)key)

	ThreadLocalStorage::ThreadLocalStorage(Destructor _destructor)
		:destructor(_destructor)
	{
		static_assert(sizeof(key) >= sizeof(DWORD), "ThreadLocalStorage's key storage is not large enouth.");
		PushStorage(this);
		KEY = TlsAlloc();
		CHECK_ERROR(KEY != TLS_OUT_OF_INDEXES, L"vl::ThreadLocalStorage::ThreadLocalStorage()#Failed to alloc new thread local storage index.");
	}

	ThreadLocalStorage::~ThreadLocalStorage()
	{
		TlsFree(KEY);
	}

	void* ThreadLocalStorage::Get()
	{
		CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Get()#Cannot access a disposed ThreadLocalStorage.");
		return TlsGetValue(KEY);
	}

	void ThreadLocalStorage::Set(void* data)
	{
		CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Set()#Cannot access a disposed ThreadLocalStorage.");
		TlsSetValue(KEY, data);
	}

#undef KEY
}


/***********************************************************************
.\ENCODING\CHARFORMAT\CHARFORMAT.WINDOWS.CPP
***********************************************************************/
/***********************************************************************
Author: Zihan Chen (vczh)
Licensed under https://github.com/vczh-libraries/License
***********************************************************************/

#include <windows.h>

#ifndef VCZH_MSVC
static_assert(false, "Do not build this file for non-Windows applications.");
#endif

namespace vl
{
	namespace stream
	{
		bool IsMbcsLeadByte(char c)
		{
			return IsDBCSLeadByte(c);
		}

		void MbcsToWChar(wchar_t* wideBuffer, vint wideChars, vint wideReaded, char* mbcsBuffer, vint mbcsChars)
		{
			MultiByteToWideChar(CP_THREAD_ACP, 0, mbcsBuffer, (int)mbcsChars, wideBuffer, (int)wideChars);
		}

/***********************************************************************
MbcsEncoder
***********************************************************************/

		vint MbcsEncoder::WriteString(wchar_t* _buffer, vint chars)
		{
			vint length = WideCharToMultiByte(CP_THREAD_ACP, 0, _buffer, (int)chars, NULL, NULL, NULL, NULL);
			char* mbcs = new char[length];
			WideCharToMultiByte(CP_THREAD_ACP, 0, _buffer, (int)chars, mbcs, (int)length, NULL, NULL);
			vint result = stream->Write(mbcs, length);
			delete[] mbcs;

			if (result != length)
			{
				Close();
				return 0;
			}
			return chars;
		}

/***********************************************************************
Helper Functions
***********************************************************************/

		extern bool CanBeMbcs(unsigned char* buffer, vint size);
		extern bool CanBeUtf8(unsigned char* buffer, vint size);
		extern bool CanBeUtf16(unsigned char* buffer, vint size, bool& hitSurrogatePairs);
		extern bool CanBeUtf16BE(unsigned char* buffer, vint size, bool& hitSurrogatePairs);

		template<vint Count>
		bool GetEncodingResult(int(&tests)[Count], bool(&results)[Count], int test)
		{
			for (vint i = 0; i < Count; i++)
			{
				if (tests[i] & test)
				{
					if (results[i]) return true;
				}
			}
			return false;
		}
		
/***********************************************************************
TestEncoding
***********************************************************************/

		extern void TestEncodingInternal(
			unsigned char* buffer,
			vint size,
			BomEncoder::Encoding& encoding,
			bool containsBom,
			bool utf16HitSurrogatePairs,
			bool utf16BEHitSurrogatePairs,
			bool roughMbcs,
			bool roughUtf8,
			bool roughUtf16,
			bool roughUtf16BE
		)
		{
			int tests[] =
			{
				IS_TEXT_UNICODE_REVERSE_ASCII16,
				IS_TEXT_UNICODE_REVERSE_STATISTICS,
				IS_TEXT_UNICODE_REVERSE_CONTROLS,

				IS_TEXT_UNICODE_ASCII16,
				IS_TEXT_UNICODE_STATISTICS,
				IS_TEXT_UNICODE_CONTROLS,

				IS_TEXT_UNICODE_ILLEGAL_CHARS,
				IS_TEXT_UNICODE_ODD_LENGTH,
				IS_TEXT_UNICODE_NULL_BYTES,
			};

			const vint TestCount = sizeof(tests) / sizeof(*tests);
			bool results[TestCount];
			for (vint i = 0; i < TestCount; i++)
			{
				int test = tests[i];
				results[i] = IsTextUnicode(buffer, (int)size, &test) != 0;
			}

			if (size % 2 == 0
				&& !GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_ASCII16)
				&& !GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_STATISTICS)
				&& !GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_CONTROLS)
				)
			{
				for (vint i = 0; i < size; i += 2)
				{
					unsigned char c = buffer[i];
					buffer[i] = buffer[i + 1];
					buffer[i + 1] = c;
				}
				// 3 = (count of reverse group) = (count of unicode group)
				for (vint i = 0; i < 3; i++)
				{
					int test = tests[i + 3];
					results[i] = IsTextUnicode(buffer, (int)size, &test) != 0;
				}
				for (vint i = 0; i < size; i += 2)
				{
					unsigned char c = buffer[i];
					buffer[i] = buffer[i + 1];
					buffer[i + 1] = c;
				}
			}

			if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_NOT_UNICODE_MASK))
			{
				if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_NOT_ASCII_MASK))
				{
					encoding = BomEncoder::Utf8;
				}
				else if (roughUtf8 || !roughMbcs)
				{
					encoding = BomEncoder::Utf8;
				}
			}
			else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_ASCII16))
			{
				encoding = BomEncoder::Utf16;
			}
			else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_ASCII16))
			{
				encoding = BomEncoder::Utf16BE;
			}
			else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_CONTROLS))
			{
				encoding = BomEncoder::Utf16;
			}
			else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_REVERSE_CONTROLS))
			{
				encoding = BomEncoder::Utf16BE;
			}
			else
			{
				if (!roughUtf8)
				{
					if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_STATISTICS))
					{
						encoding = BomEncoder::Utf16;
					}
					else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_STATISTICS))
					{
						encoding = BomEncoder::Utf16BE;
					}
				}
				else if (GetEncodingResult(tests, results, IS_TEXT_UNICODE_NOT_UNICODE_MASK))
				{
					encoding = BomEncoder::Utf8;
				}
				else if (roughUtf8 || !roughMbcs)
				{
					encoding = BomEncoder::Utf8;
				}
			}
		}
	}
}

