#pragma once

#include <string.h>
#include <mbstring.h>
#include <wchar.h>

#ifdef __MINGW32__
#include <limits.h>

__inline size_t strnlen(const char *string, size_t maxlen)
{
	const char *end = (const char *)memchr ((const void *)string, '\0', maxlen);
	return end ? (size_t) (end - string) : maxlen;
}
__inline size_t wcsnlen(const wchar_t *string, size_t maxlen)
{
	const wchar_t *end = wmemchr (string, L'\0', maxlen);
	return end ? (size_t) (end - string) : maxlen;
}

/* FIXME: This may be wrong assumption about _AtlGetConversionACP */
#define _AtlGetConversionACP() CP_THREAD_ACP
/* FIXME: This is unsafe */
#define memcpy_s(dest,size,src,count) memcpy(dest,src,count)
/* FIXME: This is quite silly implementation of _mbsstr */
#define _mbsstr(str,search) strstr((const char *)str,(const char *)search)
#define __max(x,y) (((x)<(y))?(y):(x))
#endif /* __MINGW32__ */

struct CMStringData
{
	int nDataLength;  // Length of currently used data in XCHARs (not including terminating null)
	int nAllocLength;  // Length of allocated data in XCHARs (not including terminating null)
	long nRefs;     // Reference count: negative == locked
	// XCHAR data[nAllocLength+1]  // A CStringData is always followed in memory by the actual array of character data
	void* data();
	void AddRef();
	bool IsLocked() const;
	bool IsShared() const;
	void Lock();
	void Release();
	void Unlock();
};

class CNilMStringData : public CMStringData
{
public:
	CNilMStringData();

public:
	wchar_t achNil[2];
};

template< typename BaseType = char >
class ChTraitsBase
{
public:
	typedef char XCHAR;
	typedef LPSTR PXSTR;
	typedef LPCSTR PCXSTR;
	typedef wchar_t YCHAR;
	typedef LPWSTR PYSTR;
	typedef LPCWSTR PCYSTR;
};

template<>
class ChTraitsBase< wchar_t >
{
public:
	typedef wchar_t XCHAR;
	typedef LPWSTR PXSTR;
	typedef LPCWSTR PCXSTR;
	typedef char YCHAR;
	typedef LPSTR PYSTR;
	typedef LPCSTR PCYSTR;
};

class CMBaseString
{
public:
	static CMStringData* Allocate(int nChars, int nCharSize);
	static void Free(CMStringData* pData);
	static CMStringData* Realloc(CMStringData* pData, int nChars, int nCharSize);

protected:
	static CMStringData* GetNilString();
	static CNilMStringData m_nil;
};

template< typename BaseType >
class CMSimpleStringT : public CMBaseString
{
public:
	typedef typename ChTraitsBase< BaseType >::XCHAR XCHAR;
	typedef typename ChTraitsBase< BaseType >::PXSTR PXSTR;
	typedef typename ChTraitsBase< BaseType >::PCXSTR PCXSTR;
	typedef typename ChTraitsBase< BaseType >::YCHAR YCHAR;
	typedef typename ChTraitsBase< BaseType >::PYSTR PYSTR;
	typedef typename ChTraitsBase< BaseType >::PCYSTR PCYSTR;

public:
	explicit CMSimpleStringT()
	{
		CMStringData* pData = GetNilString();
		Attach(pData);
	}

	CMSimpleStringT(const CMSimpleStringT& strSrc)
	{
		CMStringData* pSrcData = strSrc.GetData();
		CMStringData* pNewData = CloneData( pSrcData );
		Attach( pNewData );
	}

	CMSimpleStringT(PCXSTR pszSrc)
	{
		int nLength = StringLength( pszSrc );
		CMStringData* pData = Allocate( nLength, sizeof( XCHAR ));
		if (pData != NULL)
		{
			Attach( pData );
			SetLength( nLength );
			CopyChars( m_pszData, nLength, pszSrc, nLength );
		}
	}
	CMSimpleStringT(const XCHAR* pchSrc, int nLength)
	{
		CMStringData* pData = Allocate( nLength, sizeof( XCHAR ));
		if ( pData != NULL )
		{
			Attach( pData );
			SetLength( nLength );
			CopyChars( m_pszData, nLength, pchSrc, nLength );
		}
	}
	~CMSimpleStringT()
	{
		CMStringData* pData = GetData();
		pData->Release();
	}

	operator CMSimpleStringT<BaseType>&()
	{
		return *(CMSimpleStringT<BaseType>*)this;
	}

	CMSimpleStringT& operator=(const CMSimpleStringT& strSrc )
	{
		CMStringData* pSrcData = strSrc.GetData();
		CMStringData* pOldData = GetData();
		if ( pSrcData != pOldData)
		{
			if ( pOldData->IsLocked())
				SetString( strSrc.GetString(), strSrc.GetLength());
			else
			{
				CMStringData* pNewData = CloneData( pSrcData );
				pOldData->Release();
				Attach( pNewData );
			}
		}

		return *this;
	}

	CMSimpleStringT& operator=(PCXSTR pszSrc)
	{
		SetString( pszSrc );
		return *this;
	}

	CMSimpleStringT& operator+=( const CMSimpleStringT& strSrc )
	{
		Append( strSrc );

		return *this;
	}

	CMSimpleStringT& operator+=( PCXSTR pszSrc )
	{
		Append( pszSrc );

		return *this;
	}
	CMSimpleStringT& operator+=( char ch )
	{
		AppendChar(XCHAR(ch));

		return *this;
	}
	CMSimpleStringT& operator+=( unsigned char ch )
	{
		AppendChar(XCHAR(ch));

		return *this;
	}
	CMSimpleStringT& operator+=( wchar_t ch )
	{
		AppendChar(XCHAR(ch));

		return *this;
	}

	XCHAR operator[]( int iChar ) const
	{
		return m_pszData[iChar];
	}

	operator PCXSTR() const
	{
		return m_pszData;
	}

	PCXSTR c_str() const
	{
		return m_pszData;
	}

	void Append( PCXSTR pszSrc )
	{
		Append( pszSrc, StringLength( pszSrc ));
	}
	void Append( PCXSTR pszSrc, int nLength )
	{
		// See comment in SetString() about why we do this
		UINT_PTR nOffset = pszSrc - GetString();

		UINT nOldLength = GetLength();
		if (nOldLength < 0)
		{
			// protects from underflow
			nOldLength = 0;
		}

		//Make sure we don't read pass end of the terminating NULL
		int nSrcLength = StringLength(pszSrc);
		nLength = nLength > nSrcLength ? nSrcLength: nLength;

		int nNewLength = nOldLength+nLength;
		PXSTR pszBuffer = GetBuffer( nNewLength );
		if ( nOffset <= nOldLength )
		{
			pszSrc = pszBuffer+nOffset;
			// No need to call CopyCharsOverlapped, since the destination is
			// beyond the end of the original buffer
		}
		CopyChars( pszBuffer+nOldLength, nLength, pszSrc, nLength );
		ReleaseBufferSetLength( nNewLength );
	}
	void AppendChar( XCHAR ch )
	{
		UINT nOldLength = GetLength();
		int nNewLength = nOldLength+1;
		PXSTR pszBuffer = GetBuffer( nNewLength );
		pszBuffer[nOldLength] = ch;
		ReleaseBufferSetLength( nNewLength );
	}
	void Append( const CMSimpleStringT& strSrc )
	{
		Append( strSrc.GetString(), strSrc.GetLength());
	}
	void Empty()
	{
		CMStringData* pOldData = GetData();
		if ( pOldData->nDataLength == 0 )
			return;

		if ( pOldData->IsLocked())
		{
			// Don't reallocate a locked buffer that's shrinking
			SetLength( 0 );
		}
		else
		{
			pOldData->Release();
			CMStringData* pNewData = GetNilString();
			Attach( pNewData );
		}
	}
	void FreeExtra()
	{
		CMStringData* pOldData = GetData();
		int nLength = pOldData->nDataLength;
		if ( pOldData->nAllocLength == nLength )
			return;

		if ( !pOldData->IsLocked())  // Don't reallocate a locked buffer that's shrinking
		{
			CMStringData* pNewData = Allocate( nLength, sizeof( XCHAR ));
			if ( pNewData == NULL ) {
				SetLength( nLength );
				return;
			}

			CopyChars( PXSTR( pNewData->data()), nLength, PCXSTR( pOldData->data()), nLength );

			pOldData->Release();
			Attach( pNewData );
			SetLength( nLength );
		}
	}

	int GetAllocLength() const
	{
		return GetData()->nAllocLength;
	}
	XCHAR GetAt( int iChar ) const
	{
		return m_pszData[iChar];
	}
	PXSTR GetBuffer()
	{
		CMStringData* pData = GetData();
		if ( pData->IsShared())
			Fork( pData->nDataLength );

		return m_pszData;
	}
	PXSTR GetBuffer( int nMinBufferLength )
	{
		return PrepareWrite( nMinBufferLength );
	}
	PXSTR GetBufferSetLength( int nLength )
	{
		PXSTR pszBuffer = GetBuffer( nLength );
		SetLength( nLength );

		return pszBuffer;
	}
	int GetLength() const
	{
		return GetData()->nDataLength;
	}

