#include "Common.h"
#include "util/BumpAllocator.h"
#include "util/UTF8.h"
#include "util/String.h"
//#include "farts.z"
#include <string.h>
#include "platform/PlatformHelper.h"

#ifndef BF_SMALL
#define STB_SPRINTF_DECORATE(name) BF_stbsp_##name
#define STB_SPRINTF_IMPLEMENTATION
#include "third_party/stb/stb_sprintf.h"
#endif

extern "C"
{
#include "third_party/utf8proc/utf8proc.h"
}


#ifdef BF_PLATFORM_WINDOWS
#include <shellapi.h>
#include <direct.h>
#endif

#pragma warning(disable:4996)
#pragma comment(lib, "winmm.lib")

int gBFArgC;
char** gBFArgV;

#ifdef BUMPALLOC_ETW_TRACING
VSHeapTracker::CHeapTracker* Beefy::BumpAllocator::mVSHeapTracker = NULL;
#pragma comment(lib, "VSCustomNativeHeapEtwProvider.lib")
#endif

USING_NS_BF;

UTF16String Beefy::ToWString(const StringImpl& theString)
{
	UTF16String aString;
	aString.Reserve(theString.length() + 1);
	for (intptr i = 0; i < theString.length(); ++i)
		aString.Add((unsigned char)theString[i]);
	aString.Add(0);
	return aString;
}

// String Beefy::ToString(const UTF16String& theString)
// {
// 	String newString;
// 	newString.Reserve((intptr)theString.length());
// 	for (int i = 0; i < (int)theString.length(); ++i)
// 	{
// 		const unsigned int c = (unsigned int)theString[i];
// 		newString.Append(char(c & 0xFF));
// 	}
// 	return newString;
// }

String Beefy::ToUpper(const StringImpl& theString)
{
	String aString = theString;
	for (int i = 0; i < (int)aString.length(); ++i)
		aString[i] = toupper(aString[i]);
	return aString;
}

void Beefy::MakeUpper(StringImpl& theString)
{
	for (int i = 0; i < (int)theString.length(); ++i)
		theString[i] = toupper(theString[i]);
}

UTF16String Beefy::ToUpper(const UTF16String& theString)
{
	UTF16String aString = theString;
	for (int i = 0; i < (int)aString.length(); ++i)
		aString[i] = toupper(aString[i]);
	return aString;
}

UTF16String Beefy::ToLower(const UTF16String& theString)
{
	UTF16String aString = theString;
	for (int i = 0; i < (int)aString.length(); ++i)
		aString[i] = tolower(aString[i]);
	return aString;
}

String Beefy::ToLower(const StringImpl& theString)
{
	String aString = theString;
	for (int i = 0; i < (int)aString.length(); ++i)
		aString[i] = (char)tolower((int)(uint8)aString[i]);
	return aString;
}

// UTF16String Beefy::Trim(const UTF16String& theString)
// {
// 	int left = 0;
// 	while ((left < (int) theString.length() - 1) && (iswspace(theString[left])))
// 		left++;
//
// 	int right = (int)theString.length() - 1;
// 	while ((right >= left) && (iswspace(theString[right])))
// 		right--;
//
// 	if ((left == 0) && (right == theString.length() - 1))
// 		return theString;
//
// 	return theString.substr(left, right - left + 1);
// }

String Beefy::Trim(const StringImpl& theString)
{
	int left = 0;
	while ((left < (int) theString.length() - 1) && (iswspace(theString[left])))
		left++;

	int right = (int)theString.length() - 1;
	while ((right >= left) && (iswspace(theString[right])))
		right--;

	if ((left == 0) && (right == theString.length() - 1))
		return theString;

	return theString.Substring(left, right - left + 1);
}

bool Beefy::StrReplace(StringImpl& str, const StringImpl& from, const StringImpl& to)
{
	str.Replace(from, to);
	return true;
}

bool Beefy::StrStartsWith(const StringImpl& str, const StringImpl& subStr)
{
	if (subStr.length() < str.length())
		return false;

	return strncmp(str.c_str(), subStr.c_str(), subStr.length()) == 0;
}

bool Beefy::StrEndsWith(const StringImpl& str, const StringImpl& subStr)
{
	if (subStr.length() < str.length())
		return false;

	return strncmp(str.c_str() - (str.length() - subStr.length()), subStr.c_str(), subStr.length()) == 0;
}

