#include "stdafx.h"

#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "MemoryUtils.h"
#include "StringExt.h"

//------------------------------------------------------------------------

union StringExtFormatArg 
{
	int            iValue;
	unsigned int  uiValue;
	long           lValue;
	unsigned long ulValue;
	double         fValue;
	char           cValue;
	char          *sValue;
	StringExt    *seValue;
};

enum StringExtFormatType 
{
	fmtIntDecimal,
	fmtIntHex,
	fmtIntOctal,
	fmtIntBinary,
	fmtUIntDecimal,
	fmtUIntHex,
	fmtUIntOctal,
	fmtUIntBinary,
	fmtLongDecimal,
	fmtLongHex,
	fmtLongOctal,
	fmtLongBinary,
	fmtULongDecimal,
	fmtULongHex,
	fmtULongOctal,
	fmtULongBinary,
	fmtDouble,
	fmtDoubleTrim,
	fmtChar,
	fmtString,
	fmtStringExt,
	fmtSpace
};

static char *c_arrsFormatStrings[] = 
{
	 "d",  "x",  "o",  "b",  "ud",  "ux",  "uo",  "ub",
	"ld", "lx", "lo", "lb", "uld", "ulx", "ulo", "ulb",
     "f", "g",
     "c",
     "s",
     "t",
     "w",
      NULL
};

//------------------------------------------------------------------------

static inline int Size(int nLen) 
{
	int nDelta;
	for ( nDelta = 8; nDelta < nLen && nDelta < 0x100000; nDelta <<= 1 ) ;
	// ((nLen + 1) + (delta - 1)) & ~(delta - 1)
	return (nLen + nDelta) & ~( nDelta - 1);
}

inline void StringExt::Resize(int nLength) 
{
	char *sTemp = NULL;

	if ( !m_sData ) 
		m_sData = new char[Size(nLength)];
	else if ( Size(nLength) != Size(m_nLength) ) 
	{
		sTemp = new char[ Size(nLength) ];
		if ( nLength < m_nLength ) 
		{
			memcpy( sTemp, m_sData, nLength);
			sTemp[nLength] = '\0';
		} 
		else 
			memcpy( sTemp, m_sData, m_nLength + 1);
		delete[] m_sData;
		m_sData = sTemp;
	}
}

StringExt::StringExt() 
{
	m_sData = NULL;
	Resize( m_nLength = 0 );
	m_sData[0] = '\0';
}

StringExt::StringExt(const char *sSrc) 
{
	int nLen = strlen( sSrc );

	m_sData = NULL;
	Resize(m_nLength = nLen);
	memcpy(m_sData, sSrc, nLen + 1);
}

StringExt::StringExt(const char *sSrc, int nLength) 
{
	m_sData = NULL;
	Resize( m_nLength = nLength );
	memcpy( m_sData, sSrc, m_nLength * sizeof(char) );
	m_sData[m_nLength] = '\0';
}

StringExt::StringExt(StringExt *seSrc, int nIndex, int nLength) 
{
	m_sData = NULL;
	Resize( m_nLength = nLength );
	memcpy( m_sData, seSrc->GetBuffer() + nIndex, m_nLength);
	m_sData[ m_nLength ] = '\0';
}

StringExt::StringExt(StringExt *seSrc) 
{
	m_sData = NULL;
	Resize( m_nLength = seSrc->GetLength() );
	memcpy( m_sData, seSrc->GetBuffer(), m_nLength + 1);
}

StringExt::StringExt(StringExt *seString1, StringExt *seString2) 
{
	int nLen1 = seString1->GetLength();
	int nLen2 = seString2->GetLength();

	m_sData = NULL;
	Resize( m_nLength = nLen1 + nLen2 );
	memcpy( m_sData, seString1->GetBuffer(), nLen1 );
	memcpy( m_sData + nLen1, seString2->GetBuffer(), nLen2 + 1 );
}