	PCXSTR GetString() const
	{
		return m_pszData;
	}
	bool IsEmpty() const
	{
		return GetLength() == 0;
	}
	PXSTR LockBuffer()
	{
		CMStringData* pData = GetData();
		if ( pData->IsShared())
		{
			Fork( pData->nDataLength );
			pData = GetData();  // Do it again, because the fork might have changed it
		}
		pData->Lock();

		return m_pszData;
	}
	void UnlockBuffer() 
	{
		CMStringData* pData = GetData();
		pData->Unlock();
	}
	void Preallocate( int nLength )
	{
		PrepareWrite( nLength );
	}
	void ReleaseBuffer( int nNewLength = -1 )
	{
		if ( nNewLength == -1 )
		{
			int nAlloc = GetData()->nAllocLength;
			nNewLength = StringLengthN( m_pszData, nAlloc);
		}
		SetLength( nNewLength );
	}
	void ReleaseBufferSetLength( int nNewLength )
	{
		SetLength( nNewLength );
	}
	void Truncate( int nNewLength )
	{
		GetBuffer( nNewLength );
		ReleaseBufferSetLength( nNewLength );
	}
	void SetAt( int iChar, XCHAR ch )
	{
		int nLength = GetLength();
		PXSTR pszBuffer = GetBuffer();
		pszBuffer[iChar] = ch;
		ReleaseBufferSetLength( nLength );

	}
	void SetString( PCXSTR pszSrc )
	{
		SetString( pszSrc, StringLength( pszSrc ));
	}
	void SetString( PCXSTR pszSrc, int nLength )
	{
		if ( nLength == 0 )
		{
			Empty();
		}
		else
		{

			UINT nOldLength = GetLength();
			UINT_PTR nOffset = pszSrc - GetString();

			PXSTR pszBuffer = GetBuffer( nLength );
			if ( nOffset <= nOldLength )
			{
				CopyCharsOverlapped( pszBuffer, GetAllocLength(), 
					pszBuffer+nOffset, nLength );
			}
			else
			{
				CopyChars( pszBuffer, GetAllocLength(), pszSrc, nLength );
			}
			ReleaseBufferSetLength( nLength );
		}
	}
public:
	friend CMSimpleStringT __stdcall operator+(const CMSimpleStringT& str1, const CMSimpleStringT& str2)
	{
		CMSimpleStringT s;

		Concatenate( s, str1, str1.GetLength(), str2, str2.GetLength());

		return s;
	}

	friend CMSimpleStringT __stdcall operator+(const CMSimpleStringT& str1, PCXSTR psz2)
	{
		CMSimpleStringT s;

		Concatenate( s, str1, str1.GetLength(), psz2, StringLength( psz2 ));

		return s;
	}

	friend CMSimpleStringT __stdcall operator+(PCXSTR psz1, const CMSimpleStringT& str2)
	{
		CMSimpleStringT s;

		Concatenate( s, psz1, StringLength( psz1 ), str2, str2.GetLength());

		return s;
	}

	static void __stdcall CopyChars(XCHAR* pchDest, const XCHAR* pchSrc, int nChars )
	{
#pragma warning (push)
#pragma warning(disable : 4996)
		memcpy(pchDest, pchSrc, nChars * sizeof(XCHAR));
#pragma warning (pop)
	}
	static void __stdcall CopyChars(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars )
	{
		#if _MSC_VER >= 1400
			memcpy_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR));
		#else
			memcpy(pchDest, pchSrc, nDestLen * sizeof(XCHAR));
		#endif
	}

	static void __stdcall CopyCharsOverlapped(XCHAR* pchDest, const XCHAR* pchSrc, int nChars )
	{
#pragma warning (push)
#pragma warning(disable : 4996)
		memmove(pchDest, pchSrc, nChars * sizeof(XCHAR));
#pragma warning (pop)
	}
	static void __stdcall CopyCharsOverlapped(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars)
	{
		#if _MSC_VER >= 1400
			memmove_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR));
		#else
			memmove(pchDest, pchSrc, nDestLen * sizeof(XCHAR));
		#endif
	}
	static int __stdcall StringLength(const char* psz)
	{
		if (psz == NULL)
		{
			return(0);
		}
		return (int(strlen(psz)));
	}
	static int __stdcall StringLength(const wchar_t* psz)
	{
		if (psz == NULL)
			return 0;

		return int(wcslen(psz));
	}
	static int __stdcall StringLengthN(const char* psz, size_t sizeInXChar )
	{
		if ( psz == NULL )
			return 0;

		return int( strnlen( psz, sizeInXChar ));
	}
	static int __stdcall StringLengthN(const wchar_t* psz, size_t sizeInXChar )
	{
		if ( psz == NULL )
			return 0;

		return int( wcsnlen( psz, sizeInXChar ));
	}
protected:
	static void __stdcall Concatenate(CMSimpleStringT& strResult, PCXSTR psz1, int nLength1, PCXSTR psz2, int nLength2)
	{
		int nNewLength = nLength1+nLength2;
		PXSTR pszBuffer = strResult.GetBuffer(nNewLength);
		CopyChars(pszBuffer, nLength1, psz1, nLength1 );
		CopyChars(pszBuffer + nLength1, nLength2, psz2, nLength2);
		strResult.ReleaseBufferSetLength(nNewLength);
	}
	// Implementation
private:
	void Attach(CMStringData* pData)
	{
		m_pszData = static_cast<PXSTR>(pData->data());
	}
	void Fork(int nLength)
	{
		CMStringData* pOldData = GetData();
		int nOldLength = pOldData->nDataLength;
		CMStringData* pNewData = Allocate(nLength, sizeof(XCHAR));
		if (pNewData != NULL)
		{
			int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength)+1;  // Copy '\0'
			CopyChars( PXSTR( pNewData->data()), nCharsToCopy, PCXSTR( pOldData->data()), nCharsToCopy );
			pNewData->nDataLength = nOldLength;
			pOldData->Release();
			Attach(pNewData);
		}
	}
	CMStringData* GetData() const
	{
		return (reinterpret_cast<CMStringData *>(m_pszData) - 1);
	}
	PXSTR PrepareWrite( int nLength )
	{
		CMStringData* pOldData = GetData();
		int nShared = 1 - pOldData->nRefs;  // nShared < 0 means true, >= 0 means false
		int nTooShort = pOldData->nAllocLength-nLength;  // nTooShort < 0 means true, >= 0 means false
		if ((nShared | nTooShort) < 0 )  // If either sign bit is set (i.e. either is less than zero), we need to copy data
			PrepareWrite2(nLength);

		return m_pszData;
	}
	void PrepareWrite2(int nLength)
	{
		CMStringData* pOldData = GetData();
		if (pOldData->nDataLength > nLength)
			nLength = pOldData->nDataLength;

		if (pOldData->IsShared())
		{
			Fork(nLength);
		}
		else if (pOldData->nAllocLength < nLength)
		{
			// Grow exponentially, until we hit 1K.
			int nNewLength = pOldData->nAllocLength;
			if ( nNewLength > 1024 )
				nNewLength += 1024;
			else
				nNewLength *= 2;

			if ( nNewLength < nLength )
				nNewLength = nLength;

			Reallocate( nNewLength );
		}
	}
	void Reallocate( int nLength )
	{
		CMStringData* pOldData = GetData();
		if ( pOldData->nAllocLength >= nLength || nLength <= 0)
			return;

		CMStringData* pNewData = Realloc( pOldData, nLength, sizeof( XCHAR ));
		if ( pNewData != NULL )
			Attach( pNewData );
	}

	void SetLength( int nLength )
	{
		GetData()->nDataLength = nLength;
		m_pszData[nLength] = 0;
	}

	static CMStringData* __stdcall CloneData(CMStringData* pData)
	{
		CMStringData* pNewData = NULL;

		if (!pData->IsLocked()) {
			pNewData = pData;
			pNewData->AddRef();
		}

		return pNewData;
	}

public :
	//	typedef CStrBufT<BaseType> CStrBuf;
private:
	PXSTR m_pszData;
};


template< typename _CharType = char >
class ChTraitsCRT : public ChTraitsBase< _CharType >
{
public:
	static char* __stdcall CharNext( const char* p )
	{
		return reinterpret_cast< char* >( _mbsinc( reinterpret_cast< const unsigned char* >( p )));
	}

	static int __stdcall IsDigit( char ch )
	{
		return _ismbcdigit( ch );
	}

	static int __stdcall IsSpace( char ch )
	{
		return _ismbcspace( ch );
	}

