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

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


namespace vl
{

/***********************************************************************
Error
***********************************************************************/

	Error::Error(const wchar_t* _description)
	{
		description=_description;
	}

	const wchar_t* Error::Description()const
	{
		return description;
	}
}


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


namespace vl
{
	namespace console
	{
		bool& GetConsoleEnabled()
		{
			static bool enabled = true;
			return enabled;
		}
		
/***********************************************************************
Console
***********************************************************************/

		void Console::Enable()
		{
			GetConsoleEnabled() = true;
		}

		void Console::Disable()
		{
			GetConsoleEnabled() = false;
		}

		bool Console::IsEnabled()
		{
			return GetConsoleEnabled();
		}

		void Console::Write(const wchar_t* string)
		{
			Write(string, wcslen(string));
		}

		void Console::Write(const WString& string)
		{
			Write(string.Buffer(), string.Length());
		}

		void Console::WriteLine(const WString& string)
		{
			Write(string);
			Write(L"\r\n");
		}
	}
}


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


namespace vl
{

/***********************************************************************
Exception
***********************************************************************/
	Exception::Exception(const WString& _message)
		:message(_message)
	{
	}

	const WString& Exception::Message()const
	{
		return message;
	}

/***********************************************************************
ArgumentException
***********************************************************************/

	ArgumentException::ArgumentException(const WString& _message, const WString& _function, const WString& _name)
		:Exception(_message)
		,function(_function)
		,name(_name)
	{
	}

	const WString& ArgumentException::GetFunction()const
	{
		return function;
	}

	const WString& ArgumentException::GetName()const
	{
		return name;
	}
}

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


namespace vl
{
	using namespace collections;

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

	GlobalStorageDescriptor* firstGlobalStorageDescriptor = nullptr;
	GlobalStorageDescriptor** lastGlobalStorageDescriptor = &firstGlobalStorageDescriptor;

	void RegisterStorageDescriptor(GlobalStorageDescriptor* globalStorageDescriptor)
	{
		*lastGlobalStorageDescriptor = globalStorageDescriptor;
		lastGlobalStorageDescriptor = &globalStorageDescriptor->next;
	}

	void FinalizeGlobalStorage()
	{
		auto current = firstGlobalStorageDescriptor;
		while (current)
		{
			current->globalStorage->EnsureFinalized();
			current = current->next;
		}
	}

/***********************************************************************
GlobalStorage
***********************************************************************/

	GlobalStorage::GlobalStorage()
	{
	}

	GlobalStorage::~GlobalStorage()
	{
	}

	bool GlobalStorage::IsInitialized()
	{
		return initialized;
	}

	void GlobalStorage::EnsureInitialized()
	{
		if (!initialized)
		{
			initialized = true;
			InitializeResource();
		}
	}

	void GlobalStorage::EnsureFinalized()
	{
		if (initialized)
		{
			initialized = false;
			FinalizeResource();
		}
	}
}


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


namespace vl
{
	namespace collections
	{
		using namespace po;

/***********************************************************************
PartialOrderingProcessor
***********************************************************************/

		void PartialOrderingProcessor::InitNodes(vint itemCount)
		{
			nodes.Resize(itemCount);

			for (vint i = 0; i < itemCount; i++)
			{
				auto& node = nodes[i];
				node.ins = &emptyList;
				node.outs = &emptyList;

				vint inIndex = ins.Keys().IndexOf(i);
				vint outIndex = outs.Keys().IndexOf(i);

				if (inIndex != -1)
				{
					node.ins = &ins.GetByIndex(inIndex);
				}
				if (outIndex != -1)
				{
					node.outs = &outs.GetByIndex(outIndex);
				}
			}
		}

		void PartialOrderingProcessor::VisitUnvisitedNode(po::Node& node, Array<vint>& reversedOrder, vint& used)
		{
			node.visited = true;
			for (vint i = node.outs->Count() - 1; i >= 0; i--)
			{
				auto& outNode = nodes[node.outs->Get(i)];
				if (!outNode.visited)
				{
					VisitUnvisitedNode(outNode, reversedOrder, used);
				}
			}
			reversedOrder[used++] = (vint)(&node - &nodes[0]);
		}

		void PartialOrderingProcessor::AssignUnassignedNode(po::Node& node, vint componentIndex, vint& used)
		{
			node.component = componentIndex;
			firstNodesBuffer[used++] = (vint)(&node - &nodes[0]);
			for (vint i = 0; i < node.ins->Count(); i++)
			{
				auto& inNode = nodes[node.ins->Get(i)];
				if (inNode.component == -1)
				{
					AssignUnassignedNode(inNode, componentIndex, used);
				}
			}
		}

		void PartialOrderingProcessor::Sort()
		{
			// Kosaraju's Algorithm
			CHECK_ERROR(components.Count() == 0, L"PartialOrdering::Sort()#Sorting twice is not allowed.");

			Array<vint> reversedOrder(nodes.Count());
			{
				vint used = 0;
				for (vint i = nodes.Count() - 1; i >= 0; i--)
				{
					auto& node = nodes[i];
					if (!node.visited)
					{
						VisitUnvisitedNode(node, reversedOrder, used);
					}
				}
			}

			firstNodesBuffer.Resize(nodes.Count());
			{
				vint lastUsed = 0;
				vint used = 0;
				for (vint i = reversedOrder.Count() - 1; i >= 0; i--)
				{
					auto& node = nodes[reversedOrder[i]];
					if (node.component == -1)
					{
						AssignUnassignedNode(node, components.Count(), used);

						Component component;
						component.firstNode = &firstNodesBuffer[lastUsed];
						component.nodeCount = used - lastUsed;
						lastUsed = used;
						components.Add(component);
					}
				}
			}
		}
	}
}

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


namespace vl
{
	extern IDateTimeImpl* GetOSDateTimeImpl();

	feature_injection::FeatureInjection<IDateTimeImpl>& GetDateTimeInjection()
	{
		static feature_injection::FeatureInjection<IDateTimeImpl> injection(GetOSDateTimeImpl());
		return injection;
	}

	void InjectDateTimeImpl(IDateTimeImpl* impl)
	{
		GetDateTimeInjection().Inject(impl);
	}

	void EjectDateTimeImpl(IDateTimeImpl* impl)
	{
		if (impl == nullptr)
		{
			GetDateTimeInjection().EjectAll();
		}
		else
		{
			GetDateTimeInjection().Eject(impl);
		}
	}

/***********************************************************************
DateTime
***********************************************************************/

	DateTime DateTime::LocalTime()
	{
		return GetDateTimeInjection().Get()->FromOSInternal(GetDateTimeInjection().Get()->LocalTime());
	}

	DateTime DateTime::UtcTime()
	{
		return GetDateTimeInjection().Get()->FromOSInternal(GetDateTimeInjection().Get()->UtcTime());
	}

	DateTime DateTime::FromDateTime(vint _year, vint _month, vint _day, vint _hour, vint _minute, vint _second, vint _milliseconds)
	{
		return GetDateTimeInjection().Get()->FromDateTime(_year, _month, _day, _hour, _minute, _second, _milliseconds);
	}

	DateTime DateTime::FromOSInternal(vuint64_t _osInternal)
	{
		return GetDateTimeInjection().Get()->FromOSInternal(_osInternal);
	}

	DateTime DateTime::ToLocalTime()
	{
		return GetDateTimeInjection().Get()->FromOSInternal(GetDateTimeInjection().Get()->UtcToLocalTime(osInternal));
	}

	DateTime DateTime::ToUtcTime()
	{
		return GetDateTimeInjection().Get()->FromOSInternal(GetDateTimeInjection().Get()->LocalToUtcTime(osInternal));
	}

	DateTime DateTime::Forward(vuint64_t milliseconds)
	{
		return GetDateTimeInjection().Get()->FromOSInternal(GetDateTimeInjection().Get()->Forward(osInternal, milliseconds));
	}

	DateTime DateTime::Backward(vuint64_t milliseconds)
	{
		return GetDateTimeInjection().Get()->FromOSInternal(GetDateTimeInjection().Get()->Backward(osInternal, milliseconds));
	}
}


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