StringExt *StringExt::FromInt(int nValue) 
{
	char sBuffer[24];
	char *pData = NULL;
	int   nLen;

	FormatInt( nValue, sBuffer, sizeof( sBuffer ), FALSE, 0, 10, &pData, &nLen);
	return new StringExt( pData, nLen );
}

StringExt *StringExt::Format(char *sFormat, ...) 
{
	va_list sArgList;
	StringExt *seResult = new StringExt();
	va_start( sArgList, sFormat );
	seResult->AppendFormatV( sFormat, sArgList);
	va_end( sArgList );
	return seResult;
}

StringExt *StringExt::FormatV(char *sFormat, va_list sArgList) 
{
	StringExt *seResult = new StringExt();
	seResult->AppendFormatV( sFormat, sArgList );
	return seResult;
}

StringExt::~StringExt() 
{
	delete[] m_sData;
}

StringExt *StringExt::Clear() 
{
	m_sData[ m_nLength = 0 ] = '\0';
	Resize(0);
	return this;
}

StringExt *StringExt::Append(char nChar) 
{
	Resize( m_nLength + 1 );
	m_sData[ m_nLength++ ] = nChar;
	m_sData[ m_nLength ] = '\0';
	return this;
}

StringExt *StringExt::Append(StringExt *seString) 
{
	int nLen = seString->GetLength();

	Resize( m_nLength + nLen );
	memcpy( m_sData + m_nLength, seString->GetBuffer(), nLen + 1 );
	m_nLength += nLen;
	return this;
}

StringExt *StringExt::Append(const char *sString) 
{
	int nLen = strlen( sString );

	Resize( m_nLength + nLen );
	memcpy( m_sData + m_nLength, sString, nLen + 1 );
	m_nLength += nLen;
	return this;
}

StringExt *StringExt::Append(const char *sString, int nLength) 
{
	Resize( m_nLength + nLength );
	memcpy( m_sData + m_nLength, sString, nLength );
	m_nLength += nLength;
	m_sData[ m_nLength ] = '\0';
	return this;
}

StringExt *StringExt::AppendFormat(char *sFormat, ...) 
{
	va_list sArgList;
	va_start( sArgList, sFormat );
	AppendFormatV( sFormat, sArgList );
	va_end( sArgList );
	return this;
}