	static int __stdcall StringCompare( LPCSTR pszA, LPCSTR pszB )
	{
		return _mbscmp( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ));
	}

	static int __stdcall StringCompareIgnore( LPCSTR pszA, LPCSTR pszB )
	{
		return _mbsicmp( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ));
	}

	static int __stdcall StringCollate( LPCSTR pszA, LPCSTR pszB )
	{
		return _mbscoll( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ));
	}

	static int __stdcall StringCollateIgnore( LPCSTR pszA, LPCSTR pszB )
	{
		return _mbsicoll( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ));
	}

	static LPCSTR __stdcall StringFindString( LPCSTR pszBlock, LPCSTR pszMatch )
	{
		return reinterpret_cast< LPCSTR >( _mbsstr( reinterpret_cast< const unsigned char* >( pszBlock ),
			reinterpret_cast< const unsigned char* >( pszMatch )));
	}

	static LPSTR __stdcall StringFindString( LPSTR pszBlock, LPCSTR pszMatch )
	{
		return const_cast< LPSTR >( StringFindString( const_cast< LPCSTR >( pszBlock ), pszMatch ));
	}

	static LPCSTR __stdcall StringFindChar( LPCSTR pszBlock, char chMatch )
	{
		return reinterpret_cast< LPCSTR >( _mbschr( reinterpret_cast< const unsigned char* >( pszBlock ), (unsigned char)chMatch ));
	}

	static LPCSTR __stdcall StringFindCharRev( LPCSTR psz, char ch )
	{
		return reinterpret_cast< LPCSTR >( _mbsrchr( reinterpret_cast< const unsigned char* >( psz ), (unsigned char)ch ));
	}

	static LPCSTR __stdcall StringScanSet( LPCSTR pszBlock, LPCSTR pszMatch )
	{
		return reinterpret_cast< LPCSTR >( _mbspbrk( reinterpret_cast< const unsigned char* >( pszBlock ),
			reinterpret_cast< const unsigned char* >( pszMatch )));
	}

	static int __stdcall StringSpanIncluding( LPCSTR pszBlock, LPCSTR pszSet )
	{
		return (int)_mbsspn( reinterpret_cast< const unsigned char* >( pszBlock ), reinterpret_cast< const unsigned char* >( pszSet ));
	}

	static int __stdcall StringSpanExcluding( LPCSTR pszBlock, LPCSTR pszSet )
	{
		return (int)_mbscspn( reinterpret_cast< const unsigned char* >( pszBlock ), reinterpret_cast< const unsigned char* >( pszSet ));
	}

	static LPSTR __stdcall StringUppercase( LPSTR psz )
	{
#pragma warning (push)
#pragma warning(disable : 4996)
		return reinterpret_cast< LPSTR >( _mbsupr( reinterpret_cast< unsigned char* >( psz )) );
#pragma warning (pop)
	}

	static LPSTR __stdcall StringLowercase( LPSTR psz )
	{
#pragma warning (push)
#pragma warning(disable : 4996)
		return reinterpret_cast< LPSTR >( _mbslwr( reinterpret_cast< unsigned char* >( psz )) );
#pragma warning (pop)
	}

	static LPSTR __stdcall StringUppercase( LPSTR psz, size_t size )
	{
		#if _MSC_VER >= 1400
			_mbsupr_s(reinterpret_cast< unsigned char* >( psz ), size);
		#else
			_mbsupr(reinterpret_cast< unsigned char* >( psz ));
		#endif
		return psz;
	}

	static LPSTR __stdcall StringLowercase( LPSTR psz, size_t size )
	{
		#if _MSC_VER >= 1400
			_mbslwr_s( reinterpret_cast< unsigned char* >( psz ), size );
		#else
			_mbslwr(reinterpret_cast< unsigned char* >( psz ));
		#endif
		return psz;
	}

	static LPSTR __stdcall StringReverse( LPSTR psz )
	{
		return reinterpret_cast< LPSTR >( _mbsrev( reinterpret_cast< unsigned char* >( psz )) );
	}

	static int __stdcall GetFormattedLength( LPCSTR pszFormat, va_list args );

	static int __stdcall Format( LPSTR pszBuffer, LPCSTR pszFormat, va_list args )
	{
#pragma warning (push)
#pragma warning(disable : 4996)
		return vsprintf( pszBuffer, pszFormat, args );
#pragma warning (pop)

	}

	static int __stdcall Format( LPSTR pszBuffer, size_t nlength, LPCSTR pszFormat, va_list args );

	static int __stdcall GetBaseTypeLength( LPCSTR pszSrc )
	{
		// Returns required buffer length in XCHARs
		return int( strlen( pszSrc ));
	}

	static int __stdcall GetBaseTypeLength( LPCSTR pszSrc, int nLength )
	{
		(void)pszSrc;
		// Returns required buffer length in XCHARs
		return nLength;
	}

	static int __stdcall GetBaseTypeLength( LPCWSTR pszSource )
	{
		// Returns required buffer length in XCHARs
		return ::WideCharToMultiByte( _AtlGetConversionACP(), 0, pszSource, -1, NULL, 0, NULL, NULL )-1;
	}

	static int __stdcall GetBaseTypeLength( LPCWSTR pszSource, int nLength )
	{
		// Returns required buffer length in XCHARs
		return ::WideCharToMultiByte( _AtlGetConversionACP(), 0, pszSource, nLength, NULL, 0, NULL, NULL );
	}

	static void __stdcall ConvertToBaseType( LPSTR pszDest, int nDestLength, LPCSTR pszSrc, int nSrcLength = -1 )
	{
		if (nSrcLength == -1) { nSrcLength=1 + GetBaseTypeLength(pszSrc); }
		// nLen is in XCHARs
		memcpy_s( pszDest, nDestLength*sizeof( char ), 
			pszSrc, nSrcLength*sizeof( char ));
	}

	static void __stdcall ConvertToBaseType( LPSTR pszDest, int nDestLength, LPCWSTR pszSrc, int nSrcLength = -1)
	{
		// nLen is in XCHARs
		::WideCharToMultiByte( _AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength, NULL, NULL );
	}

	static void ConvertToOem( _CharType* pstrString)
	{
		BOOL fSuccess=::CharToOemA(pstrString, pstrString);
	}

	static void ConvertToAnsi( _CharType* pstrString)
	{
		BOOL fSuccess=::OemToCharA(pstrString, pstrString);
	}

	static void ConvertToOem( _CharType* pstrString, size_t size)
	{
		if(size>UINT_MAX)
		{
			return;
		}
		DWORD dwSize=static_cast<DWORD>(size);
		BOOL fSuccess=::CharToOemBuffA(pstrString, pstrString, dwSize);
	}

	static void ConvertToAnsi( _CharType* pstrString, size_t size)
	{
		if(size>UINT_MAX)
			return;

		DWORD dwSize=static_cast<DWORD>(size);
		BOOL fSuccess=::OemToCharBuffA(pstrString, pstrString, dwSize);
	}

	static void __stdcall FloodCharacters( char ch, int nLength, char* pch )
	{
		// nLength is in XCHARs
		memset( pch, ch, nLength );
	}

	static BSTR __stdcall AllocSysString( const char* pchData, int nDataLength )
	{
		int nLen = ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, NULL, NULL );
		BSTR bstr = ::SysAllocStringLen( NULL, nLen );
		if ( bstr != NULL )
			::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, bstr, nLen );

		return bstr;
	}

	static BOOL __stdcall ReAllocSysString( const char* pchData, BSTR* pbstr, int nDataLength )
	{
		int nLen = ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, NULL, NULL );
		BOOL bSuccess = ::SysReAllocStringLen( pbstr, NULL, nLen );
		if ( bSuccess )
			::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, *pbstr, nLen );

		return bSuccess;
	}

	static int __stdcall SafeStringLen( LPCSTR psz )
	{
		// returns length in bytes
		return (psz != NULL) ? int( strlen( psz )) : 0;
	}

	static int __stdcall SafeStringLen( LPCWSTR psz )
	{
		// returns length in wchar_ts
		return (psz != NULL) ? int( wcslen( psz )) : 0;
	}

	static int __stdcall GetCharLen( const wchar_t* pch )
	{
		// returns char length
		return 1;
	}

	static int __stdcall GetCharLen( const char* pch )
	{
		// returns char length
		return int( _mbclen( reinterpret_cast< const unsigned char* >( pch )) );
	}

	static DWORD __stdcall GetEnvironmentVariable( LPCSTR pszVar, LPSTR pszBuffer, DWORD dwSize )
	{
		return ::GetEnvironmentVariableA( pszVar, pszBuffer, dwSize );
	}
};

// specialization for wchar_t
template<>
class ChTraitsCRT< wchar_t > : public ChTraitsBase< wchar_t >
{
	static DWORD __stdcall _GetEnvironmentVariableW( LPCWSTR pszName, LPWSTR pszBuffer, DWORD nSize )
	{
		return ::GetEnvironmentVariableW( pszName, pszBuffer, nSize );
	}

public:
	static LPWSTR __stdcall CharNext( LPCWSTR psz )
	{
		return const_cast< LPWSTR >( psz+1 );
	}

	static int __stdcall IsDigit( wchar_t ch )
	{
		return iswdigit( static_cast<unsigned short>(ch));
	}

	static int __stdcall IsSpace( wchar_t ch )
	{
		return iswspace( static_cast<unsigned short>(ch));
	}