#if defined VCZH_GCC
#include <ctype.h>
#include <wctype.h>
#endif

namespace vl
{
	namespace encoding
	{
		__forceinline bool IsInvalid(char32_t c)
		{
			return 0xD800U <= c && c <= 0xDFFFU;
		}

/***********************************************************************
UtfConversion<wchar_t>
***********************************************************************/

		vint UtfConversion<wchar_t>::From32(char32_t source, wchar_t(&dest)[BufferLength])
		{
#if defined VCZH_WCHAR_UTF16
			return UtfConversion<char16_t>::From32(source, reinterpret_cast<char16_t(&)[BufferLength]>(dest));
#elif defined VCZH_WCHAR_UTF32
			dest[0] = static_cast<wchar_t>(source);
			return 1;
#endif
		}

		vint UtfConversion<wchar_t>::To32(const wchar_t* source, vint sourceLength, char32_t& dest)
		{
#if defined VCZH_WCHAR_UTF16
			return UtfConversion<char16_t>::To32(reinterpret_cast<const char16_t*>(source), sourceLength, dest);
#elif defined VCZH_WCHAR_UTF32
			if (sourceLength <= 0) return -1;
			dest = static_cast<char32_t>(source[0]);
			return 1;
#endif
		}

/***********************************************************************
UtfConversion<char8_t>
***********************************************************************/

		/*
		How UCS-4 translates to UTF-8
			U-00000000 - U-0000007F:  0xxxxxxx
			U-00000080 - U-000007FF:  110xxxxx 10xxxxxx
			U-00000800 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx
			U-00010000 - U-001FFFFF:  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
			U-00200000 - U-03FFFFFF:  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
			U-04000000 - U-7FFFFFFF:  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
		*/

		vint UtfConversion<char8_t>::From32(char32_t source, char8_t(&dest)[BufferLength])
		{
			if (IsInvalid(source)) return -1;
			vuint32_t c = static_cast<vuint32_t>(source);
			vuint8_t(&ds)[BufferLength] = reinterpret_cast<vuint8_t(&)[BufferLength]>(dest);

			if (c <= 0x0000007FUL)
			{
				ds[0] = static_cast<vuint8_t>(c);
				return 1;
			}
			else if (c <= 0x000007FFUL)
			{
				ds[0] = static_cast<vuint8_t>((c >> 6) | 0b11000000U);
				ds[1] = static_cast<vuint8_t>((c & 0b00111111U) | 0b10000000U);
				return 2;
			}
			else if (c <= 0x0000FFFFUL)
			{
				ds[0] = static_cast<vuint8_t>((c >> 12) | 0b11100000U);
				ds[1] = static_cast<vuint8_t>(((c >> 6) & 0b00111111U) | 0b10000000U);
				ds[2] = static_cast<vuint8_t>((c & 0b00111111U) | 0b10000000U);
				return 3;
			}
			else if (c <= 0x001FFFFFUL)
			{
				ds[0] = static_cast<vuint8_t>((c >> 18) | 0b11110000U);
				ds[1] = static_cast<vuint8_t>(((c >> 12) & 0b00111111U) | 0b10000000U);
				ds[2] = static_cast<vuint8_t>(((c >> 6) & 0b00111111U) | 0b10000000U);
				ds[3] = static_cast<vuint8_t>((c & 0b00111111U) | 0b10000000U);
				return 4;
			}
			else if (c <= 0x03FFFFFFUL)
			{
				ds[0] = static_cast<vuint8_t>((c >> 24) | 0b11111000U);
				ds[1] = static_cast<vuint8_t>(((c >> 18) & 0b00111111U) | 0b10000000U);
				ds[2] = static_cast<vuint8_t>(((c >> 12) & 0b00111111U) | 0b10000000U);
				ds[3] = static_cast<vuint8_t>(((c >> 6) & 0b00111111U) | 0b10000000U);
				ds[4] = static_cast<vuint8_t>((c & 0b00111111U) | 0b10000000U);
				return 5;
			}
			else if (c <= 0x7FFFFFFFUL)
			{
				ds[0] = static_cast<vuint8_t>((c >> 30) | 0b11111100U);
				ds[1] = static_cast<vuint8_t>(((c >> 24) & 0b00111111U) | 0b10000000U);
				ds[2] = static_cast<vuint8_t>(((c >> 18) & 0b00111111U) | 0b10000000U);
				ds[3] = static_cast<vuint8_t>(((c >> 12) & 0b00111111U) | 0b10000000U);
				ds[4] = static_cast<vuint8_t>(((c >> 6) & 0b00111111U) | 0b10000000U);
				ds[5] = static_cast<vuint8_t>((c & 0b00111111U) | 0b10000000U);
				return 6;
			}
			else
			{
				return -1;
			}
		}

		vint UtfConversion<char8_t>::To32(const char8_t* source, vint sourceLength, char32_t& dest)
		{
			const vuint8_t* cs = reinterpret_cast<const vuint8_t*>(source);
			vuint32_t& d = reinterpret_cast<vuint32_t&>(dest);
			if (sourceLength <= 0) return -1;

			if (cs[0] < 0b10000000U)
			{
				d = cs[0];
				return 1;
			}
			else if (cs[0] < 0b11100000U)
			{
				if (sourceLength < 2) return -1;
				d = ((static_cast<vuint32_t>(cs[0]) & 0b00011111U) << 6) |
					((static_cast<vuint32_t>(cs[1]) & 0b00111111U));
				return 2;
			}
			else if (cs[0] < 0b11110000U)
			{
				if (sourceLength < 3) return -1;
				d = ((static_cast<vuint32_t>(cs[0]) & 0b00001111U) << 12) |
					((static_cast<vuint32_t>(cs[1]) & 0b00111111U) << 6) |
					((static_cast<vuint32_t>(cs[2]) & 0b00111111U));
				return 3;
			}
			else if (cs[0] < 0b11111000U)
			{
				if (sourceLength < 4) return -1;
				d = ((static_cast<vuint32_t>(cs[0]) & 0b00000111U) << 18) |
					((static_cast<vuint32_t>(cs[1]) & 0b00111111U) << 12) |
					((static_cast<vuint32_t>(cs[2]) & 0b00111111U) << 6) |
					((static_cast<vuint32_t>(cs[3]) & 0b00111111U));
				return 4;
			}
			else if (cs[0] < 0b11111100U)
			{
				if (sourceLength < 5) return -1;
				d = ((static_cast<vuint32_t>(cs[0]) & 0b00000011U) << 24) |
					((static_cast<vuint32_t>(cs[1]) & 0b00111111U) << 18) |
					((static_cast<vuint32_t>(cs[2]) & 0b00111111U) << 12) |
					((static_cast<vuint32_t>(cs[3]) & 0b00111111U) << 6) |
					((static_cast<vuint32_t>(cs[4]) & 0b00111111U));
				return 5;
			}
			else
			{
				if (sourceLength < 6) return -1;
				d = ((static_cast<vuint32_t>(cs[0]) & 0b00000001U) << 30) |
					((static_cast<vuint32_t>(cs[1]) & 0b00111111U) << 24) |
					((static_cast<vuint32_t>(cs[2]) & 0b00111111U) << 18) |
					((static_cast<vuint32_t>(cs[3]) & 0b00111111U) << 12) |
					((static_cast<vuint32_t>(cs[4]) & 0b00111111U) << 6) |
					((static_cast<vuint32_t>(cs[5]) & 0b00111111U));
				return 6;
			}
			if (IsInvalid(dest)) return -1;
			return 1;
		}

/***********************************************************************
UtfConversion<char16_t>
***********************************************************************/

		/*
		How UCS-4 translates to UTF-16 Surrogate Pair
			U' = yyyyyyyyyyxxxxxxxxxx  // U - 0x10000
			W1 = 110110yyyyyyyyyy      // 0xD800 + yyyyyyyyyy
			W2 = 110111xxxxxxxxxx      // 0xDC00 + xxxxxxxxxx
		*/