StringExt *StringExt::AppendFormatV(char *sFormat, va_list sArgList) 
{
	StringExtFormatArg uArg;
	int nIndex, nWidth, nPrecision;
	BOOL bReverseAlign, bZeroFill;
	StringExtFormatType eFormatType;
	char sBuffer[65];
	int nLen;
	char *pCur, *pTemp, *sTemp;

	int nArgsLen = 0;
	int nArgsSize = 8;
	StringExtFormatArg *arrArgs = (StringExtFormatArg *)MemUtilsMallocArray( nArgsSize, sizeof(StringExtFormatArg));

	pCur = sFormat;
	while ( *pCur ) 
	{
		if ( *pCur == '{' ) 
		{
			++pCur;
			if ( *pCur == '{' ) 
			{
				++pCur;
				Append('{');
			} 
			else 
			{
				// Разбираем форматированную строку
				if ( !(*pCur >= '0' && *pCur <= '9') ) 
					break;
				nIndex = *pCur - '0';
				for (++pCur; *pCur >= '0' && *pCur <= '9'; ++pCur ) 
					nIndex = 10 * nIndex + (*pCur - '0');
				
				if ( *pCur != ':' ) 
					break;
				++pCur;
				if ( *pCur == '-' ) 
				{
					bReverseAlign = TRUE;
					++pCur;
				} 
				else 
					bReverseAlign = FALSE;
				nWidth = 0;
				bZeroFill = *pCur == '0';
				for (; *pCur >= '0' && *pCur <= '9'; ++pCur ) 
					nWidth = 10 * nWidth + (*pCur - '0');
				if ( *pCur == '.' ) 
				{
					++pCur;
					nPrecision = 0;
					for (; *pCur >= '0' && *pCur <= '9'; ++pCur ) 
					{
						nPrecision = 10 * nPrecision + (*pCur - '0');
					}	
				} 
				else 
				{
					nPrecision = 0;	
				}
				for ( eFormatType = (StringExtFormatType)0;
					c_arrsFormatStrings[ eFormatType ];	     
				eFormatType = (StringExtFormatType)( eFormatType + 1 ) ) 
				{
					if (!strncmp( pCur, c_arrsFormatStrings[ eFormatType ], strlen(c_arrsFormatStrings[ eFormatType ]))) 
					{
						break;	  
					}	
				}	
				if ( !c_arrsFormatStrings[ eFormatType ] ) 
				{	  
					break;	
				}	
				pCur += strlen( c_arrsFormatStrings[ eFormatType ] );
				if (*pCur != '}') 
				{
					break;
				}
				++pCur;	
				// fetch the argument
				if ( nIndex > nArgsLen ) 
				{
					break;
				}
				if ( nIndex == nArgsLen )
				{
					if ( nArgsLen == nArgsSize ) 
					{
						nArgsSize *= 2;
						arrArgs = (StringExtFormatArg *)MemUtilsReallocArray( arrArgs, nArgsSize, sizeof(StringExtFormatArg));
					}
					switch ( eFormatType ) 
					{
					case fmtIntDecimal:
					case fmtIntHex:
					case fmtIntOctal:
					case fmtIntBinary:
					case fmtSpace:
						arrArgs[nArgsLen].iValue = va_arg( sArgList, int );
						break;
					case fmtUIntDecimal:
					case fmtUIntHex:
					case fmtUIntOctal:
					case fmtUIntBinary:
						arrArgs[nArgsLen].uiValue = va_arg( sArgList, unsigned int );
						break;
					case fmtLongDecimal:
					case fmtLongHex:
					case fmtLongOctal:
					case fmtLongBinary:
						arrArgs[nArgsLen].lValue = va_arg( sArgList, long );
						break;
					case fmtULongDecimal:
					case fmtULongHex:
					case fmtULongOctal:
					case fmtULongBinary:
						arrArgs[nArgsLen].ulValue = va_arg( sArgList, unsigned long );
						break;
					case fmtDouble:
					case fmtDoubleTrim:
						arrArgs[nArgsLen].fValue = va_arg( sArgList, double );
						break;
					case fmtChar:
						arrArgs[nArgsLen].cValue = (char)va_arg( sArgList, int );
						break;
					case fmtString:
						arrArgs[nArgsLen].sValue = va_arg( sArgList, char * );
						break;
					case fmtStringExt:
						arrArgs[nArgsLen].seValue = va_arg(sArgList, StringExt *);
						break;
					}
					++nArgsLen;
				}

				uArg = arrArgs[ nIndex ];
				switch ( eFormatType ) 
				{
				case fmtIntDecimal:
					FormatInt( uArg.iValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 10, &sTemp, &nLen );
					break;
				case fmtIntHex:
					FormatInt( uArg.iValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 16, &sTemp, &nLen );
					break;
				case fmtIntOctal:
					FormatInt( uArg.iValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 8, &sTemp, &nLen );
					break;
				case fmtIntBinary:
					FormatInt( uArg.iValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 2, &sTemp, &nLen );
					break;
				case fmtUIntDecimal:
					FormatUInt( uArg.uiValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 10, &sTemp, &nLen );
					break;
				case fmtUIntHex:
					FormatUInt( uArg.uiValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 16, &sTemp, &nLen );
					break;
				case fmtUIntOctal:
					FormatUInt( uArg.uiValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 8, &sTemp, &nLen );
					break;
				case fmtUIntBinary:
					FormatUInt( uArg.uiValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 2, &sTemp, &nLen );
					break;
				case fmtLongDecimal:
					FormatInt( uArg.lValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 10, &sTemp, &nLen );
					break;
				case fmtLongHex:
					FormatInt( uArg.lValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 16, &sTemp, &nLen );
					break;
				case fmtLongOctal:
					FormatInt( uArg.lValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 8, &sTemp, &nLen );
					break;
				case fmtLongBinary:
					FormatInt( uArg.lValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 2, &sTemp, &nLen );
					break;
				case fmtULongDecimal:
					FormatUInt( uArg.ulValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 10, &sTemp, &nLen );
					break;
				case fmtULongHex:
					FormatUInt( uArg.ulValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 16, &sTemp, &nLen );
					break;
				case fmtULongOctal:
					FormatUInt( uArg.ulValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 8, &sTemp, &nLen );
					break;
				case fmtULongBinary:
					FormatUInt( uArg.ulValue, sBuffer, sizeof(sBuffer), bZeroFill, nWidth, 2, &sTemp, &nLen );
					break;
				case fmtDouble:
					FormatDouble( uArg.fValue, sBuffer, sizeof(sBuffer), nPrecision, FALSE, &sTemp, &nLen );
					break;
				case fmtDoubleTrim:
					FormatDouble( uArg.fValue, sBuffer, sizeof(sBuffer), nPrecision, TRUE, &sTemp, &nLen );
					break;
				case fmtChar:
					sBuffer[0] = uArg.cValue;
					sTemp = sBuffer;
					nLen = 1;
					bReverseAlign = !bReverseAlign;
					break;
				case fmtString:
					sTemp = uArg.sValue;
					nLen = strlen( sTemp );
					bReverseAlign = !bReverseAlign;
					break;
				case fmtStringExt:
					sTemp = uArg.seValue->GetBuffer();
					nLen = uArg.seValue->GetLength();
					bReverseAlign = !bReverseAlign;
					break;
				case fmtSpace:
					sTemp = sBuffer;
					nLen = 0;
					nWidth = uArg.iValue;
					break;
				}
				// Добавляем аргумент в нужном формате, с нужным прилеганием
				if ( !bReverseAlign && nLen < nWidth ) 
				{
					for (int nCounter = nLen; nCounter < nWidth; ++nCounter ) 
						Append(' ');
				}
				Append( sTemp, nLen);
				if ( bReverseAlign && nLen < nWidth ) 
				{
					for (int nCounter = nLen; nCounter < nWidth; ++nCounter ) 
						Append(' ');
				}
			}
		} 
		else if ( *pCur == '}' ) 
		{
			++pCur;
			if ( *pCur  == '}' ) 
				++pCur;
			Append('}');
		} 
		else 
		{
			for ( pTemp = pCur + 1; *pTemp && *pTemp != '{' && *pTemp != '}'; ++pTemp ) ;
			Append( pCur, pTemp - pCur );
			pCur = pTemp;
		}
	}
	MemUtilsFree( arrArgs );
	return this;
}