	static int __stdcall StringCompare( LPCWSTR pszA, LPCWSTR pszB )
	{
		return wcscmp( pszA, pszB );
	}

	static int __stdcall StringCompareIgnore( LPCWSTR pszA, LPCWSTR pszB )
	{
		return _wcsicmp( pszA, pszB );
	}

	static int __stdcall StringCollate( LPCWSTR pszA, LPCWSTR pszB )
	{
		return wcscoll( pszA, pszB );
	}

	static int __stdcall StringCollateIgnore( LPCWSTR pszA, LPCWSTR pszB )
	{
		return _wcsicoll( pszA, pszB );
	}

	static LPCWSTR __stdcall StringFindString( LPCWSTR pszBlock, LPCWSTR pszMatch )
	{
		return wcsstr( pszBlock, pszMatch );
	}

	static LPWSTR __stdcall StringFindString( LPWSTR pszBlock, LPCWSTR pszMatch )
	{
		return const_cast< LPWSTR >( StringFindString( const_cast< LPCWSTR >( pszBlock ), pszMatch ));
	}

	static LPCWSTR __stdcall StringFindChar( LPCWSTR pszBlock, wchar_t chMatch )
	{
		return wcschr( pszBlock, chMatch );
	}

	static LPCWSTR __stdcall StringFindCharRev( LPCWSTR psz, wchar_t ch )
	{
		return wcsrchr( psz, ch );
	}

	static LPCWSTR __stdcall StringScanSet( LPCWSTR pszBlock, LPCWSTR pszMatch )
	{
		return wcspbrk( pszBlock, pszMatch );
	}

	static int __stdcall StringSpanIncluding( LPCWSTR pszBlock, LPCWSTR pszSet )
	{
		return (int)wcsspn( pszBlock, pszSet );
	}

	static int __stdcall StringSpanExcluding( LPCWSTR pszBlock, LPCWSTR pszSet )
	{
		return (int)wcscspn( pszBlock, pszSet );
	}

	static LPWSTR __stdcall StringUppercase( LPWSTR psz )
	{
#pragma warning (push)
#pragma warning(disable : 4996)
		return _wcsupr( psz );
#pragma warning (pop)
	}

	static LPWSTR __stdcall StringLowercase( LPWSTR psz )
	{
#pragma warning (push)
#pragma warning(disable : 4996)
		return _wcslwr( psz );
#pragma warning (pop)
	}

	static LPWSTR __stdcall StringUppercase( LPWSTR psz, size_t )
	{
		return _wcsupr( psz );
	}

	static LPWSTR __stdcall StringLowercase( LPWSTR psz, size_t )
	{
		return _wcslwr( psz );
	}

	static LPWSTR __stdcall StringReverse( LPWSTR psz )
	{
		return _wcsrev( psz );
	}

	static int __stdcall GetFormattedLength( LPCWSTR pszFormat, va_list args);

	static int __stdcall Format( LPWSTR pszBuffer, LPCWSTR pszFormat, va_list args)
	{
#pragma warning (push)
#pragma warning(disable : 4996)
		return vswprintf( pszBuffer, pszFormat, args );
#pragma warning (pop)
	}

	static int __stdcall Format( LPWSTR pszBuffer, size_t nLength, LPCWSTR pszFormat, va_list args);

	static int __stdcall GetBaseTypeLength( LPCSTR pszSrc )
	{
		// Returns required buffer size in wchar_ts
		return ::MultiByteToWideChar( CP_ACP, 0, pszSrc, -1, NULL, 0 )-1;
	}

	static int __stdcall GetBaseTypeLength( LPCSTR pszSrc, int nLength )
	{
		// Returns required buffer size in wchar_ts
		return ::MultiByteToWideChar( CP_ACP, 0, pszSrc, nLength, NULL, 0 );
	}

	static int __stdcall GetBaseTypeLength( LPCWSTR pszSrc )
	{
		// Returns required buffer size in wchar_ts
		return (int)wcslen( pszSrc );
	}

	static int __stdcall GetBaseTypeLength( LPCWSTR pszSrc, int nLength )
	{
		(void)pszSrc;
		// Returns required buffer size in wchar_ts
		return nLength;
	}

	static void __stdcall ConvertToBaseType( LPWSTR pszDest, int nDestLength, LPCSTR pszSrc, int nSrcLength = -1)
	{
		// nLen is in wchar_ts
		::MultiByteToWideChar( CP_ACP, 0, pszSrc, nSrcLength, pszDest, nDestLength );
	}

	static void __stdcall ConvertToBaseType( LPWSTR pszDest, int nDestLength, LPCWSTR pszSrc, int nSrcLength = -1 )
	{		
		if (nSrcLength == -1) { nSrcLength=1 + GetBaseTypeLength(pszSrc); }
		// nLen is in wchar_ts
		#if _MSC_VER >= 1400
			wmemcpy_s(pszDest, nDestLength, pszSrc, nSrcLength);
		#else
			wmemcpy(pszDest, pszSrc, nDestLength);
		#endif
	}

	static void __stdcall FloodCharacters( wchar_t ch, int nLength, LPWSTR psz )
	{
		// nLength is in XCHARs
		for ( int i = 0; i < nLength; i++ )
		{
			psz[i] = ch;
		}
	}

	static BSTR __stdcall AllocSysString( const wchar_t* pchData, int nDataLength )
	{
		return ::SysAllocStringLen( pchData, nDataLength );
	}

	static BOOL __stdcall ReAllocSysString( const wchar_t* pchData, BSTR* pbstr, int nDataLength )
	{
		return ::SysReAllocStringLen( pbstr, pchData, nDataLength );
	}

	static int __stdcall SafeStringLen( LPCSTR psz )
	{
		// returns length in bytes
		return (psz != NULL) ? (int)strlen( psz ) : 0;
	}

	static int __stdcall SafeStringLen( LPCWSTR psz )
	{
		// returns length in wchar_ts
		return (psz != NULL) ? (int)wcslen( psz ) : 0;
	}

	static int __stdcall GetCharLen( const wchar_t* pch )
	{
		(void)pch;
		// returns char length
		return 1;
	}

	static int __stdcall GetCharLen( const char* pch )
	{
		// returns char length
		return (int)( _mbclen( reinterpret_cast< const unsigned char* >( pch )) );
	}

	static DWORD __stdcall GetEnvironmentVariable( LPCWSTR pszVar, LPWSTR pszBuffer, DWORD dwSize )
	{
		return _GetEnvironmentVariableW( pszVar, pszBuffer, dwSize );
	}

	static void __stdcall ConvertToOem( LPWSTR /*psz*/ )
	{
	}

	static void __stdcall ConvertToAnsi( LPWSTR /*psz*/ )
	{
	}

	static void __stdcall ConvertToOem( LPWSTR /*psz*/, size_t )
	{
	}

	static void __stdcall ConvertToAnsi( LPWSTR /*psz*/, size_t ) 
	{
	}
};

template< typename BaseType, class StringTraits >
class CMStringT : public CMSimpleStringT< BaseType >
{
public:
	typedef CMSimpleStringT< BaseType> CThisSimpleString;
	typedef typename CThisSimpleString::XCHAR XCHAR;
	typedef typename CThisSimpleString::PXSTR PXSTR;
	typedef typename CThisSimpleString::PCXSTR PCXSTR;
	typedef typename CThisSimpleString::YCHAR YCHAR;
	typedef typename CThisSimpleString::PYSTR PYSTR;
	typedef typename CThisSimpleString::PCYSTR PCYSTR;

public:
	CMStringT() : CThisSimpleString()
	  {
	  }

	  static void __stdcall Construct( CMStringT* pString )
	  {
		  new( pString ) CMStringT;
	  }

	  // Copy constructor
	  CMStringT( const CMStringT& strSrc ) :
	  CThisSimpleString( strSrc )
	  {
	  }

	  CMStringT( const XCHAR* pszSrc ) :
	  CThisSimpleString()
	  {
		  // nDestLength is in XCHARs
		  *this = pszSrc;
	  }

	  CMStringT( const YCHAR* pszSrc ) :
	  CThisSimpleString()
	  {
		  *this = pszSrc;
	  }


	  CMStringT( const unsigned char* pszSrc ) :
	  CThisSimpleString()
	  {
		  *this = reinterpret_cast< const char* >( pszSrc );
	  }

	  CMStringT( char ch, int nLength = 1 ) :
	  CThisSimpleString()
	  {
		  if ( nLength > 0 )
		  {
			  PXSTR pszBuffer = this->GetBuffer( nLength );
			  StringTraits::FloodCharacters( XCHAR( ch ), nLength, pszBuffer );
			  this->ReleaseBufferSetLength( nLength );
		  }
	  }