#ifndef BF_SMALL
String Beefy::SlashString(const StringImpl& str, bool utf8decode, bool utf8encode, bool beefString)
{
	bool prevEndedInSlashedNum = false;
	String outStr;

	bool noNextHex = false;

	bool lastWasVisibleChar = false;
	for (int idx = 0; idx < (int)str.length(); idx++)
	{
		bool endingInSlashedNum = false;
		char c = str[idx];

		if (noNextHex)
		{
			if (((c >= '0') && (c <= '9')) ||
				((c >= 'a') && (c <= 'f')) ||
				((c >= 'A') && (c <= 'F')))
			{
				outStr += "\"\"";
			}
			noNextHex = false;
		}

		bool isVisibleChar = false;

		switch (c)
		{
		case '\0':
			outStr += "\\0";
			endingInSlashedNum = true;
			break;
		case '\a':
			outStr += "\\a";
			break;
		case '\b':
			outStr += "\\b";
			break;
		case '\t':
			outStr += "\\t";
			break;
		case '\n':
			outStr += "\\n";
			break;
		case '\v':
			outStr += "\\v";
			break;
		case '\f':
			outStr += "\\f";
			break;
		case '\r':
			outStr += "\\r";
			break;
		case '\"':
			outStr += "\\\"";
			break;
		case '\\':
			outStr += "\\\\";
			break;
		default:
		{
			if ((c >= '0') && (c <= '9'))
			{
				// Need to break string to allow proper evaluation of slashed string
				if (prevEndedInSlashedNum)
					outStr += "\" \"";
			}

			// May be UTF8 byte.  Make sure it's valid before we add it, otherwise write the bytes in direct "\x" style
			if (((uint8)c >= 0x80) && (utf8decode))
			{
				int seqLen = u8_seqlen((char*)&str[idx]);
				if (seqLen > 1)
				{
					if (idx + seqLen <= str.length())
					{
						uint32 c32 = u8_toucs((char*)&str[idx], (int)str.length() - idx);
						bool wantHex = false;

						// Combining mark without previous visible char?
						if ((((c32 >= L'\u0300') && (c32 <= L'\u036F')) || ((c32 >= L'\u1DC0') && (c32 <= L'\u1DFF'))) &&
							(!lastWasVisibleChar))
							wantHex = true;

						utf8proc_category_t cat = utf8proc_category(c32);
						switch (cat)
						{
						case UTF8PROC_CATEGORY_ZS:
						case UTF8PROC_CATEGORY_ZL:
						case UTF8PROC_CATEGORY_ZP:
						case UTF8PROC_CATEGORY_CC:
						case UTF8PROC_CATEGORY_CF:
						case UTF8PROC_CATEGORY_CO:
							wantHex = true;
							break;
						default:
							break;
						}

						if (wantHex)
						{
							if (beefString)
							{
								char tempStr[32];
								sprintf(tempStr, "\\u{%x}", c32);
								outStr += tempStr;
								idx += seqLen - 1;
								continue;
							}
							else
							{
								char tempStr[32];
								sprintf(tempStr, "\\u%04x", c32);
								outStr += tempStr;
								idx += seqLen - 1;
								noNextHex = true;
								continue;
							}
						}

						bool passed = true;
						for (int checkOfs = 0; checkOfs < seqLen - 1; checkOfs++)
						{
							char checkC = str[idx + checkOfs + 1];
							if ((checkC & 0xC0) != 0x80) // Has trailing signature (0x10xxxxxx)?
								passed = false;
						}

						if (passed)
						{
							outStr += c;
							for (int checkOfs = 0; checkOfs < seqLen - 1; checkOfs++)
							{
								char checkC = str[idx + checkOfs + 1];
								outStr += checkC;
							}
							idx += seqLen - 1;
							continue;
						}
					}
				}
			}

			if (((uint8)c >= 0x80) && (utf8encode))
			{
				outStr += (char)(0xC0 | (((uint8)c & 0xFF) >> 6));
				outStr += (char)(0x80 | ((uint8)c & 0x3F));
				isVisibleChar = true;
				continue;
			}

			if (((uint8)c >= 32) && ((uint8)c < 0x80))
			{
				outStr += c;
				isVisibleChar = true;
			}
			else
			{
				char tempStr[32];
				sprintf(tempStr, "\\x%02x", (uint8)c);
				outStr += tempStr;
				endingInSlashedNum = true;
				if (!beefString)
					noNextHex = true;
			}
		}
		break;
		}

		prevEndedInSlashedNum = endingInSlashedNum;
		lastWasVisibleChar = isVisibleChar;
	}
	return outStr;
}
#endif

UTF16String Beefy::UTF8Decode(const StringImpl& theString)
{
	UTF16String strOut;

	int strLen = 0;
	char* cPtr = (char*)theString.c_str();
	int lenLeft = (int)theString.length();
	while (lenLeft > 0)
	{
		int seqLen = 0;
		uint32 c32 = u8_toucs(cPtr, lenLeft, &seqLen);
		if ((c32 >= 0x10000) && (sizeof(wchar_t) == 2))
			strLen += 2;
		else
			strLen += 1;
		cPtr += seqLen;
		lenLeft -= seqLen;
	}

	strOut.ResizeRaw(strLen + 1);
	strOut[strLen] = 0;

	cPtr = (char*)theString.c_str();
	uint16* wcPtr = strOut.mVals;
	int wcLenLeft = (int)strOut.length() + 1;
	lenLeft = (int)theString.length();
	while (lenLeft > 0)
	{
		int seqLen = 0;
		uint32 c32 = u8_toucs(cPtr, lenLeft, &seqLen);
		if ((c32 >= 0x10000) && (sizeof(wchar_t) == 2))
		{
			*(wcPtr++) = (wchar_t)(((c32 - 0x10000) >> 10) + 0xD800);
			*(wcPtr++) = (wchar_t)(((c32 - 0x10000) & 0x3FF) + 0xDC00);
			wcLenLeft -= 2;
		}
		else
		{
			*(wcPtr++) = (wchar_t)c32;
			wcLenLeft -= 1;
		}
		cPtr += seqLen;
		lenLeft -= seqLen;
	}

	return strOut;
}