		vint UtfConversion<char16_t>::From32(char32_t source, char16_t(&dest)[BufferLength])
		{
			if (IsInvalid(source)) return -1;
			vuint32_t c = static_cast<vuint32_t>(source);
			vuint16_t(&ds)[BufferLength] = reinterpret_cast<vuint16_t(&)[BufferLength]>(dest);

			if (0x000000UL <= c && c <= 0x00D7FFUL)
			{
				ds[0] = static_cast<vuint16_t>(c);
				return 1;
			}
			else if (0x00E000UL <= c && c <= 0x00FFFFUL)
			{
				ds[0] = static_cast<vuint16_t>(c);
				return 1;
			}
			else if (0x010000UL <= c && c <= 0x10FFFFUL)
			{
				c -= 0x010000UL;
				ds[0] = static_cast<vuint16_t>((c >> 10) | 0xD800U);
				ds[1] = static_cast<vuint16_t>((c & 0x03FFU) | 0xDC00U);
				return 2;
			}
			else
			{
				return -1;
			}
		}

		vint UtfConversion<char16_t>::To32(const char16_t* source, vint sourceLength, char32_t& dest)
		{
			const vuint16_t* cs = reinterpret_cast<const vuint16_t* >(source);
			vuint32_t& d = reinterpret_cast<vuint32_t&>(dest);
			if (sourceLength <= 0) return -1;

			if ((cs[0] & 0xFC00U) == 0xD800U)
			{
				if (sourceLength < 2) return -1;
				if ((cs[1] & 0xFC00U) == 0xDC00U)
				{
					d = 0x010000UL + (
						((static_cast<vuint32_t>(cs[0]) & 0x03FF) << 10) |
						(static_cast<vuint32_t>(cs[1]) & 0x03FF)
						);
					if (IsInvalid(dest)) return -1;
					return 2;
				}
				else
				{
					return -1;
				}
			}
			else
			{
				d = cs[0];
				if (IsInvalid(dest)) return -1;
				return 1;
			}
		}

/***********************************************************************
UtfConversion<char16be_t>
***********************************************************************/

		vint UtfConversion<char16be_t>::From32(char32_t source, char16be_t(&dest)[BufferLength])
		{
			char16_t destle[BufferLength];
			vint result = UtfConversion<char16_t>::From32(source, destle);
			SwapByteForUtf16BE(destle[0]);
			SwapByteForUtf16BE(destle[1]);
			dest[0].value = destle[0];
			dest[1].value = destle[1];
			return result;
		}

		vint UtfConversion<char16be_t>::To32(const char16be_t* source, vint sourceLength, char32_t& dest)
		{
			char16_t destle[BufferLength];
			if (sourceLength >= 1) destle[0] = source[0].value;
			if (sourceLength >= 2) destle[1] = source[1].value;
			SwapByteForUtf16BE(destle[0]);
			SwapByteForUtf16BE(destle[1]);
			return UtfConversion<char16_t>::To32(destle, sourceLength, dest);
		}
	}

/***********************************************************************
String Conversions (buffer walkthrough)
***********************************************************************/

	template<typename TFrom, typename TTo, typename TReader>
	vint _utftoutf_reader(const TFrom* s, TTo* d, vint chars)
	{
		TReader reader(s);
		vint size = 0;
		if (d == nullptr)
		{
			while (reader.Read()) size++;
			return size + 1;
		}
		else
		{
			while (true)
			{
				if (chars == 0) break;
				auto c = reader.Read();
				*d++ = c;
				size++;
				chars--;
				if (!c) break;
			}
			return size;
		}
	}

	template<typename TFrom, typename TTo>
	vint _utftoutf(const TFrom* s, TTo* d, vint chars)
	{
		return _utftoutf_reader<TFrom, TTo, encoding::UtfStringToStringReader<TFrom, TTo>>(s, d, chars);
	}

	template vint			_utftoutf<wchar_t, char8_t>(const wchar_t* s, char8_t* d, vint chars);
	template vint			_utftoutf<wchar_t, char16_t>(const wchar_t* s, char16_t* d, vint chars);
	template vint			_utftoutf<char8_t, wchar_t>(const char8_t* s, wchar_t* d, vint chars);
	template vint			_utftoutf<char8_t, char16_t>(const char8_t* s, char16_t* d, vint chars);
	template vint			_utftoutf<char16_t, wchar_t>(const char16_t* s, wchar_t* d, vint chars);
	template vint			_utftoutf<char16_t, char8_t>(const char16_t* s, char8_t* d, vint chars);

	template vint			_utftoutf<char32_t, char8_t>(const char32_t* s, char8_t* d, vint chars);
	template vint			_utftoutf<char32_t, char16_t>(const char32_t* s, char16_t* d, vint chars);
	template vint			_utftoutf<char32_t, wchar_t>(const char32_t* s, wchar_t* d, vint chars);
	template vint			_utftoutf<char8_t, char32_t>(const char8_t* s, char32_t* d, vint chars);
	template vint			_utftoutf<char16_t, char32_t>(const char16_t* s, char32_t* d, vint chars);
	template vint			_utftoutf<wchar_t, char32_t>(const wchar_t* s, char32_t* d, vint chars);

/***********************************************************************
String Conversions (ObjectString)
***********************************************************************/

	template<typename TFrom, typename TTo, vint(*Convert)(const TFrom*, TTo*, vint)>
	ObjectString<TTo> ConvertStringDirect(const ObjectString<TFrom>& source)
	{
		vint len = Convert(source.Buffer(), nullptr, 0);
		if (len < 1) return {};
		TTo* buffer = new TTo[len];
		memset(buffer, 0, len * sizeof(TTo));
		Convert(source.Buffer(), buffer, len);
		return ObjectString<TTo>::TakeOver(buffer, len - 1);
	}

	template<typename TFrom, typename TTo>
	ObjectString<TTo> ConvertUtfString(const ObjectString<TFrom>& source)
	{
		return ConvertStringDirect<TFrom, TTo, _utftoutf<TFrom, TTo>>(source);
	}

#if defined VCZH_WCHAR_UTF16
	template<>
	ObjectString<char16_t> ConvertUtfString<wchar_t, char16_t>(const ObjectString<wchar_t>& source)
	{
		return ObjectString<char16_t>::UnsafeCastFrom<wchar_t>(source);
	}
	template<>
	ObjectString<wchar_t> ConvertUtfString<char16_t, wchar_t>(const ObjectString<char16_t>& source)
	{
		return ObjectString<wchar_t>::UnsafeCastFrom<char16_t>(source);
	}
	template ObjectString<wchar_t>		ConvertUtfString<char32_t, wchar_t>(const ObjectString<char32_t>& source);
	template ObjectString<char32_t>		ConvertUtfString<wchar_t, char32_t>(const ObjectString<wchar_t>& source);
#elif defined VCZH_WCHAR_UTF32
	template<>
	ObjectString<char32_t> ConvertUtfString<wchar_t, char32_t>(const ObjectString<wchar_t>& source)
	{
		return ObjectString<char32_t>::UnsafeCastFrom<wchar_t>(source);
	}
	template<>
	ObjectString<wchar_t> ConvertUtfString<char32_t, wchar_t>(const ObjectString<char32_t>& source)
	{
		return ObjectString<wchar_t>::UnsafeCastFrom<char32_t>(source);
	}
	template ObjectString<char16_t>		ConvertUtfString<wchar_t, char16_t>(const ObjectString<wchar_t>& source);
	template ObjectString<wchar_t>		ConvertUtfString<char16_t, wchar_t>(const ObjectString<char16_t>& source);
#endif

	template ObjectString<char8_t>		ConvertUtfString<wchar_t, char8_t>(const ObjectString<wchar_t>& source);
	template ObjectString<wchar_t>		ConvertUtfString<char8_t, wchar_t>(const ObjectString<char8_t>& source);
	template ObjectString<char16_t>		ConvertUtfString<char8_t, char16_t>(const ObjectString<char8_t>& source);
	template ObjectString<char8_t>		ConvertUtfString<char16_t, char8_t>(const ObjectString<char16_t>& source);