	  CMStringT( wchar_t ch, int nLength = 1 ) :
	  CThisSimpleString()
	  {
		  if ( nLength > 0 )
		  {			
			  //Convert ch to the BaseType
			  wchar_t pszCh[2] = { ch , 0 };
			  int nBaseTypeCharLen = 1;

			  if(ch != L'\0')
			  {
				  nBaseTypeCharLen = StringTraits::GetBaseTypeLength(pszCh);
			  }

			  XCHAR *buffBaseTypeChar = new XCHAR[nBaseTypeCharLen+1];
			  StringTraits::ConvertToBaseType( buffBaseTypeChar, nBaseTypeCharLen+1, pszCh, 1 );
			  //Allocate enough characters in String and flood (replicate) with the (converted character)*nLength
			  PXSTR pszBuffer = this->GetBuffer( nLength*nBaseTypeCharLen );
			  if (nBaseTypeCharLen == 1)
			  {   //Optimization for a common case - wide char translates to 1 ansi/wide char.
				  StringTraits::FloodCharacters( buffBaseTypeChar[0], nLength, pszBuffer );				
			  } else
			  {
				  XCHAR* p=pszBuffer;
				  for (int i=0 ; i < nLength ;++i)
				  {
					  for (int j=0 ; j < nBaseTypeCharLen ;++j)
					  {	
						  *p=buffBaseTypeChar[j];
						  ++p;
					  }
				  }
			  }
			  this->ReleaseBufferSetLength( nLength*nBaseTypeCharLen );
			  delete [] buffBaseTypeChar;
		  }
	  }

	  CMStringT( const XCHAR* pch, int nLength ) :
	  CThisSimpleString( pch, nLength )
	  {
	  }

	  CMStringT( const YCHAR* pch, int nLength ) :
	  CThisSimpleString()
	  {
		  if ( nLength > 0 )
		  {
			  int nDestLength = StringTraits::GetBaseTypeLength( pch, nLength );
			  PXSTR pszBuffer = this->GetBuffer( nDestLength );
			  StringTraits::ConvertToBaseType( pszBuffer, nDestLength, pch, nLength );
			  this->ReleaseBufferSetLength( nDestLength );
		  }
	  }

	  // Destructor
	  ~CMStringT()
	  {
	  }

	  // Assignment operators
	  CMStringT& operator=( const CMStringT& strSrc )
	  {
		  CThisSimpleString::operator=( strSrc );

		  return *this;
	  }

	  CMStringT& operator=( PCXSTR pszSrc )
	  {
		  CThisSimpleString::operator=( pszSrc );

		  return *this;
	  }

	  CMStringT& operator=( PCYSTR pszSrc )
	  {
		  // nDestLength is in XCHARs
		  int nDestLength = (pszSrc != NULL) ? StringTraits::GetBaseTypeLength( pszSrc ) : 0;
		  if ( nDestLength > 0 )
		  {
			  PXSTR pszBuffer = this->GetBuffer( nDestLength );
			  StringTraits::ConvertToBaseType( pszBuffer, nDestLength, pszSrc);
			  this->ReleaseBufferSetLength( nDestLength );
		  }
		  else
		  {
			  this->Empty();
		  }

		  return *this;
	  }

	  CMStringT& operator=( const unsigned char* pszSrc )
	  {
		  return operator=( reinterpret_cast< const char* >( pszSrc ));
	  }

	  CMStringT& operator=( char ch )
	  {
		  char ach[2] = { ch, 0 };

		  return operator=( ach );
	  }

	  CMStringT& operator=( wchar_t ch )
	  {
		  wchar_t ach[2] = { ch, 0 };

		  return operator=( ach );
	  }

//	  CMStringT& operator=( const VARIANT& var );

	  CMStringT& operator+=( const CMStringT& str )
	  {
		  CThisSimpleString::operator+=( str );
		  return *this;
	  }

	  CMStringT& operator+=( const CThisSimpleString& str )
	  {
		  CThisSimpleString::operator+=( str );
		  return *this;
	  }

	  CMStringT& operator+=( PCXSTR pszSrc )
	  {
		  CThisSimpleString::operator+=( pszSrc );
		  return *this;
	  }
//	  template< int t_nSize >
//	  CMStringT& operator+=( const CStaticString< XCHAR, t_nSize >& strSrc )
//	  {
//		  CThisSimpleString::operator+=( strSrc );
//
//		  return *this;
//	  }
	  CMStringT& operator+=( PCYSTR pszSrc )
	  {
		  CMStringT str( pszSrc );

		  return operator+=( str );
	  }

	  CMStringT& operator+=( char ch )
	  {
		  CThisSimpleString::operator+=( ch );

		  return *this;
	  }

	  CMStringT& operator+=( unsigned char ch )
	  {
		  CThisSimpleString::operator+=( ch );

		  return *this;
	  }

	  CMStringT& operator+=( wchar_t ch )
	  {
		  CThisSimpleString::operator+=( ch );

		  return *this;
	  }

	  // Comparison

	  int Compare( PCXSTR psz ) const
	  {
		  return StringTraits::StringCompare( this->GetString(), psz );
	  }

	  int CompareNoCase( PCXSTR psz ) const
	  {
		  return StringTraits::StringCompareIgnore( this->GetString(), psz );
	  }

	  int Collate( PCXSTR psz ) const
	  {
		  return StringTraits::StringCollate( this->GetString(), psz );
	  }

	  int CollateNoCase( PCXSTR psz ) const
	  {
		  return StringTraits::StringCollateIgnore( this->GetString(), psz );
	  }

	  // Advanced manipulation

	  // Delete 'nCount' characters, starting at index 'iIndex'
	  int Delete( int iIndex, int nCount = 1 )
	  {
		  if ( iIndex < 0 )
			  iIndex = 0;

		  if ( nCount < 0 )
			  nCount = 0;

		  int nLength = this->GetLength();
		  if ( nCount + iIndex > nLength )
		  {
			  nCount = nLength-iIndex;
		  }
		  if ( nCount > 0 )
		  {
			  int nNewLength = nLength-nCount;
			  int nXCHARsToCopy = nLength-(iIndex+nCount)+1;
			  PXSTR pszBuffer = this->GetBuffer();
				#if _MSC_VER >= 1400
				  memmove_s( pszBuffer+iIndex, nXCHARsToCopy*sizeof( XCHAR ), pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof( XCHAR ));
				#else
				  memmove( pszBuffer+iIndex, pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof( XCHAR ));
				#endif
			  this->ReleaseBufferSetLength( nNewLength );
		  }

		  return this->GetLength();
	  }

	  // Insert character 'ch' before index 'iIndex'
	  int Insert( int iIndex, XCHAR ch )
	  {
		  if ( iIndex < 0 )
			  iIndex = 0;

			if ( iIndex > this->GetLength())
				iIndex = this->GetLength();

			int nNewLength = this->GetLength()+1;

			PXSTR pszBuffer = this->GetBuffer( nNewLength );

		  // move existing bytes down 
			#if _MSC_VER >= 1400
				memmove_s( pszBuffer+iIndex+1, (nNewLength-iIndex)*sizeof( XCHAR ), pszBuffer+iIndex, (nNewLength-iIndex)*sizeof( XCHAR ));
			#else
				memmove( pszBuffer+iIndex+1, pszBuffer+iIndex, (nNewLength-iIndex)*sizeof( XCHAR ));
			#endif
			pszBuffer[iIndex] = ch;

			this->ReleaseBufferSetLength( nNewLength );
			return nNewLength;
		}