String Beefy::UTF8Encode(const uint16* theString, int length)
{
	if (length == 0)
		return String();

	String strOut;
	int utf8Len = 0;
	uint16 utf16hi = 0;
	for (int i = 0; i < length; i++)
	{
		uint16 c = theString[i];
		uint32 c32 = c;
		if ((c >= 0xD800) && (c < 0xDC00))
		{
			utf16hi = (uint16)c;
			continue;
		}
		else if ((c >= 0xDC00) && (c < 0xE000))
		{
			uint16 utf16lo = c;
			c32 = 0x10000 + ((uint32)(utf16hi - 0xD800) << 10) | (uint32)(utf16lo - 0xDC00);
		}

		utf8Len += u8_seqlen(c32);
	}
	strOut.Append('?', utf8Len);
	char* cPtr = (char*)&strOut[0];
	int lenLeft = (int)strOut.length() + 1;
	for (int i = 0; i < length; i++)
	{
		uint16 c = theString[i];
		uint32 c32 = c;
		if ((c >= 0xD800) && (c < 0xDC00))
		{
			utf16hi = (uint16)c;
			continue;
		}
		else if ((c >= 0xDC00) && (c < 0xE000))
		{
			uint16 utf16lo = c;
			c32 = 0x10000 + ((uint32)(utf16hi - 0xD800) << 10) | (uint32)(utf16lo - 0xDC00);
		}

		int len = u8_toutf8(cPtr, lenLeft, c32);
		cPtr += len;
		lenLeft -= len;
	}

	return strOut;
}

String Beefy::UTF8Encode(const UTF16String& theString)
{
	return UTF8Encode((uint16*)theString.c_str(), (int)theString.length());
}

UTF16String Beefy::UTF16Decode(const uint16* theString)
{
	if (sizeof(wchar_t) == 2)
	{
		UTF16String str;
		str.Set((wchar_t*)theString);
		return str;
	}
	else
	{
		UTF16String str;

		int len = 0;
		while (theString[len] != 0)
			len++;

		str.ResizeRaw(len + 1);
		str[len] = 0;
		for (int pos = 0; pos < len; pos++)
			str[pos] = (wchar_t)theString[pos];
		return str;
	}
}