	template ObjectString<char8_t>		ConvertUtfString<char32_t, char8_t>(const ObjectString<char32_t>& source);
	template ObjectString<char16_t>		ConvertUtfString<char32_t, char16_t>(const ObjectString<char32_t>& source);
	template ObjectString<char32_t>		ConvertUtfString<char8_t, char32_t>(const ObjectString<char8_t>& source);
	template ObjectString<char32_t>		ConvertUtfString<char16_t, char32_t>(const ObjectString<char16_t>& source);

	template<typename TFrom, typename TTo>
	void ConvertUtfString(const ObjectString<TFrom>& source, ObjectString<TTo>& dest)
	{
		dest = ConvertUtfString<TFrom, TTo>(source);
	}

	template void		ConvertUtfString<wchar_t, char8_t>(const ObjectString<wchar_t>& source, ObjectString<char8_t>& dest);
	template void		ConvertUtfString<wchar_t, char16_t>(const ObjectString<wchar_t>& source, ObjectString<char16_t>& dest);
	template void		ConvertUtfString<char8_t, wchar_t>(const ObjectString<char8_t>& source, ObjectString<wchar_t>& dest);
	template void		ConvertUtfString<char8_t, char16_t>(const ObjectString<char8_t>& source, ObjectString<char16_t>& dest);
	template void		ConvertUtfString<char16_t, wchar_t>(const ObjectString<char16_t>& source, ObjectString<wchar_t>& dest);
	template void		ConvertUtfString<char16_t, char8_t>(const ObjectString<char16_t>& source, ObjectString<char8_t>& dest);

	template void		ConvertUtfString<char32_t, char8_t>(const ObjectString<char32_t>& source, ObjectString<char8_t>& dest);
	template void		ConvertUtfString<char32_t, char16_t>(const ObjectString<char32_t>& source, ObjectString<char16_t>& dest);
	template void		ConvertUtfString<char32_t, wchar_t>(const ObjectString<char32_t>& source, ObjectString<wchar_t>& dest);
	template void		ConvertUtfString<char8_t, char32_t>(const ObjectString<char8_t>& source, ObjectString<char32_t>& dest);
	template void		ConvertUtfString<char16_t, char32_t>(const ObjectString<char16_t>& source, ObjectString<char32_t>& dest);
	template void		ConvertUtfString<wchar_t, char32_t>(const ObjectString<wchar_t>& source, ObjectString<char32_t>& dest);

/***********************************************************************
String Conversions (direct)
***********************************************************************/

#if defined VCZH_WCHAR_UTF16
	U32String				wtou32	(const WString& source)		{ return ConvertStringDirect<wchar_t, char32_t, _utftoutf<wchar_t, char32_t>>(source); }
	WString					u32tow	(const U32String& source)	{ return ConvertStringDirect<char32_t, wchar_t, _utftoutf<char32_t, wchar_t>>(source); }
#elif defined VCZH_WCHAR_UTF32
	U32String				wtou32	(const WString& source)		{ return U32String::UnsafeCastFrom(source); }
	WString					u32tow	(const U32String& source)	{ return WString::UnsafeCastFrom(source); }
#endif
	U32String				u8tou32	(const U8String& source)	{ return ConvertStringDirect<char8_t, char32_t, _utftoutf<char8_t, char32_t>>(source); }
	U8String				u32tou8	(const U32String& source)	{ return ConvertStringDirect<char32_t, char8_t, _utftoutf<char32_t, char8_t>>(source); }
	U32String				u16tou32(const U16String& source)	{ return ConvertStringDirect<char16_t, char32_t, _utftoutf<char16_t, char32_t>>(source); }
	U16String				u32tou16(const U32String& source)	{ return ConvertStringDirect<char32_t, char16_t, _utftoutf<char32_t, char16_t>>(source); }

/***********************************************************************
String Conversions (unicode indirect)
***********************************************************************/

	AString					wtoa	(const WString& source)		{ return ConvertStringDirect<wchar_t, char, _wtoa>(source); }
	WString					atow	(const AString& source)		{ return ConvertStringDirect<char, wchar_t, _atow>(source); }

	U8String				wtou8	(const WString& source)		{ return ConvertStringDirect<wchar_t, char8_t, _utftoutf<wchar_t, char8_t>>(source); }
	WString					u8tow	(const U8String& source)	{ return ConvertStringDirect<char8_t, wchar_t, _utftoutf<char8_t, wchar_t>>(source); }
#if defined VCZH_WCHAR_UTF16
	U16String				wtou16	(const WString& source)		{ return U16String::UnsafeCastFrom(source); }
	WString					u16tow	(const U16String& source)	{ return WString::UnsafeCastFrom(source); }
#elif defined VCZH_WCHAR_UTF32
	U16String				wtou16	(const WString& source)		{ return ConvertStringDirect<wchar_t, char16_t, _utftoutf<wchar_t, char16_t>>(source); }
	WString					u16tow	(const U16String& source)	{ return ConvertStringDirect<char16_t, wchar_t, _utftoutf<char16_t, wchar_t>>(source); }
#endif
	U16String				u8tou16	(const U8String& source)	{ return ConvertStringDirect<char8_t, char16_t, _utftoutf<char8_t, char16_t>>(source); }
	U8String				u16tou8	(const U16String& source)	{ return ConvertStringDirect<char16_t, char8_t, _utftoutf<char16_t, char8_t>>(source); }
}


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

#include <time.h>