void StringExt::FormatInt (         long nValue, char *sBuffer, int nBufferSize, BOOL bZeroFill, int nWidth, int nBase, char **ppData, int *pnLen) 
{
	static char c_sValues[17] = "0123456789abcdef";
	BOOL bNegative = FALSE;
	int nStart;

	int nCur = nBufferSize;
	if ( ( bNegative = nValue < 0 ) ) 
	{
		nValue = -nValue;
	}
	nStart = bNegative ? 1 : 0;
	if ( nValue == 0 ) 
	{
		sBuffer[--nCur] = '0';
	} 
	else 
	{
		while ( nCur > nStart && nValue ) 
		{
			sBuffer[--nCur] = c_sValues[ nValue % nBase ];
			nValue /= nBase;
		}
	}
	if ( bZeroFill ) 
	{
		for (int nCounter = nBufferSize - nCur; nCur > nStart && nCounter < nWidth - nStart; ++nCounter ) 
		{
			sBuffer[--nCur] = '0';
		}
	}
	if ( bNegative ) 
	{
		sBuffer[--nCur] = '-';
	}
	*ppData = sBuffer + nCur;
	*pnLen = nBufferSize - nCur;
}

void StringExt::FormatUInt(unsigned long nValue, char *sBuffer, int nBufferSize, BOOL bZeroFill, int nWidth, int nBase, char **ppData, int *pnLen) 
{
	static char c_sValues[17] = "0123456789abcdef";

	int nCur = nBufferSize;
	if (nValue == 0) 
	{
		sBuffer[--nCur] = '0';
	} 
	else 
	{
		while (nCur > 0 && nValue) 
		{
			sBuffer[--nCur] = c_sValues[nValue % nBase];
			nValue /= nBase;
		}
	}
	if (bZeroFill) 
	{
		for (int nCounter = nBufferSize - nCur; nCur > 0 && nCounter < nWidth; ++nCounter ) 
		{
			sBuffer[--nCur] = '0';
		}
	}
	*ppData = sBuffer + nCur;
	*pnLen = nBufferSize - nCur;
}