static const char gHexChar[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
String Beefy::FileNameToURI(const StringImpl& fileName)
{
#ifdef _WIN32
	String out = "file:///";
#else
	String out = "file://";
#endif
	for (int i = 0; i < (int)fileName.length(); i++)
	{
		char c = out[i];

		bool isValid =
			((c >= '@' && c <= 'Z') ||
			 (c >= 'a' && c <= 'z') ||
			 (c >= '&' && c < 0x3b) ||
			 (c == '!') || (c == '$') || (c == '_') || (c == '=') || (c == '~'));

		if ((((unsigned char)c) >= 0x80) || (!isValid))
		{
			out += '%';
			out += gHexChar[((unsigned char)(c)) >> 4];
			out += gHexChar[((unsigned char)(c)) & 0xf];
		}
		else if (c == '\\')
			out += '/';
		else
			out += c;
	}
	return out;
}

static const uint8 cCharTo64b[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0,
	0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 63,
	0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int64 Beefy::DecodeULEB32(const char*& p)
{
	uint64 val = 0;
	unsigned shift = 0;
	uint8 charVal;
	do
	{
		charVal = cCharTo64b[(uint8)*(p++)];
		val += uint64(charVal & 0x1f) << shift;
		shift += 5;
	} while ((charVal & 0x20) != 0);
	return val;
}

static const char c64bToChar[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
	'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
	'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
	'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '_'};
void Beefy::EncodeULEB32(uint64 value, StringImpl& buffer)
{
	do
	{
		uint8 byteVal = value & 0x1f;
		value >>= 5;
		if (value != 0)
			byteVal |= 0x20; // Mark this byte to show that more bytes will follow
		buffer.Append(c64bToChar[byteVal]);
	}
	while (value != 0);
}

#ifndef BF_SMALL
void Beefy::ExactMinimalFloatToStr(float f, char* str)
{
	sprintf(str, "%1.9g", f);

	char* cPtrLast = str;
	while (true)
	{
		char c = cPtrLast[1];
		if ((c == 0) || (c == 'e'))
			break;
		cPtrLast++;
	}
	char* cStrEnd = cPtrLast + 1;
	int baseLen = (int)(cPtrLast - str) + 1;
	if (baseLen < 9)
		return; // Isn't "full" precision

	char removeChar;
	char lastChar = *cPtrLast;
	char secondToLast = cPtrLast[-1];
	if (secondToLast == '9')
	{
		removeChar = '9';
	}
	else if (secondToLast == '0')
	{
		removeChar = '0';
	}
	else
	{
		return; // Not a 'transitional' representation
	}

	do
	{
		*cPtrLast = 0;
		cPtrLast--;
	} while (*cPtrLast == removeChar);
	strcpy(cPtrLast + 1, cStrEnd);

	if (removeChar == '9')
		(*cPtrLast)++;

	// Verify that our pretty representation is equivalent
	float checkF = 0;
	sscanf(str, "%f", &checkF);
	if (f == checkF)
		return;

	// Rollback
	int endLen = (int)strlen(cPtrLast + 1);
	memmove(cStrEnd, cPtrLast + 1, endLen);

	if (removeChar == '9')
		(*cPtrLast)--;

	cPtrLast++;
	while (cPtrLast < cStrEnd - 1)
	{
		*cPtrLast = removeChar;
		cPtrLast++;
	}
	*cPtrLast = lastChar;
}

void Beefy::ExactMinimalDoubleToStr(double d, char* str)
{
	sprintf(str, "%1.17g", d);

	char* cPtrLast = str;
	while (true)
	{
		char c = cPtrLast[1];
		if ((c == 0) || (c == 'e'))
			break;
		cPtrLast++;
	}
	char* cStrEnd = cPtrLast + 1;
	int baseLen = (int)(cPtrLast - str) + 1;
	if (baseLen < 18)
		return; // Isn't "full" precision

	char removeChar;
	char lastChar = *cPtrLast;
	if ((lastChar == '9') || (lastChar == '8'))
	{
		removeChar = '9';
	}
	else if ((lastChar == '1') || (lastChar == '2'))
	{
		removeChar = '0';
	}
	else
	{
		return; // Not a 'transitional' representation
	}

	do
	{
		*cPtrLast = 0;
		cPtrLast--;
	}
	while (*cPtrLast == removeChar);
	strcpy(cPtrLast + 1, cStrEnd);

	if (removeChar == '9')
		(*cPtrLast)++;

	// Verify that our pretty representation is equivalent
	double checkD = 0;
	sscanf(str, "%lf", &checkD);
	if (d == checkD)
		return;

	// Rollback
	int endLen = (int)strlen(cPtrLast + 1);
	memmove(cStrEnd, cPtrLast + 1, endLen);

	if (removeChar == '9')
		(*cPtrLast)--;

	cPtrLast++;
	while (cPtrLast < cStrEnd - 1)
	{
		*cPtrLast = removeChar;
		cPtrLast++;
	}
	*cPtrLast = lastChar;
}
#endif

static char* StbspCallback(char *buf, void *user, int len)
{
    ((String*)user)->Append(buf, len);
    return buf;
}

//extern "C" int _vsnprintf(char* const _Buffer, size_t const _BufferCount, char const* const _Format, va_list _ArgList, int zz);

#ifdef BF_SMALL
String Beefy::vformat(const char* fmt, va_list argPtr)
{
	// We draw the line at a 1MB string.
	const int maxSize = 1000000;

	//va_list checkArgPtr = argPtr;
	//va_start(checkArgPtr, fmt);
	int argIdx = 0;
	char* newFmt = NULL;
	char tempBuff[2048];
	char* tempBuffPtr = tempBuff;
	for (int i = 0; fmt[i] != 0; i++)
	{
		if (fmt[i] == '%')
		{

			if (fmt[i + 1] == '%')
			{
				i++;
				continue;
			}

#ifdef BF32
			bool isLongAddr = false;
#else
			bool isLongAddr = true;
#endif
			if ((fmt[i + 1] == 'l') && (fmt[i + 2] == '@'))
			{
				isLongAddr = true;
				i++;
			}

			if (fmt[i + 1] == '@')
			{
				if (newFmt == NULL)
				{
					newFmt = (char*)malloc(strlen(fmt) + 1);
					strcpy(newFmt, fmt);
					//newFmt = strdup(fmt);
				}
				newFmt[i + 1] = 's';

				const char*& argValPtr = *(const char**)((intptr*)argPtr + argIdx);

				if (isLongAddr)
				{
					int64 iVal = *(int64*)((intptr*)argPtr + argIdx);
#ifdef BF32
					argIdx++; // Takes two spots
#endif
					argValPtr = tempBuffPtr;

					int leftVal = (int)(iVal >> 32);
					if (leftVal != 0)
					{
						sprintf(tempBuffPtr, "%x", leftVal);
						tempBuffPtr += strlen(tempBuffPtr);
					}
					tempBuffPtr[0] = '\'';
					tempBuffPtr++;
					sprintf(tempBuffPtr, "%08x", (int)iVal);
					tempBuffPtr += strlen(tempBuffPtr) + 1;
				}
				else
				{
					int32 iVal = (int32)(intptr)argValPtr;
					argValPtr = tempBuffPtr;
					sprintf(tempBuffPtr, "%08x", iVal);
					tempBuffPtr += strlen(tempBuffPtr) + 1;
				}

				if (newFmt[i] == 'l')
					newFmt[i] = '+';
			}
			else
			{
				//const char*& argValPtr = va_arg(checkArgPtr, const char*);
			}

			argIdx++;
		}
	}
	if (newFmt != NULL)
	{
		Beefy::String retVal = vformat(newFmt, argPtr);
		free(newFmt);
		return retVal;
	}

	// If the string is less than 161 characters,
	// allocate it on the stack because this saves
	// the malloc/free time.
	const int bufSize = 161;
	char stackBuffer[bufSize];

	int attemptedSize = bufSize - 1;

	int numChars = 0;

#ifdef _WIN32
	numChars = _vsnprintf(stackBuffer, attemptedSize, fmt, argPtr);
#else
	numChars = vsnprintf(stackBuffer, attemptedSize, fmt, argPtr);
#endif

	if ((numChars >= 0) && (numChars <= attemptedSize))
	{
		// Needed for case of 160-character printf thing
		stackBuffer[numChars] = '\0';

		// Got it on the first try.
		return String(stackBuffer);
	}

	// Now use the heap.
	char* heapBuffer = NULL;

	while (((numChars == -1) || (numChars > attemptedSize)) &&
		(attemptedSize < maxSize))
	{
		// Try a bigger size
		attemptedSize *= 2;
		heapBuffer = (char*)realloc(heapBuffer, (attemptedSize + 1));

#ifdef _WIN32
		numChars = _vsnprintf(heapBuffer, attemptedSize, fmt, argPtr);
#else
		numChars = vsnprintf(heapBuffer, attemptedSize, fmt, argPtr);
#endif

	}

	if (numChars == -1)
	{
		free(heapBuffer);
		return "";
	}

	heapBuffer[numChars] = 0;

	Beefy::String aResult = Beefy::String(heapBuffer);

	free(heapBuffer);
	return aResult;
}
#else
String Beefy::vformat(const char* fmt, va_list argPtr)
{
    String str;
    char buf[STB_SPRINTF_MIN];
    BF_stbsp_vsprintfcb(StbspCallback, (void*)&str, buf, fmt, argPtr);
    return str;
}
#endif

String Beefy::StrFormat(const char* fmt ...)
{
    va_list argList;
    va_start(argList, fmt);
	String aResult = vformat(fmt, argList);
    va_end(argList);

    return aResult;
}

String Beefy::IntPtrDynAddrFormat(intptr addr)
{
	if ((addr & 0xFFFFFFFF00000000LL) == 0)
		return StrFormat("%08X", addr);
	return StrFormat("%p", addr);
}

void Beefy::OutputDebugStr(const StringImpl& theString)
{
	const int maxLen = 0xFFFE;
	if (theString.length() > maxLen)
	{
		OutputDebugStr(theString.Substring(0, maxLen));
		OutputDebugStr(theString.Substring(maxLen));
		return;
	}
    BfpOutput_DebugString(theString.c_str());
}

void Beefy::OutputDebugStrF(const char* fmt ...)
{
	va_list argList;
	va_start(argList, fmt);
	String aResult = vformat(fmt, argList);
	va_end(argList);

    OutputDebugStr(aResult);
}

uint8* Beefy::LoadBinaryData(const StringImpl& path, int* size)
{
	FILE* fP = fopen(path.c_str(), "rb");
	if (fP == NULL)
		return NULL;

	fseek(fP, 0, SEEK_END);
	int aSize = (int32)ftell(fP);
	fseek(fP, 0, SEEK_SET);

	uint8* data = new uint8[aSize];
	int readSize = (int)fread(data, 1, aSize, fP);
	(void)readSize;
	fclose(fP);
	if (size)
		*size = aSize;
	return data;
}

char* Beefy::LoadTextData(const StringImpl& path, int* size)
{
	FILE* fP = fopen(path.c_str(), "rb");
	if (fP == NULL)
		return NULL;

	fseek(fP, 0, SEEK_END);
	int fileSize = (int32)ftell(fP);
	int strLen = fileSize;
	fseek(fP, 0, SEEK_SET);

	uint8 charHeader[3] = {0};
	int readSize = (int)fread(charHeader, 1, 3, fP);

	if ((charHeader[0] == 0xFF) && (charHeader[1] == 0xFE))
	{
		//UTF16 LE

		int dataLen = fileSize - 2;
		char* data = new char[dataLen + 2];
		data[0] = (char)charHeader[2];
		data[dataLen] = 0;
		data[dataLen + 1] = 0;
		int readSize = (int)fread(data + 1, 1, dataLen - 1, fP);
		(void)readSize;
		fclose(fP);

		// UTF16
		UTF16String str;
		str.Set((wchar_t*)data);
		delete [] data;

		String utf8Str = UTF8Encode(str);

		char* utf8Data = new char[utf8Str.length() + 1];
		strLen = (int)utf8Str.length();
		if (size != NULL)
			*size = strLen;
		memcpy(utf8Data, utf8Str.c_str(), strLen);
		return utf8Data;
	}
	else if ((charHeader[0] == 0xEF) && (charHeader[1] == 0xBB) && (charHeader[2] == 0xBF))
	{
		strLen = fileSize - 3;
		char* data = new char[strLen + 1];
		data[strLen] = 0;
		if (size != NULL)
			*size = strLen;
		int readSize = (int)fread(data, 1, strLen, fP);
		(void)readSize;
		fclose(fP);
		return data;
	}

	if (size != NULL)
		*size = strLen;
	char* data = new char[strLen + 1];
	data[strLen] = 0;
	for (int i = 0; i < BF_MIN(3, strLen); i++)
		data[i] = charHeader[i];
	if (strLen > 3)
	{
		int readSize = (int)fread(data + 3, 1, strLen - 3, fP);
		(void)readSize;
	}
	fclose(fP);
	return data;
}

bool Beefy::LoadTextData(const StringImpl& path, StringImpl& str)
{
	int size = 0;
	char* data = LoadTextData(path, &size);
	if (data == NULL)
		return false;
	if ((str.mAllocSizeAndFlags & StringImpl::DynAllocFlag) != 0)
		str.Release();
	str.mPtr = data;
	str.mAllocSizeAndFlags = size | StringImpl::DynAllocFlag | StringImpl::StrPtrFlag;
	str.mLength = size;
	return true;
}


#ifdef BF_MINGW
unsigned long long __cdecl _byteswap_uint64(unsigned long long _Int64)
{
#ifdef _WIN64
  unsigned long long retval;
  __asm__ __volatile__ ("bswapq %[retval]" : [retval] "=rm" (retval) : "[retval]" (_Int64));
  return retval;
#else
  union {
    long long int64part;
    struct {
      unsigned long lowpart;
      unsigned long hipart;
    };
  } retval;
  retval.int64part = _Int64;
  __asm__ __volatile__ ("bswapl %[lowpart]\n"
    "bswapl %[hipart]\n"
    : [lowpart] "=rm" (retval.hipart), [hipart] "=rm" (retval.lowpart)  : "[lowpart]" (retval.lowpart), "[hipart]" (retval.hipart));
  return retval.int64part;
#endif
}
#endif

#ifdef _WIN32
int64 Beefy::EndianSwap(int64 val)
{
	return _byteswap_uint64(val);
}
#endif

int32 Beefy::EndianSwap(int val)
{
	return ((val & 0x000000FF) << 24) | ((val & 0x0000FF00) <<  8) |
		((val & 0x00FF0000) >>  8) | ((val & 0xFF000000) >> 24);
}

int16 Beefy::EndianSwap(int16 val)
{
	return ((val & 0x00FF) << 8) | ((val & 0xFF00) >> 8);
}

int32 Beefy::Rand()
{
	return (rand() & 0xFFFF) | ((rand() & 0x7FFF) << 16);
}

int32 Beefy::GetHighestBitSet(int32 n)
{
	int i = 0;
	for (; n; n = (int)((uint32)n >> 1), i++)
		; /* empty */
	return i;
}

String Beefy::GetFileDir(const StringImpl& path)
{
	int slashPos = BF_MAX((int)path.LastIndexOf('\\'), (int)path.LastIndexOf('/'));
	if (slashPos == -1)
		return "";
	return path.Substring(0, slashPos);
}

String Beefy::GetFileName(const StringImpl& path)
{
	int slashPos = BF_MAX((int)path.LastIndexOf('\\'), (int)path.LastIndexOf('/'));
	if (slashPos == -1)
		return path;
	return path.Substring(slashPos + 1);
}

String Beefy::GetFileExtension(const StringImpl& path)
{
	int dotPos = (int)path.LastIndexOf('.');
	if (dotPos == -1)
		return path;
	return path.Substring(dotPos);
}

static String GetDriveStringTo(String path)
{
	if ((path.length() >= 2) && (path[1] == ':'))
		return String(path, 0, 2);
	return "";
}

String Beefy::GetRelativePath(const StringImpl& fullPath, const StringImpl& curDir)
{
	String curPath1 = String(curDir);
	String curPath2 = String(fullPath);

	for (int i = 0; i < (int)curPath1.length(); i++)
		if (curPath1[i] == DIR_SEP_CHAR_ALT)
			curPath1[i] = DIR_SEP_CHAR;

	for (int i = 0; i < (int)curPath2.length(); i++)
		if (curPath2[i] == DIR_SEP_CHAR_ALT)
			curPath2[i] = DIR_SEP_CHAR;

	String driveString1 = GetDriveStringTo(curPath1);
	String driveString2 = GetDriveStringTo(curPath2);

#ifdef _WIN32
	StringImpl::CompareKind compareType = StringImpl::CompareKind_OrdinalIgnoreCase;
#else
	StringImpl::CompareKind compareType = StringImpl::CompareKind_Ordinal;
#endif

	// On separate drives?
	if (!driveString1.Equals(driveString2, compareType))
	{
		return fullPath;
	}

	if (driveString1.mLength > 0)
		curPath1.Remove(0, BF_MIN(driveString1.mLength + 1, curPath1.mLength));
	if (driveString2.mLength > 0)
		curPath2.Remove(0, BF_MIN(driveString2.mLength + 1, curPath2.mLength));

	while ((curPath1.mLength > 0) && (curPath2.mLength > 0))
	{
		int slashPos1 = (int)curPath1.IndexOf(DIR_SEP_CHAR);
		if (slashPos1 == -1)
			slashPos1 = curPath1.mLength;
		int slashPos2 = (int)curPath2.IndexOf(DIR_SEP_CHAR);
		if (slashPos2 == -1)
			slashPos2 = curPath2.mLength;

		String section1;
		section1.Append(StringView(curPath1, 0, slashPos1));
		String section2;
		section2.Append(StringView(curPath2, 0, slashPos2));

		if (!section1.Equals(section2, compareType))
		{
			// a/b/c
			// d/e/f

			while (curPath1.mLength > 0)
			{
				slashPos1 = (int)curPath1.IndexOf(DIR_SEP_CHAR);
				if (slashPos1 == -1)
					slashPos1 = curPath1.mLength;

				if (slashPos1 + 1 >= curPath1.mLength)
					curPath1.Clear();
				else
					curPath1.Remove(0, slashPos1 + 1);
				if (DIR_SEP_CHAR == '\\')
					curPath2.Insert(0, "..\\");
				else
					curPath2.Insert(0, "../");
			}
		}
		else
		{
			if (slashPos1 + 1 >= curPath1.mLength)
				curPath1.Clear();
			else
				curPath1.Remove(0, slashPos1 + 1);

			if (slashPos2 + 2 >= curPath2.mLength)
				curPath1 = "";
			else
				curPath2.Remove(0, slashPos2 + 1);
		}
	}

	return curPath2;
}

String Beefy::GetAbsPath(const StringImpl& relPathIn, const StringImpl& dir)
{
	String relPath = relPathIn;

	String driveString = "";
	String newPath;
	newPath = dir;
	for (int i = 0; i < (int)newPath.length(); i++)
		if (newPath[i] == DIR_SEP_CHAR_ALT)
			newPath[i] = DIR_SEP_CHAR;

	for (int i = 0; i < (int)relPath.length(); i++)
		if (relPath[i] == DIR_SEP_CHAR_ALT)
			relPath[i] = DIR_SEP_CHAR;

	if ((relPath.length() >= 2) && (relPath[1] == ':'))
		return relPath;

	char slashChar = DIR_SEP_CHAR;

	if ((newPath.length() >= 2) && (newPath[1] == ':'))
	{
		driveString = newPath.Substring(0, 2);
		newPath = newPath.Substring(2);
	}

	// Append a trailing slash if necessary
	if ((newPath.length() > 0) && (newPath[newPath.length() - 1] != '\\') && (newPath[newPath.length() - 1] != '/'))
		newPath += slashChar;

	int relIdx = 0;

	for (;;)
	{
		if (newPath.length() == 0)
			break;

		int firstSlash = -1;
		for (int32 i = relIdx; i < (int)relPath.length(); i++)
			if ((relPath[i] == '\\') || (relPath[i] == '/'))
			{
				firstSlash = i;
				break;
			}
		if (firstSlash == -1)
			break;

		String chDir = relPath.Substring(relIdx, firstSlash - relIdx);

		relIdx = firstSlash + 1;

		if (chDir == "..")
		{
			int lastDirStart = (int)newPath.length() - 1;
			while ((lastDirStart > 0) && (newPath[lastDirStart - 1] != '\\') && (newPath[lastDirStart - 1] != '/'))
				lastDirStart--;

			String lastDir = newPath.Substring(lastDirStart, newPath.length() - lastDirStart - 1);
			if (lastDir == "..")
			{
				newPath += "..";
				newPath += DIR_SEP_CHAR;
			}
			else
			{
				newPath.RemoveToEnd(lastDirStart);
			}
		}
		else if (chDir == "")
		{
			newPath = DIR_SEP_CHAR;
			break;
		}
		else if (chDir != ".")
		{
			newPath += chDir + slashChar;
			break;
		}
	}

	//newPath = driveString + newPath + tempRelPath;
	newPath = driveString + newPath;
	newPath += relPath.Substring(relIdx);

	return newPath;
}

String Beefy::FixPath(const StringImpl& pathIn)
{
	String path = pathIn;

	for (int i = 0; i < (int)path.length(); i++)
	{
		if (path[i] == DIR_SEP_CHAR_ALT)
			path[i] = DIR_SEP_CHAR;
		if ((i > 0) && (path[i - 1] == '.') && (path[i] == '.'))
		{
			for (int checkIdx = i - 3; checkIdx >= 0; checkIdx--)
			{
				if ((path[checkIdx] == '\\') || (path[checkIdx] == '/'))
				{
					path = path.Substring(0, checkIdx) + path.Substring(i + 1);
					i = checkIdx;
					break;
				}
			}
		}
	}

	return path;
}

String Beefy::FixPathAndCase(const StringImpl& pathIn)
{
	if ((!pathIn.IsEmpty()) && (pathIn[0] == '$'))
		return pathIn;

	String path = FixPath(pathIn);
#ifdef _WIN32
	for (int i = 0; i < (int)path.length(); ++i)
		path[i] = toupper(path[i]);
#endif
	return path;
}

String Beefy::EnsureEndsInSlash(const StringImpl& dir)
{
	if ((dir[dir.length() - 1] != '/') && (dir[dir.length() - 1] != '\\'))
		return dir + "/";
	return dir;
}

String Beefy::RemoveTrailingSlash(const StringImpl& str)
{
	if (str.length() != 0)
	{
		char c = str[str.length() - 1];
		if ((c == '\\') || (c == '/'))
			return str.Substring(0, str.length() - 1);
	}
	return str;
}

bool Beefy::FileNameEquals(const StringImpl& filePathA, const StringImpl& filePathB)
{
#ifdef _WIN32
	if (filePathA.length() != filePathB.length())
		return false;

	const char* aPtr = filePathA.c_str();
	const char* bPtr = filePathB.c_str();
	while (true)
	{
		char a = *(aPtr++);
		char b = *(bPtr++);

		if (a == 0)
			return true;
		if (a == b)
			continue;
		if (a == '/')
			a = '\\';
		if (b == '/')
			b = '\\';
		if (a == b)
			continue;
		if (::toupper(a) == ::toupper(b))
			continue;
		return false;
	}
#else
	return strcmp(filePathA.c_str(), filePathB.c_str()) == 0;
#endif
}

bool Beefy::RecursiveCreateDirectory(const StringImpl& dirName)
{
	int slashPos = -1;
	for (int i = (int)dirName.length() - 1; i >= 0; i--)
	{
		char c = dirName[i];
		if ((c == '\\') || (c == '/'))
		{
			slashPos = i;
			break;
		}
	}

	if (slashPos != -1)
	{
		RecursiveCreateDirectory(dirName.Substring(0, slashPos));
	}

	BfpFileResult result;
    BfpDirectory_Create(dirName.c_str(), &result);
	return result == BfpFileResult_Ok;
}

bool Beefy::RecursiveDeleteDirectory(const StringImpl& dirPath)
{
	String findSpec = dirPath + "/*.*";
	bool failed = false;

	BfpFileResult result;
	BfpFindFileData* findFileData = BfpFindFileData_FindFirstFile(findSpec.c_str(), (BfpFindFileFlags)(BfpFindFileFlag_Directories | BfpFindFileFlag_Files), &result);
	if (result == BfpFileResult_Ok)
	{
		while (true)
		{
			Beefy::String fileName;
			BFP_GETSTR_HELPER(fileName, result, BfpFindFileData_GetFileName(findFileData, __STR, __STRLEN, &result));

			String filePath = dirPath + "/" + fileName;
			if ((BfpFindFileData_GetFileAttributes(findFileData) & BfpFileAttribute_Directory) != 0)
			{
				if (!RecursiveDeleteDirectory(filePath))
					failed = true;
			}
			else
			{
				BfpFile_Delete(filePath.c_str(), &result);
				if (result != BfpFileResult_Ok)
					failed = true;
			}

			if (!BfpFindFileData_FindNextFile(findFileData))
				break;
		}
		BfpFindFileData_Release(findFileData);
	}
	else if (result != BfpFileResult_NoResults)
	{
		return false;
	}

	BfpDirectory_Delete(dirPath.c_str(), &result);
	return (result == BfpFileResult_Ok) && (!failed);
}

void Beefy::BFFatalError(const char* message, const char* file, int line)
{
	BFFatalError(String(message), String(file), line);
}

bool Beefy::ParseMemorySpan(const StringImpl& str, void*& outPtr, int& outSize, StringImpl* outKey)
{
#ifndef BF_SMALL
	static int anonymousIdx = 0;

	if (str.StartsWith("@"))
	{
		int colon = (int)str.IndexOf(':');
		String addrStr = str.Substring(1, colon - 1);
		String lenStr = str.Substring(colon + 1);
		outPtr = (void*)(intptr)strtoll(addrStr.c_str(), NULL, 16);
		outSize = (int)strtol(lenStr.c_str(), NULL, 10);

		if (outKey != NULL)
		{
			int nextColon = (int)str.IndexOf(':', colon + 1);
			if (nextColon > 0)
			{
				*outKey = str.Substring(nextColon + 1);
			}
			else
			{
				int dotPos = (int)str.IndexOf('.', colon + 1);
				*outKey = StrFormat("ANON_%d", anonymousIdx++) + str.Substring(dotPos);
			}
		}

		return true;
	}
#endif
	return false;
}