		// Insert string 'psz' before index 'iIndex'
		int Insert( int iIndex, PCXSTR psz )
	  {
		  if ( iIndex < 0 )
			  iIndex = 0;

		  if ( iIndex > this->GetLength())
		  {
			  iIndex = this->GetLength();
		  }

		  // nInsertLength and nNewLength are in XCHARs
		  int nInsertLength = StringTraits::SafeStringLen( psz );
		  int nNewLength = this->GetLength();
		  if ( nInsertLength > 0 )
		  {
			  nNewLength += nInsertLength;

			  PXSTR pszBuffer = this->GetBuffer( nNewLength );
			  // move existing bytes down 
				#if _MSC_VER >= 1400
					memmove_s( pszBuffer+iIndex+nInsertLength, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ), pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ));
					memcpy_s( pszBuffer+iIndex, nInsertLength*sizeof( XCHAR ), psz, nInsertLength*sizeof( XCHAR ));
				#else
					memmove( pszBuffer+iIndex+nInsertLength, pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ));
					memcpy( pszBuffer+iIndex, psz, nInsertLength*sizeof( XCHAR ));
				#endif
			  this->ReleaseBufferSetLength( nNewLength );
		  }

		  return nNewLength;
	  }

	  // Replace all occurrences of character 'chOld' with character 'chNew'
	  int Replace( XCHAR chOld, XCHAR chNew )
	  {
		  int nCount = 0;

		  // short-circuit the nop case
		  if ( chOld != chNew )
		  {
			  // otherwise modify each character that matches in the string
			  bool bCopied = false;
			  PXSTR pszBuffer = const_cast< PXSTR >( this->GetString());  // We don't actually write to pszBuffer until we've called GetBuffer().

			  int nLength = this->GetLength();
			  int iChar = 0;
			  while( iChar < nLength )
			  {
				  // replace instances of the specified character only
				  if ( pszBuffer[iChar] == chOld )
				  {
					  if ( !bCopied )
					  {
						  bCopied = true;
						  pszBuffer = this->GetBuffer( nLength );
					  }
					  pszBuffer[iChar] = chNew;
					  nCount++;
				  }
				  iChar = int( StringTraits::CharNext( pszBuffer+iChar )-pszBuffer );
			  }
			  if ( bCopied )
			  {
				  this->ReleaseBufferSetLength( nLength );
			  }
		  }

		  return nCount;
	  }

	  // Replace all occurrences of string 'pszOld' with string 'pszNew'
	  int Replace( PCXSTR pszOld, PCXSTR pszNew )
	  {
		  // can't have empty or NULL lpszOld

		  // nSourceLen is in XCHARs
		  int nSourceLen = StringTraits::SafeStringLen( pszOld );
		  if ( nSourceLen == 0 )
			  return 0;
		  // nReplacementLen is in XCHARs
		  int nReplacementLen = StringTraits::SafeStringLen( pszNew );

		  // loop once to figure out the size of the result string
		  int nCount = 0;
		  {
			  PCXSTR pszStart = this->GetString();
			  PCXSTR pszEnd = pszStart+this->GetLength();
			  while( pszStart < pszEnd )
			  {
				  PCXSTR pszTarget;
				  while( (pszTarget = StringTraits::StringFindString( pszStart, pszOld )) != NULL)
				  {
					  nCount++;
					  pszStart = pszTarget+nSourceLen;
				  }
				  pszStart += StringTraits::SafeStringLen( pszStart )+1;
			  }
		  }

		  // if any changes were made, make them
		  if ( nCount > 0 )
		  {
			  // if the buffer is too small, just
			  //   allocate a new buffer (slow but sure)
			  int nOldLength = this->GetLength();
			  int nNewLength = nOldLength+(nReplacementLen-nSourceLen)*nCount;

			  PXSTR pszBuffer = this->GetBuffer( __max( nNewLength, nOldLength ));

			  PXSTR pszStart = pszBuffer;
			  PXSTR pszEnd = pszStart+nOldLength;

			  // loop again to actually do the work
			  while( pszStart < pszEnd )
			  {
				  PXSTR pszTarget;
				  while( (pszTarget = StringTraits::StringFindString( pszStart, pszOld )) != NULL )
				  {
					  int nBalance = nOldLength-int(pszTarget-pszBuffer+nSourceLen);
					  memmove_s( pszTarget+nReplacementLen, nBalance*sizeof( XCHAR ), 
						  pszTarget+nSourceLen, nBalance*sizeof( XCHAR ));
					  memcpy_s( pszTarget, nReplacementLen*sizeof( XCHAR ), 
						  pszNew, nReplacementLen*sizeof( XCHAR ));
					  pszStart = pszTarget+nReplacementLen;
					  pszTarget[nReplacementLen+nBalance] = 0;
					  nOldLength += (nReplacementLen-nSourceLen);
				  }
				  pszStart += StringTraits::SafeStringLen( pszStart )+1;
			  }
			  this->ReleaseBufferSetLength( nNewLength );
		  }

		  return nCount;
	  }

	  // Remove all occurrences of character 'chRemove'
	  int Remove( XCHAR chRemove )
	  {
		  int nLength = this->GetLength();
		  PXSTR pszBuffer = this->GetBuffer( nLength );

		  PXSTR pszSource = pszBuffer;
		  PXSTR pszDest = pszBuffer;
		  PXSTR pszEnd = pszBuffer+nLength;

		  while( pszSource < pszEnd )
		  {
			  PXSTR pszNewSource = StringTraits::CharNext( pszSource );
			  if ( *pszSource != chRemove )
			  {
				  // Copy the source to the destination.  Remember to copy all bytes of an MBCS character
				  // Copy the source to the destination.  Remember to copy all bytes of an MBCS character
				  size_t NewSourceGap = (pszNewSource-pszSource);
				  PXSTR pszNewDest = pszDest + NewSourceGap;
				  size_t i = 0;
				  for (i = 0;  pszDest != pszNewDest && i < NewSourceGap; i++)
				  {
					  *pszDest = *pszSource;
					  pszSource++;
					  pszDest++;
				  }
			  }
			  pszSource = pszNewSource;
		  }
		  *pszDest = 0;
		  int nCount = int( pszSource-pszDest );
		  this->ReleaseBufferSetLength( nLength-nCount );

		  return nCount;
	  }

	  CMStringT Tokenize( PCXSTR pszTokens, int& iStart ) const
	  {
		  if ( (pszTokens == NULL) || (*pszTokens == (XCHAR)0))
		  {
			  if (iStart < this->GetLength())
				  return CMStringT( this->GetString()+iStart );
		  }
		  else
		  {
			  PCXSTR pszPlace = this->GetString()+iStart;
			  PCXSTR pszEnd = this->GetString()+this->GetLength();
			  if ( pszPlace < pszEnd )
			  {
				  int nIncluding = StringTraits::StringSpanIncluding( pszPlace, pszTokens );

				  if ( (pszPlace+nIncluding) < pszEnd )
				  {
					  pszPlace += nIncluding;
					  int nExcluding = StringTraits::StringSpanExcluding( pszPlace, pszTokens );

					  int iFrom = iStart+nIncluding;
					  int nUntil = nExcluding;
					  iStart = iFrom+nUntil+1;

					  return Mid( iFrom, nUntil );
				  }
			  }
		  }

		  // return empty string, done tokenizing
		  iStart = -1;

		  return CMStringT();
	  }

	  // find routines

	  // Find the first occurrence of character 'ch', starting at index 'iStart'
	  int Find( XCHAR ch, int iStart = 0 ) const
	  {
		  // nLength is in XCHARs
		  int nLength = this->GetLength();
		  if ( iStart < 0 || iStart >= nLength)
			  return -1;

		  // find first single character
		  PCXSTR psz = StringTraits::StringFindChar( this->GetString()+iStart, ch );

		  // return -1 if not found and index otherwise
		  return (psz == NULL) ? -1 : int( psz-this->GetString());
	  }

	  // look for a specific sub-string

	  // Find the first occurrence of string 'pszSub', starting at index 'iStart'
	  int Find( PCXSTR pszSub, int iStart = 0 ) const
	  {
		  // iStart is in XCHARs
		  if(pszSub == NULL)
			  return -1;

		  // nLength is in XCHARs
		  int nLength = this->GetLength();
		  if ( iStart < 0 || iStart > nLength )
			  return -1;

		  // find first matching substring
		  PCXSTR psz = StringTraits::StringFindString( this->GetString()+iStart, pszSub );

		  // return -1 for not found, distance from beginning otherwise
		  return (psz == NULL) ? -1 : int( psz-this->GetString());
	  }

	  // Find the first occurrence of any of the characters in string 'pszCharSet'
	  int FindOneOf( PCXSTR pszCharSet ) const
	  {
		  PCXSTR psz = StringTraits::StringScanSet( this->GetString(), pszCharSet );
		  return (psz == NULL) ? -1 : int( psz-this->GetString());
	  }

	  // Find the last occurrence of character 'ch'
	  int ReverseFind( XCHAR ch ) const
	  {
		  // find last single character
		  PCXSTR psz = StringTraits::StringFindCharRev( this->GetString(), ch );

		  // return -1 if not found, distance from beginning otherwise
		  return (psz == NULL) ? -1 : int( psz-this->GetString());
	  }

	  // manipulation

	  // Convert the string to uppercase
	  CMStringT& MakeUpper()
	  {
		  int nLength = this->GetLength();
		  PXSTR pszBuffer = this->GetBuffer( nLength );
		  StringTraits::StringUppercase( pszBuffer, nLength+1 );
		  this->ReleaseBufferSetLength( nLength );

		  return *this;
	  }

	  // Convert the string to lowercase
	  CMStringT& MakeLower()
	  {
		  int nLength = this->GetLength();
		  PXSTR pszBuffer = this->GetBuffer( nLength );
		  StringTraits::StringLowercase( pszBuffer, nLength+1 );
		  this->ReleaseBufferSetLength( nLength );

		  return *this;
	  }

	  // Reverse the string
	  CMStringT& MakeReverse()
	  {
		  int nLength = this->GetLength();
		  PXSTR pszBuffer = this->GetBuffer( nLength );
		  StringTraits::StringReverse( pszBuffer );
		  this->ReleaseBufferSetLength( nLength );

		  return *this;
	  }

	  // trimming

	  // Remove all trailing whitespace
	  CMStringT& TrimRight()
	  {
		  // find beginning of trailing spaces by starting
		  // at beginning (DBCS aware)

		  PCXSTR psz = this->GetString();
		  PCXSTR pszLast = NULL;

		  while( *psz != 0 )
		  {
			  if ( StringTraits::IsSpace( *psz ))
			  {
				  if ( pszLast == NULL )
					  pszLast = psz;
			  }
			  else
			  {
				  pszLast = NULL;
			  }
			  psz = StringTraits::CharNext( psz );
		  }

		  if ( pszLast != NULL )
		  {
			  // truncate at trailing space start
			  int iLast = int( pszLast-this->GetString());

			  this->Truncate( iLast );
		  }

		  return *this;
	  }

	  // Remove all leading whitespace
	  CMStringT& TrimLeft()
	  {
		  // find first non-space character

		  PCXSTR psz = this->GetString();

		  while( StringTraits::IsSpace( *psz ))
		  {
			  psz = StringTraits::CharNext( psz );
		  }

		  if ( psz != this->GetString())
		  {
			  // fix up data and length
			  int iFirst = int( psz-this->GetString());
			  PXSTR pszBuffer = this->GetBuffer( this->GetLength());
			  psz = pszBuffer+iFirst;
			  int nDataLength = this->GetLength()-iFirst;
			  memmove_s( pszBuffer, (this->GetLength()+1)*sizeof( XCHAR ), 
				  psz, (nDataLength+1)*sizeof( XCHAR ));
			  this->ReleaseBufferSetLength( nDataLength );
		  }

		  return *this;
	  }

	  // Remove all leading and trailing whitespace
	  CMStringT& Trim()
	  {
		  return TrimRight().TrimLeft();
	  }

	  // Remove all leading and trailing occurrences of character 'chTarget'
	  CMStringT& Trim( XCHAR chTarget )
	  {
		  return TrimRight( chTarget ).TrimLeft( chTarget );
	  }

	  // Remove all leading and trailing occurrences of any of the characters in the string 'pszTargets'
	  CMStringT& Trim( PCXSTR pszTargets )
	  {
		  return TrimRight( pszTargets ).TrimLeft( pszTargets );
	  }

	  // trimming anything (either side)

	  // Remove all trailing occurrences of character 'chTarget'
	  CMStringT& TrimRight( XCHAR chTarget )
	  {
		  // find beginning of trailing matches
		  // by starting at beginning (DBCS aware)

		  PCXSTR psz = this->GetString();
		  PCXSTR pszLast = NULL;

		  while( *psz != 0 )
		  {
			  if ( *psz == chTarget )
			  {
				  if ( pszLast == NULL )
				  {
					  pszLast = psz;
				  }
			  }
			  else
			  {
				  pszLast = NULL;
			  }
			  psz = StringTraits::CharNext( psz );
		  }

		  if ( pszLast != NULL )
		  {
			  // truncate at left-most matching character  
			  int iLast = int( pszLast-this->GetString());
			  this->Truncate( iLast );
		  }

		  return *this;
	  }

	  // Remove all trailing occurrences of any of the characters in string 'pszTargets'
	  CMStringT& TrimRight( PCXSTR pszTargets )
	  {
		  // if we're not trimming anything, we're not doing any work
		  if ( (pszTargets == NULL) || (*pszTargets == 0))
		  {
			  return *this;
		  }

		  // find beginning of trailing matches
		  // by starting at beginning (DBCS aware)

		  PCXSTR psz = this->GetString();
		  PCXSTR pszLast = NULL;

		  while( *psz != 0 )
		  {
			  if ( StringTraits::StringFindChar( pszTargets, *psz ) != NULL )
			  {
				  if ( pszLast == NULL )
				  {
					  pszLast = psz;
				  }
			  }
			  else
			  {
				  pszLast = NULL;
			  }
			  psz = StringTraits::CharNext( psz );
		  }

		  if ( pszLast != NULL )
		  {
			  // truncate at left-most matching character  
			  int iLast = int( pszLast-this->GetString());
			  this->Truncate( iLast );
		  }

		  return *this;
	  }

	  // Remove all leading occurrences of character 'chTarget'
	  CMStringT& TrimLeft( XCHAR chTarget )
	  {
		  // find first non-matching character
		  PCXSTR psz = this->GetString();

		  while( chTarget == *psz )
		  {
			  psz = StringTraits::CharNext( psz );
		  }

		  if ( psz != this->GetString())
		  {
			  // fix up data and length
			  int iFirst = int( psz-this->GetString());
			  PXSTR pszBuffer = this->GetBuffer( this->GetLength());
			  psz = pszBuffer+iFirst;
			  int nDataLength = this->GetLength()-iFirst;
			  memmove_s( pszBuffer, (this->GetLength()+1)*sizeof( XCHAR ), 
				  psz, (nDataLength+1)*sizeof( XCHAR ));
			  this->ReleaseBufferSetLength( nDataLength );
		  }

		  return *this;
	  }

	  // Remove all leading occurrences of any of the characters in string 'pszTargets'
	  CMStringT& TrimLeft( PCXSTR pszTargets )
	  {
		  // if we're not trimming anything, we're not doing any work
		  if ( (pszTargets == NULL) || (*pszTargets == 0))
		  {
			  return *this;
		  }

		  PCXSTR psz = this->GetString();
		  while( (*psz != 0) && (StringTraits::StringFindChar( pszTargets, *psz ) != NULL))
		  {
			  psz = StringTraits::CharNext( psz );
		  }

		  if ( psz != this->GetString())
		  {
			  // fix up data and length
			  int iFirst = int( psz-this->GetString());
			  PXSTR pszBuffer = this->GetBuffer( this->GetLength());
			  psz = pszBuffer+iFirst;
			  int nDataLength = this->GetLength()-iFirst;
			  memmove_s( pszBuffer, (this->GetLength()+1)*sizeof( XCHAR ), 
				  psz, (nDataLength+1)*sizeof( XCHAR ));
			  this->ReleaseBufferSetLength( nDataLength );
		  }

		  return *this;
	  }

	  // Convert the string to the OEM character set
	  void AnsiToOem()
	  {
		  int nLength = this->GetLength();
		  PXSTR pszBuffer = this->GetBuffer( nLength );
		  StringTraits::ConvertToOem( pszBuffer, nLength+1 );
		  this->ReleaseBufferSetLength( nLength );
	  }

	  // Convert the string to the ANSI character set
	  void OemToAnsi()
	  {
		  int nLength = this->GetLength();
		  PXSTR pszBuffer = this->GetBuffer( nLength );
		  StringTraits::ConvertToAnsi( pszBuffer, nLength+1 );
		  this->ReleaseBufferSetLength( nLength );
	  }

	  // Very simple sub-string extraction

	  // Return the substring starting at index 'iFirst'
	  CMStringT Mid( int iFirst ) const
	  {
		  return Mid( iFirst, this->GetLength()-iFirst );
	  }

	  // Return the substring starting at index 'iFirst', with length 'nCount'
	  CMStringT Mid( int iFirst, int nCount ) const
	  {
		  // nCount is in XCHARs

		  // out-of-bounds requests return sensible things
		  if (iFirst < 0)
			  iFirst = 0;
		  if (nCount < 0)
			  nCount = 0;

		  if ( (iFirst + nCount) > this->GetLength())
			  nCount = this->GetLength()-iFirst;

		  if ( iFirst > this->GetLength())
			  nCount = 0;

		  // optimize case of returning entire string
		  if ( (iFirst == 0) && ((iFirst+nCount) == this->GetLength()))
			  return *this;

		  return CMStringT( this->GetString()+iFirst, nCount );
	  }

	  // Return the substring consisting of the rightmost 'nCount' characters
	  CMStringT Right( int nCount ) const
	  {
		  // nCount is in XCHARs
		  if (nCount < 0)
			  nCount = 0;

		  int nLength = this->GetLength();
		  if ( nCount >= nLength )
		  {
			  return *this;
		  }

		  return CMStringT( this->GetString()+nLength-nCount, nCount );
	  }

	  // Return the substring consisting of the leftmost 'nCount' characters
	  CMStringT Left( int nCount ) const
	  {
		  // nCount is in XCHARs
		  if (nCount < 0)
			  nCount = 0;

		  int nLength = this->GetLength();
		  if ( nCount >= nLength )
			  return *this;

		  return CMStringT( this->GetString(), nCount );
	  }

	  // Return the substring consisting of the leftmost characters in the set 'pszCharSet'
	  CMStringT SpanIncluding( PCXSTR pszCharSet ) const
	  {
		  return Left( StringTraits::StringSpanIncluding( this->GetString(), pszCharSet ));
	  }

	  // Return the substring consisting of the leftmost characters not in the set 'pszCharSet'
	  CMStringT SpanExcluding( PCXSTR pszCharSet ) const
	  {
		  return Left( StringTraits::StringSpanExcluding( this->GetString(), pszCharSet ));
	  }

	  // Format data using format string 'pszFormat'
	  void Format( PCXSTR pszFormat, ... );

	  // Append formatted data using format string 'pszFormat'
	  void AppendFormat( PCXSTR pszFormat, ... );

	  void AppendFormatV( PCXSTR pszFormat, va_list args )
	  {
		  int nCurrentLength = this->GetLength();
		  int nAppendLength = StringTraits::GetFormattedLength( pszFormat, args );
		  PXSTR pszBuffer = this->GetBuffer( nCurrentLength+nAppendLength );
		  StringTraits::Format( pszBuffer+nCurrentLength, 
			  nAppendLength+1, pszFormat, args );
		  this->ReleaseBufferSetLength( nCurrentLength+nAppendLength );
	  }

	  void FormatV( PCXSTR pszFormat, va_list args )
	  {
		  int nLength = StringTraits::GetFormattedLength( pszFormat, args );
		  PXSTR pszBuffer = this->GetBuffer( nLength );
		  StringTraits::Format( pszBuffer, nLength+1, pszFormat, args );
		  this->ReleaseBufferSetLength( nLength );
	  }

	  // OLE BSTR support

	  // Allocate a BSTR containing a copy of the string
	  BSTR AllocSysString() const
	  {
		  BSTR bstrResult = StringTraits::AllocSysString( this->GetString(), this->GetLength());
		  return bstrResult;
	  }

	  BSTR SetSysString( BSTR* pbstr ) const
	  {
		  StringTraits::ReAllocSysString( this->GetString(), pbstr, this->GetLength());
		  return *pbstr;
	  }

	  // Set the string to the value of environment variable 'pszVar'
	  BOOL GetEnvironmentVariable( PCXSTR pszVar )
	  {
		  ULONG nLength = StringTraits::GetEnvironmentVariable( pszVar, NULL, 0 );
		  BOOL bRetVal = FALSE;

		  if ( nLength == 0 )
		  {
			  this->Empty();
		  }
		  else
		  {
			  PXSTR pszBuffer = this->GetBuffer( nLength );
			  StringTraits::GetEnvironmentVariable( pszVar, pszBuffer, nLength );
			  this->ReleaseBuffer();
			  bRetVal = TRUE;
		  }

		  return bRetVal;
	  }

	  // Load the string from resource 'nID'
	  BOOL LoadString( UINT nID )
	  {
		  HINSTANCE hInst = StringTraits::FindStringResourceInstance( nID );
		  if ( hInst == NULL )
			  return FALSE;

		  return LoadString( hInst, nID );
	  }

	  friend CMStringT __stdcall operator+( const CMStringT& str1, const CMStringT& str2 )
	  {
		  CMStringT strResult;

		  Concatenate( strResult, str1, str1.GetLength(), str2, str2.GetLength());

		  return strResult;
	  }

	  friend CMStringT __stdcall operator+( const CMStringT& str1, PCXSTR psz2 )
	  {
		  CMStringT strResult;

		  Concatenate( strResult, str1, str1.GetLength(), psz2, StringLength( psz2 ));

		  return strResult;
	  }

	  friend CMStringT __stdcall operator+( PCXSTR psz1, const CMStringT& str2 )
	  {
		  CMStringT strResult;

		  Concatenate( strResult, psz1, StringLength( psz1 ), str2, str2.GetLength());

		  return strResult;
	  }

	  friend CMStringT __stdcall operator+( const CMStringT& str1, wchar_t ch2 )
	  {
		  CMStringT strResult;
		  XCHAR chTemp = XCHAR( ch2 );

		  Concatenate( strResult, str1, str1.GetLength(), &chTemp, 1 );

		  return strResult;
	  }

	  friend CMStringT __stdcall operator+( const CMStringT& str1, char ch2 )
	  {
		  CMStringT strResult;
		  XCHAR chTemp = XCHAR( ch2 );

		  Concatenate( strResult, str1, str1.GetLength(), &chTemp, 1 );

		  return strResult;
	  }

	  friend CMStringT __stdcall operator+( wchar_t ch1, const CMStringT& str2 )
	  {
		  CMStringT strResult;
		  XCHAR chTemp = XCHAR( ch1 );

		  Concatenate( strResult, &chTemp, 1, str2, str2.GetLength());

		  return strResult;
	  }

	  friend CMStringT __stdcall operator+( char ch1, const CMStringT& str2 )
	  {
		  CMStringT strResult;
		  XCHAR chTemp = XCHAR( ch1 );

		  Concatenate( strResult, &chTemp, 1, str2, str2.GetLength());

		  return strResult;
	  }

	  friend bool __stdcall operator==( const CMStringT& str1, const CMStringT& str2 )
	  {
		  return str1.Compare( str2 ) == 0;
	  }

	  friend bool __stdcall operator==( const CMStringT& str1, PCXSTR psz2 )
	  {
		  return str1.Compare( psz2 ) == 0;
	  }

	  friend bool __stdcall operator==( PCXSTR psz1, const CMStringT& str2 )
	  {
		  return str2.Compare( psz1 ) == 0;
	  }

	  friend bool __stdcall operator==( const CMStringT& str1, PCYSTR psz2 )
	  {
		  CMStringT str2( psz2 );

		  return str1 == str2;
	  }

	  friend bool __stdcall operator==( PCYSTR psz1, const CMStringT& str2 )
	  {
		  CMStringT str1( psz1 );

		  return str1 == str2;
	  }

	  friend bool __stdcall operator!=( const CMStringT& str1, const CMStringT& str2 )
	  {
		  return str1.Compare( str2 ) != 0;
	  }

	  friend bool __stdcall operator!=( const CMStringT& str1, PCXSTR psz2 )
	  {
		  return str1.Compare( psz2 ) != 0;
	  }

	  friend bool __stdcall operator!=( PCXSTR psz1, const CMStringT& str2 )
	  {
		  return str2.Compare( psz1 ) != 0;
	  }

	  friend bool __stdcall operator!=( const CMStringT& str1, PCYSTR psz2 )
	  {
		  CMStringT str2( psz2 );

		  return str1 != str2;
	  }

	  friend bool __stdcall operator!=( PCYSTR psz1, const CMStringT& str2 )
	  {
		  CMStringT str1( psz1 );

		  return str1 != str2;
	  }

	  friend bool __stdcall operator<( const CMStringT& str1, const CMStringT& str2 )
	  {
		  return str1.Compare( str2 ) < 0;
	  }

	  friend bool __stdcall operator<( const CMStringT& str1, PCXSTR psz2 )
	  {
		  return str1.Compare( psz2 ) < 0;
	  }

	  friend bool __stdcall operator<( PCXSTR psz1, const CMStringT& str2 )
	  {
		  return str2.Compare( psz1 ) > 0;
	  }

	  friend bool __stdcall operator>( const CMStringT& str1, const CMStringT& str2 )
	  {
		  return str1.Compare( str2 ) > 0;
	  }

	  friend bool __stdcall operator>( const CMStringT& str1, PCXSTR psz2 )
	  {
		  return str1.Compare( psz2 ) > 0;
	  }

	  friend bool __stdcall operator>( PCXSTR psz1, const CMStringT& str2 )
	  {
		  return str2.Compare( psz1 ) < 0;
	  }

	  friend bool __stdcall operator<=( const CMStringT& str1, const CMStringT& str2 )
	  {
		  return str1.Compare( str2 ) <= 0;
	  }

	  friend bool __stdcall operator<=( const CMStringT& str1, PCXSTR psz2 )
	  {
		  return str1.Compare( psz2 ) <= 0;
	  }

	  friend bool __stdcall operator<=( PCXSTR psz1, const CMStringT& str2 )
	  {
		  return str2.Compare( psz1 ) >= 0;
	  }

	  friend bool __stdcall operator>=( const CMStringT& str1, const CMStringT& str2 )
	  {
		  return str1.Compare( str2 ) >= 0;
	  }

	  friend bool __stdcall operator>=( const CMStringT& str1, PCXSTR psz2 )
	  {
		  return str1.Compare( psz2 ) >= 0;
	  }

	  friend bool __stdcall operator>=( PCXSTR psz1, const CMStringT& str2 )
	  {
		  return str2.Compare( psz1 ) <= 0;
	  }

	  friend bool __stdcall operator==( XCHAR ch1, const CMStringT& str2 )
	  {
		  return (str2.GetLength() == 1) && (str2[0] == ch1);
	  }

	  friend bool __stdcall operator==( const CMStringT& str1, XCHAR ch2 )
	  {
		  return (str1.GetLength() == 1) && (str1[0] == ch2);
	  }

	  friend bool __stdcall operator!=( XCHAR ch1, const CMStringT& str2 )
	  {
		  return (str2.GetLength() != 1) || (str2[0] != ch1);
	  }

	  friend bool __stdcall operator!=( const CMStringT& str1, XCHAR ch2 )
	  {
		  return (str1.GetLength() != 1) || (str1[0] != ch2);
	  }
};

template< typename BaseType, class StringTraits >
inline void CMStringT<BaseType, StringTraits>::Format(PCXSTR pszFormat, ... )
{
	va_list argList;
	va_start( argList, pszFormat );
	FormatV( pszFormat, argList );
	va_end( argList );
}

template< typename BaseType, class StringTraits >
inline void CMStringT<BaseType, StringTraits>::AppendFormat(PCXSTR pszFormat, ... )
{
	va_list argList;
	va_start( argList, pszFormat );
	AppendFormatV( pszFormat, argList );
	va_end( argList );
}

typedef CMStringT< wchar_t, ChTraitsCRT< wchar_t > > CMStringW;
typedef CMStringT< char, ChTraitsCRT< char > > CMStringA;
typedef CMStringT< TCHAR, ChTraitsCRT< TCHAR > > CMString;