namespace vl
{
	WString LoremIpsum(vint bestLength, LoremIpsumCasing casing)
	{
		static const wchar_t* words[] =
		{
			L"lorem", L"ipsum", L"dolor", L"sit", L"amet", L"consectetur", L"adipiscing", L"elit", L"integer",
			L"nec", L"odio", L"praesent", L"libero", L"sed", L"cursus", L"ante", L"dapibus", L"diam",
			L"sed", L"nisi", L"nulla", L"quis", L"sem", L"at", L"nibh", L"elementum", L"imperdiet", L"duis",
			L"sagittis", L"ipsum", L"praesent", L"mauris", L"fusce", L"nec", L"tellus", L"sed", L"augue",
			L"semper", L"porta", L"mauris", L"massa", L"vestibulum", L"lacinia", L"arcu", L"eget", L"nulla",
			L"class", L"aptent", L"taciti", L"sociosqu", L"ad", L"litora", L"torquent", L"per", L"conubia",
			L"nostra", L"per", L"inceptos", L"himenaeos", L"curabitur", L"sodales", L"ligula", L"in",
			L"libero", L"sed", L"dignissim", L"lacinia", L"nunc", L"curabitur", L"tortor", L"pellentesque",
			L"nibh", L"aenean", L"quam", L"in", L"scelerisque", L"sem", L"at", L"dolor", L"maecenas",
			L"mattis", L"sed", L"convallis", L"tristique", L"sem", L"proin", L"ut", L"ligula", L"vel",
			L"nunc", L"egestas", L"porttitor", L"morbi", L"lectus", L"risus", L"iaculis", L"vel", L"suscipit",
			L"quis", L"luctus", L"non", L"massa", L"fusce", L"ac", L"turpis", L"quis", L"ligula", L"lacinia",
			L"aliquet", L"mauris", L"ipsum", L"nulla", L"metus", L"metus", L"ullamcorper", L"vel", L"tincidunt",
			L"sed", L"euismod", L"in", L"nibh", L"quisque", L"volutpat", L"condimentum", L"velit", L"class",
			L"aptent", L"taciti", L"sociosqu", L"ad", L"litora", L"torquent", L"per", L"conubia", L"nostra",
			L"per", L"inceptos", L"himenaeos", L"nam", L"nec", L"ante", L"sed", L"lacinia", L"urna",
			L"non", L"tincidunt", L"mattis", L"tortor", L"neque", L"adipiscing", L"diam", L"a", L"cursus",
			L"ipsum", L"ante", L"quis", L"turpis", L"nulla", L"facilisi", L"ut", L"fringilla", L"suspendisse",
			L"potenti", L"nunc", L"feugiat", L"mi", L"a", L"tellus", L"consequat", L"imperdiet", L"vestibulum",
			L"sapien", L"proin", L"quam", L"etiam", L"ultrices", L"suspendisse", L"in", L"justo", L"eu",
			L"magna", L"luctus", L"suscipit", L"sed", L"lectus", L"integer", L"euismod", L"lacus", L"luctus",
			L"magna", L"quisque", L"cursus", L"metus", L"vitae", L"pharetra", L"auctor", L"sem", L"massa",
			L"mattis", L"sem", L"at", L"interdum", L"magna", L"augue", L"eget", L"diam", L"vestibulum",
			L"ante", L"ipsum", L"primis", L"in", L"faucibus", L"orci", L"luctus", L"et", L"ultrices",
			L"posuere", L"cubilia", L"curae;", L"morbi", L"lacinia", L"molestie", L"dui", L"praesent",
			L"blandit", L"dolor", L"sed", L"non", L"quam", L"in", L"vel", L"mi", L"sit", L"amet", L"augue",
			L"congue", L"elementum", L"morbi", L"in", L"ipsum", L"sit", L"amet", L"pede", L"facilisis",
			L"laoreet", L"donec", L"lacus", L"nunc", L"viverra", L"nec", L"blandit", L"vel", L"egestas",
			L"et", L"augue", L"vestibulum", L"tincidunt", L"malesuada", L"tellus", L"ut", L"ultrices",
			L"ultrices", L"enim", L"curabitur", L"sit", L"amet", L"mauris", L"morbi", L"in", L"dui",
			L"quis", L"est", L"pulvinar", L"ullamcorper", L"nulla", L"facilisi", L"integer", L"lacinia",
			L"sollicitudin", L"massa", L"cras", L"metus", L"sed", L"aliquet", L"risus", L"a", L"tortor",
			L"integer", L"id", L"quam", L"morbi", L"mi", L"quisque", L"nisl", L"felis", L"venenatis",
			L"tristique", L"dignissim", L"in", L"ultrices", L"sit", L"amet", L"augue", L"proin", L"sodales",
			L"libero", L"eget", L"ante", L"nulla", L"quam", L"aenean", L"laoreet", L"vestibulum", L"nisi",
			L"lectus", L"commodo", L"ac", L"facilisis", L"ac", L"ultricies", L"eu", L"pede", L"ut", L"orci",
			L"risus", L"accumsan", L"porttitor", L"cursus", L"quis", L"aliquet", L"eget", L"justo",
			L"sed", L"pretium", L"blandit", L"orci", L"ut", L"eu", L"diam", L"at", L"pede", L"suscipit",
			L"sodales", L"aenean", L"lectus", L"elit", L"fermentum", L"non", L"convallis", L"id", L"sagittis",
			L"at", L"neque", L"nullam", L"mauris", L"orci", L"aliquet", L"et", L"iaculis", L"et", L"viverra",
			L"vitae", L"ligula", L"nulla", L"ut", L"felis", L"in", L"purus", L"aliquam", L"imperdiet",
			L"maecenas", L"aliquet", L"mollis", L"lectus", L"vivamus", L"consectetuer", L"risus", L"et",
			L"tortor"
		};
		static vint index = 0;
		const vint WordCount = sizeof(words) / sizeof(*words);

		if (bestLength < 0) bestLength = 0;
		vint bufferLength = bestLength + 20;
		wchar_t* buffer = new wchar_t[bufferLength + 1];

		buffer[0] = 0;
		vint used = 0;
		wchar_t* writing = buffer;
		while (used < bestLength)
		{
			if (used != 0)
			{
				*writing++ = L' ';
				used++;
			}

			vint wordSize = (vint)wcslen(words[index]);
			wcscpy_s(writing, bufferLength - used, words[index]);
			if (casing == LoremIpsumCasing::AllWordsUpperCase || (casing == LoremIpsumCasing::FirstWordUpperCase && used == 0))
			{
				*writing -= L'a' - L'A';
			}

			if (used != 0 && used + wordSize > bestLength)
			{
				vint deltaShort = bestLength - used + 1;
				vint deltaLong = used + wordSize - bestLength;
				if (deltaShort < deltaLong)
				{
					*--writing = 0;
					used--;
					break;
				}
			}
			writing += wordSize;
			used += wordSize;
			index = (index + 1) % WordCount;
		}

		WString result = buffer;
		delete[] buffer;
		return result;
	}

	WString LoremIpsumTitle(vint bestLength)
	{
		return LoremIpsum(bestLength, LoremIpsumCasing::AllWordsUpperCase);
	}

	WString LoremIpsumSentence(vint bestLength)
	{
		return LoremIpsum(bestLength, LoremIpsumCasing::FirstWordUpperCase) + L".";
	}

	WString LoremIpsumParagraph(vint bestLength)
	{
		srand((unsigned)time(0));
		auto casing = LoremIpsumCasing::FirstWordUpperCase;
		vint comma = 0;
		WString result;
		while (result.Length() < bestLength)
		{
			vint offset = bestLength - result.Length();
			if (comma == 0)
			{
				comma = rand() % 4 + 1;
			}
			vint length = rand() % 45 + 15;
			if (offset < 20)
			{
				comma = 0;
				length = offset - 1;
			}
			else if (length > offset)
			{
				comma = 0;
				length = offset + rand() % 11 - 5;
			}

			result += LoremIpsum(length, casing);
			if (comma == 0)
			{
				result += L".";
				break;
			}
			else if (comma == 1)
			{
				result += L". ";
				casing = LoremIpsumCasing::FirstWordUpperCase;
			}
			else
			{
				result += L", ";
				casing = LoremIpsumCasing::AllWordsLowerCase;
			}
			comma--;
		}
		return result;
	}
}


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

#if defined VCZH_MSVC
#elif defined VCZH_GCC
#define _strtoi64 strtoll
#define _strtoui64 strtoull
#define _wcstoi64 wcstoll
#define _wcstoui64 wcstoull
#endif

namespace vl
{
	template class ObjectString<char>;
	template class ObjectString<wchar_t>;
	template class ObjectString<char8_t>;
	template class ObjectString<char16_t>;
	template class ObjectString<char32_t>;

#if defined VCZH_GCC
	void _itoa_s(vint32_t value, char* buffer, size_t size, vint radix)
	{
		snprintf(buffer, size, "%d", value);
	}

	void _itow_s(vint32_t value, wchar_t* buffer, size_t size, vint radix)
	{
		swprintf(buffer, size - 1, L"%d", value);
	}

	void _i64toa_s(vint64_t value, char* buffer, size_t size, vint radix)
	{
		snprintf(buffer, size, "%lld", static_cast<long long>(value));
	}

	void _i64tow_s(vint64_t value, wchar_t* buffer, size_t size, vint radix)
	{
		swprintf(buffer, size - 1, L"%lld", static_cast<long long>(value));
	}

	void _uitoa_s(vuint32_t value, char* buffer, size_t size, vint radix)
	{
		snprintf(buffer, size, "%u", value);
	}

	void _uitow_s(vuint32_t value, wchar_t* buffer, size_t size, vint radix)
	{
		swprintf(buffer, size - 1, L"%u", value);
	}

	void _ui64toa_s(vuint64_t value, char* buffer, size_t size, vint radix)
	{
		snprintf(buffer, size, "%llu", static_cast<unsigned long long>(value));
	}

	void _ui64tow_s(vuint64_t value, wchar_t* buffer, size_t size, vint radix)
	{
		swprintf(buffer, size - 1, L"%llu", static_cast<unsigned long long>(value));
	}

	void _gcvt_s(char* buffer, size_t size, double value, vint numberOfDigits)
	{
		snprintf(buffer, size, "%f", value);
		char* point = strchr(buffer, '.');
		if(!point) return;
		char* zero = buffer + strlen(buffer);
		while(zero[-1] == '0')
		{
			*--zero = '\0';
		}
		if(zero[-1] == '.') *--zero = '\0';
	}

	void _strlwr_s(char* buffer, size_t size)
	{
		while(*buffer)
		{
			*buffer=(char)tolower(*buffer);
			buffer++;
		}
	}