void StringExt::FormatDouble(     double dValue, char *sBuffer, int nBufferSize, int nPrecision, BOOL bTrim,            char **ppData, int *pnLen) 
{
	BOOL bNegative = FALSE, bStarted = FALSE;
	double dTemp = 0;
	int nInt;

	if ( ( bNegative = dValue < 0 ) )
	    dValue = -dValue;
	
	dValue = floor( dValue * pow( (double)10, nPrecision) + 0.5);
	int nCur = nBufferSize;
	bStarted = !bTrim;
	for (int nCounter = 0; nCounter < nPrecision && nCur > 1; ++nCounter ) 
	{
		dTemp = floor(0.1 * (dValue + 0.5));
		nInt = (int)floor( dValue - 10 * dTemp + 0.5);
		if ( bStarted || nInt != 0 ) 
		{
			sBuffer[--nCur] = '0' + nInt;
			bStarted = TRUE;
		}
		dValue = dTemp;
	}
	if ( nCur > 1 && bStarted ) 
		sBuffer[--nCur] = '.';
	if ( nCur > 1 ) 
	{
		do {
			dTemp = floor( 0.1 * ( dValue + 0.5 ) );
			nInt = (int)floor(dValue - 10 * dTemp + 0.5);
			sBuffer[--nCur] = '0' + nInt;
			dValue = dTemp;
		} while ( nCur > 1 && dValue );
	}
	if (bNegative)
		sBuffer[--nCur] = '-';

	*ppData = sBuffer + nCur;
	*pnLen = nBufferSize - nCur;
}

StringExt *StringExt::Insert(int nIndex, char nChar) 
{
	Resize( m_nLength + 1 );
	for (int nJ = m_nLength + 1; nJ > nIndex; --nJ )
		m_sData[ nJ ] = m_sData[ nJ - 1 ];
	m_sData[ nIndex ] = nChar;
	++m_nLength;
	return this;
}

StringExt *StringExt::Insert(int nIndex, StringExt *seString) 
{
	int nLen = seString->GetLength();
	Resize( m_nLength + nLen );
	for (int nJ = m_nLength; nJ >= nIndex; --nJ )
		m_sData[ nJ + nLen ] = m_sData[ nJ ];
	memcpy( m_sData + nIndex, seString->GetBuffer(), nLen );
	m_nLength += nLen;
	return this;
}

StringExt *StringExt::Insert(int nIndex, const char *sString) 
{
	int nLen = strlen( sString );

	Resize(m_nLength + nLen);
	for (int nJ = m_nLength; nJ >= nIndex; --nJ )
		m_sData[ nJ + nLen ] = m_sData[ nJ ];
	memcpy( m_sData + nIndex, sString, nLen);
	m_nLength += nLen;
	return this;
}