	void _strupr_s(char* buffer, size_t size)
	{
		while(*buffer)
		{
			*buffer=(char)toupper(*buffer);
			buffer++;
		}
	}

	void _wcslwr_s(wchar_t* buffer, size_t size)
	{
		while(*buffer)
		{
			*buffer=(char)towlower(*buffer);
			buffer++;
		}
	}

	void _wcsupr_s(wchar_t* buffer, size_t size)
	{
		while(*buffer)
		{
			*buffer=(char)towupper(*buffer);
			buffer++;
		}
	}

	void wcscpy_s(wchar_t* buffer, size_t size, const wchar_t* text)
	{
		wcscpy(buffer, text);
	}
#endif

	vint atoi_test(const AString& string, bool& success)
	{
		char* endptr = 0;
		vint result = strtol(string.Buffer(), &endptr, 10);
		success = endptr == string.Buffer() + string.Length() && itoa(result) == string;
		if (success) success &= (_I32_MIN <= result && result <= _I32_MAX);
		return result;
	}

	vint wtoi_test(const WString& string, bool& success)
	{
		wchar_t* endptr = 0;
		vint result = wcstol(string.Buffer(), &endptr, 10);
		success = endptr == string.Buffer() + string.Length() && itow(result) == string;
		if (success) success &= (_I32_MIN <= result && result <= _I32_MAX);
		return result;
	}

	vint64_t atoi64_test(const AString& string, bool& success)
	{
		char* endptr = 0;
		vint64_t result = _strtoi64(string.Buffer(), &endptr, 10);
		success = endptr == string.Buffer() + string.Length() && i64toa(result) == string;
		return result;
	}

	vint64_t wtoi64_test(const WString& string, bool& success)
	{
		wchar_t* endptr = 0;
		vint64_t result = _wcstoi64(string.Buffer(), &endptr, 10);
		success = endptr == string.Buffer() + string.Length() && i64tow(result) == string;
		return result;
	}

	vuint atou_test(const AString& string, bool& success)
	{
		char* endptr = 0;
		vuint result = strtoul(string.Buffer(), &endptr, 10);
		success = endptr == string.Buffer() + string.Length() && utoa(result) == string;
		if (success) success &= (result <= _UI32_MAX);
		return result;
	}

	vuint wtou_test(const WString& string, bool& success)
	{
		wchar_t* endptr = 0;
		vuint result = wcstoul(string.Buffer(), &endptr, 10);
		success = endptr == string.Buffer() + string.Length() && utow(result) == string;
		if (success) success &= (result <= _UI32_MAX);
		return result;
	}

	vuint64_t atou64_test(const AString& string, bool& success)
	{
		char* endptr = 0;
		vuint64_t result = _strtoui64(string.Buffer(), &endptr, 10);
		success = endptr == string.Buffer() + string.Length() && u64toa(result) == string;
		return result;
	}

	vuint64_t wtou64_test(const WString& string, bool& success)
	{
		wchar_t* endptr = 0;
		vuint64_t result = _wcstoui64(string.Buffer(), &endptr, 10);
		success = endptr == string.Buffer() + string.Length() && u64tow(result) == string;
		return result;
	}

	double atof_test(const AString& string, bool& success)
	{
		char* endptr = 0;
		double result = strtod(string.Buffer(), &endptr);
		success = endptr == string.Buffer() + string.Length();
		return result;
	}

	double wtof_test(const WString& string, bool& success)
	{
		wchar_t* endptr = 0;
		double result = wcstod(string.Buffer(), &endptr);
		success = endptr == string.Buffer() + string.Length();
		return result;
	}

	vint atoi(const AString& string)
	{
		bool success = false;
		vint result = atoi_test(string, success);
		return success ? result : 0;
	}

	vint wtoi(const WString& string)
	{
		bool success = false;
		vint result = wtoi_test(string, success);
		return success ? result : 0;
	}

	vint64_t atoi64(const AString& string)
	{
		bool success = false;
		vint64_t result = atoi64_test(string, success);
		return success ? result : 0;
	}

	vint64_t wtoi64(const WString& string)
	{
		bool success = false;
		vint64_t result = wtoi64_test(string, success);
		return success ? result : 0;
	}

	vuint atou(const AString& string)
	{
		bool success = false;
		vuint result = atou_test(string, success);
		return success ? result : 0;
	}

	vuint wtou(const WString& string)
	{
		bool success = false;
		vuint result = wtou_test(string, success);
		return success ? result : 0;
	}

	vuint64_t atou64(const AString& string)
	{
		bool success = false;
		vuint64_t result = atou64_test(string, success);
		return success ? result : 0;
	}

	vuint64_t wtou64(const WString& string)
	{
		bool success = false;
		vuint64_t result = wtou64_test(string, success);
		return success ? result : 0;
	}

	double atof(const AString& string)
	{
		bool success = false;
		double result = atof_test(string, success);
		return success ? result : 0;
	}

	double wtof(const WString& string)
	{
		bool success = false;
		double result = wtof_test(string, success);
		return success ? result : 0;
	}

	AString itoa(vint number)
	{
		char buffer[100];
		ITOA_S(number, buffer, sizeof(buffer) / sizeof(*buffer), 10);
		return buffer;
	}

	WString itow(vint number)
	{
		wchar_t buffer[100];
		ITOW_S(number, buffer, sizeof(buffer) / sizeof(*buffer), 10);
		return buffer;
	}

	AString i64toa(vint64_t number)
	{
		char buffer[100];
		I64TOA_S(number, buffer, sizeof(buffer) / sizeof(*buffer), 10);
		return buffer;
	}

	WString i64tow(vint64_t number)
	{
		wchar_t buffer[100];
		I64TOW_S(number, buffer, sizeof(buffer) / sizeof(*buffer), 10);
		return buffer;
	}

	AString utoa(vuint number)
	{
		char buffer[100];
		UITOA_S(number, buffer, sizeof(buffer) / sizeof(*buffer), 10);
		return buffer;
	}

	WString utow(vuint number)
	{
		wchar_t buffer[100];
		UITOW_S(number, buffer, sizeof(buffer) / sizeof(*buffer), 10);
		return buffer;
	}

	AString u64toa(vuint64_t number)
	{
		char buffer[100];
		UI64TOA_S(number, buffer, sizeof(buffer) / sizeof(*buffer), 10);
		return buffer;
	}

	WString u64tow(vuint64_t number)
	{
		wchar_t buffer[100];
		UI64TOW_S(number, buffer, sizeof(buffer) / sizeof(*buffer), 10);
		return buffer;
	}

	AString ftoa(double number)
	{
		char buffer[320];
		_gcvt_s(buffer, 320, number, 30);
		vint len = (vint)strlen(buffer);
		if (buffer[len - 1] == '.')
		{
			buffer[len - 1] = '\0';
		}
		return buffer;
	}

	WString ftow(double number)
	{
		return atow(ftoa(number));
	}

	AString alower(const AString& string)
	{
		AString result = string.Buffer();
		_strlwr_s((char*)result.Buffer(), result.Length() + 1);
		return result;
	}

	WString wlower(const WString& string)
	{
		WString result = string.Buffer();
		_wcslwr_s((wchar_t*)result.Buffer(), result.Length() + 1);
		return result;
	}

	AString aupper(const AString& string)
	{
		AString result = string.Buffer();
		_strupr_s((char*)result.Buffer(), result.Length() + 1);
		return result;
	}

	WString wupper(const WString& string)
	{
		WString result = string.Buffer();
		_wcsupr_s((wchar_t*)result.Buffer(), result.Length() + 1);
		return result;
	}
}


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


#ifdef VCZH_MSVC
#include <io.h>
#endif

#ifdef VCZH_GCC
#define _wcsnicmp wcsncasecmp
#endif

namespace vl
{

/***********************************************************************
MatchWildcardNaive
***********************************************************************/