StringExt *StringExt::Insert(int nIndex, const char *sString, int nLength) 
{
	Resize( m_nLength + nLength );
	for (int nJ = m_nLength; nJ >= nIndex; --nJ )
		m_sData[ nJ + nLength ] = m_sData[ nJ ];
	memcpy( m_sData + nIndex, sString, nLength );
	m_nLength += nLength;
	return this;
}

StringExt *StringExt::Delete(int nIndex, int nCount) 
{
	if ( nCount > 0 ) 
	{
		if ( nIndex + nCount > m_nLength) 
			nCount = m_nLength - nIndex;
		for (int nJ = nIndex; nJ <= m_nLength - nCount; ++nJ ) 
		{
			m_sData[ nJ ] = m_sData[ nJ + nCount ];
		}
		Resize( m_nLength -= nCount );
	}
	return this;
}

StringExt *StringExt::MakeUpper() 
{
	for ( int nIndex = 0; nIndex < m_nLength; ++nIndex ) 
	{
		if ( islower( m_sData[ nIndex ] ) )
			m_sData[nIndex] = toupper( m_sData[nIndex] );
	}
	return this;
}

StringExt *StringExt::MakeLower() 
{
	for (int nIndex = 0; nIndex < m_nLength; ++nIndex) 
	{
		if (isupper(m_sData[nIndex]))
			m_sData[nIndex] = tolower(m_sData[nIndex]);
	}
	return this;
}

int StringExt::Compare (StringExt *seOther) 
{
	int nIndex = 0;
	char *pThis, *pOther;

	int nThisLen = m_nLength;
	int nOtherLen = seOther->m_nLength;
	for (nIndex = 0, pThis = m_sData, pOther = seOther->m_sData; nIndex < nThisLen && nIndex < nOtherLen; ++nIndex, ++pThis, ++pOther) 
	{
		int nRes = *pThis - *pOther;
		if ( 0 != nRes ) 
			return nRes;
	}
	return nThisLen - nOtherLen;
}

int StringExt::CompareN(StringExt *seOther, int nCount) 
{
	int nIndex;
	char *pThis, *pOther;

	int nThisLen = m_nLength;
	int nOtherLen = seOther->m_nLength;
	for (nIndex = 0, pThis = m_sData, pOther = seOther->m_sData; nIndex < nThisLen && nIndex < nOtherLen && nIndex < nCount; ++nIndex, ++pThis, ++pOther) 
	{
		int nRes = *pThis - *pOther;
		if ( 0 != nRes ) 
			return nRes;
	}
	if (nIndex == nCount)
		return 0;
	return nThisLen - nOtherLen;
}

int StringExt::Compare (const char *sOther) 
{
	int nIndex;
	const char *pThis, *pOther;

	int nThisLen = m_nLength;
	for (nIndex = 0, pThis = m_sData, pOther = sOther; nIndex < nThisLen && *pOther; ++nIndex, ++pThis, ++pOther) 
	{
		int nRes = *pThis - *pOther;
		if ( 0 != nRes ) 
			return nRes;
	}
	if ( nIndex < nThisLen )
		return 1;
	if ( *pOther )
		return -1;
	return 0;
}

int StringExt::CompareN(const char *sOther, int nCount) 
{
	int nIndex;
	const char *pThis, *pOther;

	int nThisLen = m_nLength;
	for (nIndex = 0, pThis = m_sData, pOther = sOther; nIndex < nThisLen && *pOther && nIndex < nCount; ++nIndex, ++pThis, ++pOther ) 
	{
		int nRes = *pThis - *pOther;
		if ( 0 != nRes ) 
			return nRes;
	}
	if ( nIndex == nCount )
		return 0;
	if ( nIndex < nThisLen)
		return 1;
	if ( *pOther )
		return -1;
	return 0;
}