	static vint WildcardNextToken(const wchar_t*& wildcard)
	{
		wchar_t c = *wildcard;
		if (c == L'\0')
		{
			return -3;
		}
		else if (c == L'?')
		{
			wildcard++;
			return -2;
		}
		else if (c == L'*')
		{
			vint questionCount = 0;
			while (true)
			{
				if (c == L'?')
				{
					questionCount++;
				}
				else if (c != L'*')
				{
					break;
				}
				c = *++wildcard;
			}
			return questionCount;
		}
		else
		{
			while (c != L'\0' && c != L'?' && c != L'*')
			{
				c = *++wildcard;
			}
			return -1;
		}
	}

	bool MatchWildcardNaive(const wchar_t* wildcard, const wchar_t* text, bool caseSensitive)
	{
#define ERROR_MESSAGE_PREFIX L"vl::MatchWildcardNaive(const wchar_t*, const wchar_t*, bool)#"
		CHECK_ERROR(wildcard != nullptr, ERROR_MESSAGE_PREFIX L"wildcard cannot be nullptr");
		CHECK_ERROR(text != nullptr, ERROR_MESSAGE_PREFIX L"text cannot be nullptr");
		
		while (true)
		{
			const wchar_t* tokenStart = wildcard;
			vint tokenType = WildcardNextToken(wildcard);

			switch (tokenType)
			{
			case -3:
				return *text == L'\0';
			case -2:
				if (*text == L'\0') return false;
				text++;
				break;
			case -1:
				{
					vint literalLen = wildcard - tokenStart;
					vint cmpResult = caseSensitive
						? wcsncmp(tokenStart, text, literalLen)
						: _wcsnicmp(tokenStart, text, literalLen);
					if (cmpResult != 0) return false;
					text += literalLen;
				}
				break;
			default:
				{
					vint minSkip = tokenType;
					for (vint i = 0; i < minSkip; i++)
					{
						if (*text++ == L'\0') return false;
					}

					while (true)
					{
						if (MatchWildcardNaive(wildcard, text, caseSensitive))
						{
							return true;
						}
						if (*text++ == L'\0') return false;
					}
				}
			}
		}
#undef ERROR_MESSAGE_PREFIX
	}

	namespace unittest
	{
		using namespace vl::console;
		using namespace vl::collections;

/***********************************************************************
UnitTest
***********************************************************************/

		namespace execution_impl
		{
			UnitTestLink*					testHead = nullptr;
			UnitTestLink**					testTail = &testHead;

			enum class UnitTestContextKind
			{
				File,
				Category,
				Case,
			};
			
			struct UnitTestContext
			{
				UnitTestContext*			parent = nullptr;
				WString						indentation;
				UnitTestContextKind			kind = UnitTestContextKind::File;
				bool						failed = false;
			};

			UnitTestContext*				testContext = nullptr;
			vint							totalFiles = 0;
			vint							passedFiles = 0;
			vint							totalCases = 0;
			vint							passedCases = 0;
			UnitTest::FailureMode			failureMode = UnitTest::FailureMode::NotRunning;
			vint							debugOutputArgIndex = -1;
			constexpr const wchar_t*		DebugOutputPrefix = L"/DebugOutput:";
			constexpr vint					DebugOutputPrefixLength = 13;

			template<typename TMessage>
			void RecordFailure(TMessage errorMessage)
			{
				UnitTest::PrintMessage(errorMessage, UnitTest::MessageKind::Error);
				auto current = testContext;
				while (current)
				{
					current->failed = true;
					current = current->parent;
				}
			}

			template<typename TCallback>
			void SuppressCppFailure(TCallback&& callback)
			{
				try
				{
					callback();
				}
				catch (const UnitTestAssertError& e)
				{
					RecordFailure(e.message);
					if (failureMode == UnitTest::FailureMode::Copilot)
					{
						throw UnitTestJustCrashError{};
					}
				}
				catch (const UnitTestConfigError& e)
				{
					RecordFailure(e.message);
					if (failureMode == UnitTest::FailureMode::Copilot)
					{
						throw UnitTestJustCrashError{};
					}
				}
				catch (const Error& e)
				{
					RecordFailure(e.Description());
					if (failureMode == UnitTest::FailureMode::Copilot)
					{
						throw UnitTestJustCrashError{};
					}
				}
				catch (const Exception& e)
				{
					RecordFailure(e.Message());
					if (failureMode == UnitTest::FailureMode::Copilot)
					{
						throw UnitTestJustCrashError{};
					}
				}
				catch (const UnitTestJustCrashError&)
				{
					throw;
				}
				catch (...)
				{
					RecordFailure(L"Unknown exception occurred!");
					if (failureMode == UnitTest::FailureMode::Copilot)
					{
						throw UnitTestJustCrashError{};
					}
				}
			}

			template<typename TCallback>
			void SuppressCFailure(TCallback&& callback)
			{
#ifdef VCZH_MSVC
				__try
				{
					SuppressCppFailure(std::forward<TCallback&&>(callback));
				}
				__except (/*EXCEPTION_EXECUTE_HANDLER*/ 1)
				{
					RecordFailure(L"Runtime exception occurred!");
				}
#else
				SuppressCppFailure(callback);
#endif
			}

			template<typename TCallback>
			void ExecuteAndSuppressFailure(TCallback&& callback)
			{
				switch (failureMode)
				{
				case UnitTest::FailureMode::Release:
					SuppressCFailure(std::forward<TCallback&&>(callback));
					break;
				case UnitTest::FailureMode::Copilot:
					SuppressCppFailure(std::forward<TCallback&&>(callback));
					break;
				default:
					callback();
				}
			}
		}
		using namespace execution_impl;

		UnitTest::FailureMode UnitTest::GetFailureMode()
		{
			return execution_impl::failureMode;
		}

		void UnitTest::PrintMessage(const WString& string, MessageKind kind)
		{
			if (kind != MessageKind::Error && !testContext)
			{
				throw UnitTestConfigError(L"Cannot print message when unit test is not running.");
			}

			switch (kind)
			{
			case MessageKind::Error:
				Console::SetColor(true, false, false, true);
				break;
			case MessageKind::Info:
				Console::SetColor(true, true, true, true);
				break;
			case MessageKind::File:
				Console::SetColor(true, false, true, true);
				break;
			case MessageKind::Category:
				Console::SetColor(true, true, false, true);
				break;
			case MessageKind::Case:
				Console::SetColor(false, true, false, true);
				break;
			}
			Console::WriteLine((testContext ? testContext->indentation : L"") + string);
			Console::SetColor(true, true, true, false);
		}

		int UnitTest::PrintUsages()
		{
			PrintMessage(L"Usage: [/D | /R | /C] {/F:TestFile} [/DebugOutput:File]", MessageKind::Error);
			return 1;
		}

		int UnitTest::RunAndDisposeTests(const collections::Array<WString>& options)
		{
#ifdef VCZH_MSVC
			_set_abort_behavior(0, _WRITE_ABORT_MSG);
#ifdef VCZH_CHECK_MEMORY_LEAKS
			auto debugFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
			_CrtSetDbgFlag(debugFlag | _CRTDBG_ALLOC_MEM_DF);
#endif
#endif
			bool unrecognized = false;
			bool _D = false;
			bool _R = false;
			bool _C = false;
			List<AString> _Fs;
			debugOutputArgIndex = -1;

			for (vint i = 0; i < options.Count(); i++)
			{
				auto&& option = options[i];
				if (option == L"/D")
				{
					_D = true;
				}
				else if (option == L"/R")
				{
					_R = true;
				}
				else if (option == L"/C")
				{
					_C = true;
				}
				else if (option.Length() > 3 && option.Left(3) == L"/F:")
				{
					_Fs.Add(wtoa(option.Sub(3, option.Length() - 3)));
				}
				else if (option.Length() > DebugOutputPrefixLength && wcsncmp(option.Buffer(), DebugOutputPrefix, DebugOutputPrefixLength) == 0)
				{
					if (debugOutputArgIndex != -1)
					{
						unrecognized = true;
					}
					debugOutputArgIndex = i;
				}
				else
				{
					unrecognized = true;
				}
			}

			vint modeCount = 0;
			if (_D) modeCount++;
			if (_R) modeCount++;
			if (_C) modeCount++;
			if (unrecognized || modeCount > 1)
			{
				return PrintUsages();
			}

			if (_D)
			{
				failureMode = FailureMode::Debug;
			}
			else if (_R)
			{
				failureMode = FailureMode::Release;
			}
			else if (_C)
			{
				failureMode = FailureMode::Copilot;
			}
			else
			{
				failureMode = IsDebuggerAttached() ? FailureMode::Debug : FailureMode::Release;
			}

			{
				UnitTestContext context;
				testContext = &context;
				totalFiles = 0;
				passedFiles = 0;
				totalCases = 0;
				passedCases = 0;

				switch (failureMode)
				{
				case FailureMode::Debug:
					PrintMessage(L"Failures are not suppressed.", MessageKind::Info);
					break;
				case FailureMode::Release:
					PrintMessage(L"Failures are suppressed.", MessageKind::Info);
					break;
				case FailureMode::Copilot:
					PrintMessage(L"Failures cause immediate exit.", MessageKind::Info);
					break;
				default:;
				}

				auto current = testHead;
				while (current)
				{
					bool skipped = false;
					context.failed = false;
					if (_Fs.Count() > 0)
					{
						skipped = true;
						
						// Extract the filename from the full path
						const char* fullPath = current->fileName;
						const char* fileNameStart = fullPath;
						for (const char* p = fullPath; *p != '\0'; p++)
						{
							if (*p == '/' || *p == '\\')
							{
								fileNameStart = p + 1;
							}
						}
						auto currentFileName = atow(AString::Unmanaged(fileNameStart));
						
						// Check if any wildcard pattern matches the filename
						for (auto pattern : From(_Fs))
						{
							auto widePattern = atow(pattern);
							if (MatchWildcardNaive(widePattern.Buffer(), currentFileName.Buffer(), false))
							{
								skipped = false;
								break;
							}
						}
					}

					if (skipped)
					{
						PrintMessage(atow(AString::Unmanaged(current->fileName)) + WString::Unmanaged(L" [SKIPPED]"), MessageKind::File);
					}
					else
					{
						PrintMessage(atow(AString::Unmanaged(current->fileName)), MessageKind::File);
						context.indentation = L"    ";
						ExecuteAndSuppressFailure(current->testProc);
						if (!testContext->failed) passedFiles++;
						totalFiles++;
						context.indentation = L"";
					}
					current = current->next;
				}

				bool passed = totalFiles == passedFiles;
				auto messageKind = passed ? MessageKind::Case : MessageKind::Error;
				PrintMessage(L"Passed test files: " + itow(passedFiles) + L"/" + itow(totalFiles), messageKind);
				PrintMessage(L"Passed test cases: " + itow(passedCases) + L"/" + itow(totalCases), messageKind);
				testContext = nullptr;
				return passed ? 0 : 1;
			}
		}

		int UnitTest::RunAndDisposeTests(int argc, wchar_t* argv[])
		{
			if (argc < 2)
			{
				return RunAndDisposeTests({});
			}
			else
			{
				Array<WString> options(argc - 1);
				for (int i = 1; i < argc; i++)
				{
					options[i - 1] = WString::Unmanaged(argv[i]);
				}
				return RunAndDisposeTests(options);
			}
		}

		int UnitTest::RunAndDisposeTests(int argc, char* argv[])
		{
			if (argc < 2)
			{
				return RunAndDisposeTests({});
			}
			else
			{
				Array<WString> options(argc - 1);
				for (int i = 1; i < argc; i++)
				{
					options[i - 1] = atow(argv[i]);
				}
				return RunAndDisposeTests(options);
			}
		}

		void UnitTest::DumpMemoryLeak(int argc, wchar_t* argv[])
		{
#if defined VCZH_MSVC && defined VCZH_CHECK_MEMORY_LEAKS
			if (debugOutputArgIndex != -1 && debugOutputArgIndex + 1 < argc)
			{
				auto option = argv[debugOutputArgIndex + 1];
				auto fileName = option + DebugOutputPrefixLength;
				FILE* file = nullptr;
				if (_wfopen_s(&file, fileName, L"w") == 0 && file)
				{
					auto reportFile = (_HFILE)_get_osfhandle(_fileno(file));
					auto oldWarnMode = _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
					auto oldErrorMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
					auto oldAssertMode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
					auto oldWarnFile = _CrtSetReportFile(_CRT_WARN, reportFile);
					auto oldErrorFile = _CrtSetReportFile(_CRT_ERROR, reportFile);
					auto oldAssertFile = _CrtSetReportFile(_CRT_ASSERT, reportFile);
					_CrtDumpMemoryLeaks();
					_CrtSetReportFile(_CRT_ASSERT, oldAssertFile);
					_CrtSetReportFile(_CRT_ERROR, oldErrorFile);
					_CrtSetReportFile(_CRT_WARN, oldWarnFile);
					_CrtSetReportMode(_CRT_ASSERT, oldAssertMode);
					_CrtSetReportMode(_CRT_ERROR, oldErrorMode);
					_CrtSetReportMode(_CRT_WARN, oldWarnMode);
					fclose(file);
					return;
				}
			}
			_CrtDumpMemoryLeaks();
#endif
		}

		void UnitTest::DumpMemoryLeak(int argc, char* argv[])
		{
#if defined VCZH_MSVC && defined VCZH_CHECK_MEMORY_LEAKS
			if (debugOutputArgIndex != -1 && debugOutputArgIndex + 1 < argc)
			{
				auto option = argv[debugOutputArgIndex + 1];
				auto fileName = option + DebugOutputPrefixLength;
				FILE* file = nullptr;
				if (fopen_s(&file, fileName, "w") == 0 && file)
				{
					auto reportFile = (_HFILE)_get_osfhandle(_fileno(file));
					auto oldWarnMode = _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
					auto oldErrorMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
					auto oldAssertMode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
					auto oldWarnFile = _CrtSetReportFile(_CRT_WARN, reportFile);
					auto oldErrorFile = _CrtSetReportFile(_CRT_ERROR, reportFile);
					auto oldAssertFile = _CrtSetReportFile(_CRT_ASSERT, reportFile);
					_CrtDumpMemoryLeaks();
					_CrtSetReportFile(_CRT_ASSERT, oldAssertFile);
					_CrtSetReportFile(_CRT_ERROR, oldErrorFile);
					_CrtSetReportFile(_CRT_WARN, oldWarnFile);
					_CrtSetReportMode(_CRT_ASSERT, oldAssertMode);
					_CrtSetReportMode(_CRT_ERROR, oldErrorMode);
					_CrtSetReportMode(_CRT_WARN, oldWarnMode);
					fclose(file);
					return;
				}
			}
			_CrtDumpMemoryLeaks();
#endif
		}

		void UnitTest::RegisterTestFile(UnitTestLink* link)
		{
			*testTail = link;
			testTail = &link->next;
		}

		void UnitTest::RunCategoryOrCase(const WString& description, bool isCategory, Func<void()>&& callback)
		{
			if (!testContext) throw UnitTestConfigError(L"TEST_CATEGORY is not allowed to execute outside of TEST_FILE.");
			if (testContext->kind == UnitTestContextKind::Case) throw UnitTestConfigError(
				isCategory ?
				L"TEST_CATEGORY is not allowed to execute inside TEST_CASE" :
				L"TEST_CASE is not allowed to execute inside TEST_CASE");

			PrintMessage(description, (isCategory ? MessageKind::Category : MessageKind::Case));

			UnitTestContext context;
			context.parent = testContext;
			context.indentation = testContext->indentation + L"    ";
			context.kind = isCategory ? UnitTestContextKind::Category : UnitTestContextKind::Case;

			testContext = &context;
			ExecuteAndSuppressFailure(callback);
			if (!isCategory)
			{
				if (!testContext->failed) passedCases++;
				totalCases++;
			}
			testContext = context.parent;
		}

		void UnitTest::EnsureLegalToAssert()
		{
			if (!testContext) throw UnitTestConfigError(L"Assertion is not allowed to execute outside of TEST_CASE.");
			if (testContext->kind != UnitTestContextKind::Case) throw UnitTestConfigError(L"Assertion is not allowed to execute outside of TEST_CASE.");
		}
	}
}

