
/*
  File: include/basic/charstring.h
  Description: ASCII/Unicode string class.
  Part of the Mantis project
  (c) 2002 Lycos Inc.

  This file is distributed for the sole purpose of allowing you to write
  software to interface with Sonique 2 and other Mantis programs. It is
  not to be modified or used for any other purpose without written
  authorization.

  If you redistribute this file, it must be in the original package
  complete with all other files provided, including the usage agreement.

  Do not remove this notice.
*/

#ifndef _CHARSTRING_H_
#define _CHARSTRING_H_

#ifdef DEBUG_INCLUDE_ORDER
#pragma message("Including " __FILE__ "\n" )
#endif

#if _MSC_VER > 1000
#pragma once
#endif


#pragma warning(push)
#pragma warning( disable : 4127 )


#include <string.h>

#ifdef _LINUX
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <wchar.h>
#include <wctype.h>
#include <netinet/in.h>
#endif  

#include "typesport.h"
#include "threadport.h"
#include "stdlib.h"
#include "print.h"
#include "Array.h"


inline bool isspacing(char c)
{
	return c==' '||c=='\t';
}
inline bool isspacing(wchar_t c)
{
	return c==L' '||c==L'\t';
}
inline bool isnewline(char c)
{
	return c=='\r'||c=='\n';
}
inline bool isnewline(wchar_t c)
{
	return c==L'\r'||c==L'\n';
}
inline bool iswhitespace(char c)
{
	return isspacing(c) || isnewline(c);
}
inline bool iswhitespace(wchar_t c)
{
	return isspacing(c) || isnewline(c);
}
template <class CHARTYPE>
int32 hexdignum(CHARTYPE Char)
{
	if (Char >= CONSTCHAR('0', CHARTYPE) && Char <= CONSTCHAR('9', CHARTYPE))
		return Char - CONSTCHAR('0', CHARTYPE);
	else if (Char >= CONSTCHAR('A', CHARTYPE) && Char <= CONSTCHAR('F', CHARTYPE))
		return Char - CONSTCHAR('A', CHARTYPE)+10;
	else if (Char >= CONSTCHAR('a', CHARTYPE) && Char <= CONSTCHAR('f', CHARTYPE))
		return Char - CONSTCHAR('a', CHARTYPE)+10;
	else
		return -1;
}



int32 __stdcall TStringData_AllocateString( void** ppData, uint32 nNewByteSize );

class TStringData
{
public:
	int32	nRefCount;
	int32	nLength;
	int32	nAllocated;
	int32 (__stdcall *AllocateString)( void** ppData, uint32 nNewSize ); // pData as in the TString pData member
};


template <class CHARTYPE>
class Parser;

template< class TYPE >
class TString  
{
protected:
	TYPE*			pData;
//	int				lConstSize;
#define NSIZE()			(pData?TD()->nLength:0)
#define NALLOCATED()	(pData?TD()->nAllocated:0)

	void Reset(){
//		lConstSize = 0;
		pData	= NULL;
	}

protected:
	__forceinline TStringData* TD() const
	{
		ASSERT(pData);
		return (TStringData*)(((char*)pData)-sizeof(TStringData));
	}
	__forceinline static TStringData* TD(void* data) 
	{
		ASSERT(data);
		return (TStringData*)(((char*)data)-sizeof(TStringData));
	}
public:
	

	template<class OTHERTYPE>
	TString<TYPE>& Copy( const OTHERTYPE* pText, int32 nLen = -1, bool Replace = true, int32 (__stdcall *AllocateString)( void** ppData, uint32 nNewSize ) = NULL )
	{
		if( pText )
		{
			int32 oldSize;
			const OTHERTYPE* ptr = pText;

			//Clip len to actual length of string - compute if nLen == -1
			if( nLen == -1 )
			{
				int32 len = 0;
				while( len != nLen && *ptr  /* != CONSTCHAR('\0',OTHERTYPE)*/ )
				{
					len++;
					ptr++;
				}
				nLen = len;
			}

			if( Replace || !pData )
				oldSize = 0;
			else
#ifdef _DEBUG
			    oldSize = Len();
#else
                oldSize = NSIZE()-1;
#endif

//			if( nLen+oldSize+1 > NSIZE() )
//			{
			if( !AllocateString )
				AllocateString = TStringData_AllocateString;
			SetBufferSize( nLen+oldSize+1, AllocateString );
//			} else {
//				PrepareForWrite();
//				TD(pData)->nLength = nLen+oldSize+1;
//			}
	
			if( sizeof(TYPE)==sizeof(OTHERTYPE)) // if types are the same
			{
				if( sizeof( TYPE ) == sizeof( wchar_t ) )
					wcsncpy( (wchar_t*) pData + oldSize, (const wchar_t*) pText, nLen );
				else
					strncpy( (char*)	pData + oldSize, (const char*)	pText, nLen );
			}
			else
			{
				if( sizeof(TYPE)		== sizeof( wchar_t) &&
					sizeof(OTHERTYPE)	== sizeof( char)
				) 
					mbstowcs((wchar_t*)pData + oldSize, (const char*)pText, nLen);
				else if ( sizeof(OTHERTYPE)	== sizeof( wchar_t) &&
						  sizeof(TYPE)		== sizeof( char)
						 )
					wcstombs((char*)pData + oldSize, (const wchar_t*)pText, nLen);
				else
					ASSERT(0);// what kind of types are you using? (should only be wchar_t and char)
			}

			pData[nLen+oldSize] = CONSTCHAR('\0',TYPE);
		} else {
			if( Replace )
				Free();
		}
		return *this;
	}
	template<class OTHERTYPE>
	TString<TYPE>& Copy( const TString<OTHERTYPE>& String, int32 nLen = -1, bool Replace = true )
	{
		return Copy((const OTHERTYPE*)String, nLen==-1?String.Len():nLen, Replace);
	}
	template<class OTHERTYPE>
	TString<TYPE>& Cat( const TString<OTHERTYPE>& String, int32 nLen = -1 )
	{
		return Cat((const OTHERTYPE*)String, nLen==-1?String.Len():nLen);
	}

	__forceinline uint32 Len() const
	{
#ifdef _DEBUG
		if( pData )
		{
			if( sizeof(TYPE) == sizeof(char) )
			{
				if( NSIZE()-1 != (int32)strlen((char*)pData) )
				{
					TRACE3("Character string '%s' reports length %d but is length %d.\n", (const char*)pData, NSIZE()-1, (int32)strlen((char*)pData));
					ASSERT(0);
				}
			} else {
				if( NSIZE()-1 != (int32)wcslen((wchar_t*)pData) )
				{
					TRACE3("Wide character string '%s' reports length %d but is length %d.\n", (const char*)TString<char>((const wchar_t*)pData), NSIZE()-1, (int32)wcslen((wchar_t*)pData));
					ASSERT(0);
				}
			}				
		}
#endif
		return pData?TD()->nLength-1:0;
	}

	template<class OTHERTYPE>
	TString<TYPE>& Cat( const OTHERTYPE* pText, int32 nLen = -1 )
	{
		return Copy(pText, nLen, false);
	}

	void Free()
	{
		if( pData )
		{
			if( AtomicDecrement(&TD()->nRefCount) == 0)
				Dealloc(pData);
		}
		Reset();
	}

	TYPE* GetAt()                   { PrepareForWrite(); return pData; }
	//operator TYPE*()                { PrepareForWrite(); return pData; }
	operator const TYPE*()			{ return pData; }
	operator const TYPE*() const	{ return pData; }
	TYPE& operator [] ( size_t index ) const { ASSERT(index >= 0 && index <= Len()); return pData[index]; }
	TYPE& operator [] ( int index ) const { ASSERT(index >= 0 && index <= (int)Len()); return pData[index]; }
	TYPE& operator [] ( uint32 index ) const { ASSERT(/*index >= 0 && */index <= Len()); return pData[index]; }
	TYPE& operator [] ( int32 index ) const { ASSERT(index >= 0 && index <= (int32)Len()); return pData[index]; }
	//operator bool() const			{ return pData != NULL; }
	bool operator !() const			{ return pData == NULL; }
	//operator void*() const			{ return pData; }
	
	template<class OTHERTYPE>
	TString<TYPE>& operator= ( const TString<OTHERTYPE>& other )
	{
		Copy( other, other.Len() );
		return *this;
	}

	TString<TYPE>& operator= ( const TString<TYPE>& other )
	{
		Free();
		if( other.pData )
		{
			AtomicIncrement(&other.TD()->nRefCount);
			pData		= (TYPE*)other.pData;
		} else {
			Copy(other.pData, other.Len());
		}
		return *this;
	}

	template< class OTHERTYPE>
	TString<TYPE>& operator= ( const OTHERTYPE* pText )
	{
		return Copy( pText );
	}

	TString<TYPE>& operator= ( TYPE* pText )
	{
		return Copy( pText );
	}

	TString<TYPE>& operator= (const void* Null)	{ ASSERT(Null==NULL); Free(); return *this; }


	TString& operator= ( const TYPE* pText )
	{
		return Copy( pText );
	}

	template< class OTHERTYPE>
	TString<TYPE>& operator+= ( const OTHERTYPE* pText )
	{
		if( sizeof(OTHERTYPE) == sizeof(TYPE) )
			return Cat( (const TYPE*) pText );
		else
			return Cat( TString<TYPE>(pText) ); // convert too
	}
	
	template< class OTHERTYPE>
	TString<TYPE>& operator+= ( const TString<OTHERTYPE>& other )
	{
		if( sizeof(OTHERTYPE) == sizeof(TYPE) )
			return Cat( (TString<TYPE>&) other );
		else
			return Cat( TString<TYPE>(other) ); // convert too
	}

	
	template< class OTHERTYPE>
	TString<TYPE> operator+ ( const OTHERTYPE* pText ) const
	{
		TString<TYPE> TS(*this);

		if( sizeof(OTHERTYPE) == sizeof(TYPE) )
			return TS.Cat( (const TYPE*) pText );
		else
			return TS.Cat( TString<TYPE>(pText) ); // convert too

		return TS;
	}

	template< class OTHERTYPE>
	TString<TYPE> operator+ ( const TString<OTHERTYPE>& other ) const
	{
		TString<TYPE> TS(*this);

		if( sizeof(OTHERTYPE) == sizeof(TYPE) )
			return TS.Cat( (TString<TYPE>&) other );
		else
			return TS.Cat( TString<TYPE>(other) ); // convert too

		return TS;
	}

	unsigned int GetBufferSize() const { return NSIZE(); }

	TString(){ Reset(); }
	
	template<class OTHERTYPE>
	TString( const TString<OTHERTYPE>& other, int nLen = -1 )
	{
		Reset();
		Copy( (const OTHERTYPE*)other, nLen==-1?other.Len():nLen );
	}

	TString( const TString<TYPE>& other )
	{
		if( other.pData  )
		{
			AtomicIncrement(&other.TD()->nRefCount);
			pData		= (TYPE*)other.pData;
		} else {
			Reset();
			Copy(other.pData, other.Len());
		}
	}

	static TString<TYPE> StringFromInt(int32 nInteger)
	{
		TString<TYPE> newstr;
		newstr.print(CONSTSTR("%d",TYPE), nInteger);
		return newstr;
	}
	static TString<TYPE> StringFromUInt(uint32 nInteger)
	{
		TString<TYPE> newstr;
		newstr.print(CONSTSTR("%u",TYPE), nInteger);
		return newstr;
	}
	static TString<TYPE> StringFromHexInt(uint32 nInteger)
	{
		TString<TYPE> newstr;
		newstr.print(CONSTSTR("%#08x",TYPE), nInteger);
		return newstr;
	}
	static TString<TYPE> StringFromDouble(double lfReal)
	{
		TString<TYPE> newstr;
		newstr.print(CONSTSTR("%g",TYPE), lfReal);
		return newstr;
	}
	static TString<TYPE> PrintString( const TYPE* wcFormat, ... )
	{
		va_list args;

		va_start( args, wcFormat );
		TString<TYPE> newstr;
		newstr.vprint( wcFormat, args );
		va_end( args );

		return newstr;
	}

	template<class OTHERTYPE >
	TString( const OTHERTYPE* p1,		const OTHERTYPE* p2, 
			 const OTHERTYPE* p3=NULL,	const OTHERTYPE* p4=NULL, 
			 const OTHERTYPE* p5=NULL,	const OTHERTYPE* p6=NULL,
			 const OTHERTYPE* p7=NULL,	const OTHERTYPE* p8=NULL )
	{

		Reset();
		Copy( (const OTHERTYPE*) p1 );
		Cat(  (const OTHERTYPE*) p2 );
		Cat(  (const OTHERTYPE*) p3 );
		Cat(  (const OTHERTYPE*) p4 );
		Cat(  (const OTHERTYPE*) p5 );
		Cat(  (const OTHERTYPE*) p6 );
		Cat(  (const OTHERTYPE*) p7 );
		Cat(  (const OTHERTYPE*) p8 );
	}

	TString( const void* Null )
	{
		ASSERT(Null == NULL);
		Reset();
	}

	template<class OTHERTYPE>
	TString( const OTHERTYPE* pText, int32 nLen = -1 )
	{
		Reset();
 		Copy( (const OTHERTYPE*) pText, nLen );
	}

	~TString()
	{
		Free();
	}


	uint32 GetInt( int32& toHere ) const
	{

		if( Len() )
		{
			TYPE* pRet;

			if( sizeof( TYPE ) == sizeof( wchar_t ) )
			{
				if(!CompareTo( L"0x", true, 2))
					toHere  = (int32) wcstoul((const wchar_t*)pData, (wchar_t**)&pRet, 0x10 );
				else
					toHere  = (int32) wcstol((const wchar_t*)pData, (wchar_t**)&pRet, 10 );
			}
			else
			{
				if(!CompareTo("0x", true, 2))
					toHere  = (int32) strtoul((const char*)pData, (char**)&pRet, 0x10 );
				else
					toHere  = (int32) strtol((const char*)pData, (char**)&pRet, 10 );
			}

			if( pData == pRet )
				return 0;

			return (TYPE*)pRet-(TYPE*)pData;
		}
		return 0;
	}
	void SetInt( int32 fromHere )
	{
		print(CONSTSTR("%d",TYPE), fromHere);
	}

	uint32 GetUInt( uint32& toHere ) const
	{

		if( Len() )
		{

			TYPE* pRet;

			if( sizeof( TYPE ) == sizeof( wchar_t ) )
			{
				if(!CompareTo( L"0x", true, 2))
					toHere  = (uint32) wcstoul((const wchar_t*)pData, (wchar_t**)&pRet, 0x10 );
				else
					toHere  = (uint32) wcstoul((const wchar_t*)pData, (wchar_t**)&pRet, 10 );
			}
			else
			{
				if(!CompareTo("0x", true, 2))
					toHere  = (uint32) strtoul((const char*)pData, (char**)&pRet, 0x10 );
				else
					toHere  = (uint32) strtoul((const char*)pData, (char**)&pRet, 10 );
			}
			if( pData == pRet )
				return 0;

			return (TYPE*)pRet-(TYPE*)pData;
		}
		return 0;
	}
	void SetUInt( int32 fromHere )
	{
		print(CONSTSTR("%u",TYPE), fromHere);
	}

	uint32 GetBool( bool& toHere ) const
	{
		if( Len() )
		{
			if( !CompareTo("0") || !CompareTo("false") )
				toHere = false;
			else
				toHere = true;

			return Len();
		}
		return 0;
	}
	void SetBool( bool fromHere )
	{
		if( fromHere )
			Copy(CONSTSTR("true",TYPE));
		else
			Copy(CONSTSTR("false",TYPE));
	}

	uint32 GetDouble( double& toHere ) const
	{
		if( Len() )
		{
			TYPE* pRet;

			if( sizeof( TYPE ) == sizeof( wchar_t ) )
			{
				if(!CompareTo(L"0x", true, 2))
					toHere  = (double) wcstoul((const wchar_t*)pData, (wchar_t**)&pRet, 0x10 );
				else
					toHere = wcstod((const wchar_t*)pData,(wchar_t**) &pRet);

			}
			else
			{
				if(!CompareTo("0x", true, 2))
					toHere  = (double) strtoul((const char*)pData, (char**)&pRet, 0x10 );
				else
					toHere = strtod((const char*)pData,(char**) &pRet);
			}

			if( pData == pRet )
				return 0;

			return (TYPE*)pRet-(TYPE*)pData;
		}
		return 0;
	}
	void SetDouble( double fromHere )
	{
		print(CONSTSTR("%lf",TYPE), fromHere);
	}


	bool IsNonWhiteSpace() const
	{
		if( pData )
		{
			TYPE* ptr = pData;

			while( *ptr && iswhitespace( *ptr ) )
				ptr++;

			if( *ptr )
				return true;
		}
		return false;
	}

	bool ContainsChar(TYPE Char) const
	{
		TYPE* String = pData;

		if( !String )
			return false;

		while (*String != CONSTCHAR('\0',TYPE) && *String != Char)
			String++;
		return (*String) != CONSTCHAR('\0',TYPE);
	}
	int32 Find(TYPE Char, int32 Start = 0) const
	{
		TYPE* String = pData;

		if( !String )
			return false;

		String += Start;

		while (*String != CONSTCHAR('\0',TYPE) && *String != Char)
			String++;
		if( (*String) != CONSTCHAR('\0',TYPE) )
			return String-pData;
		else
			return -1;
	}

	template <class OTHERTYPE>
	int32 CompareTo(const OTHERTYPE* other, bool CaseSensitive = false, int32 nLen = -1) const
	{
		return Compare(pData, other, CaseSensitive, nLen);
	}
	template <class OTHERTYPE>
	int32 CompareTo(const TString<OTHERTYPE>& other, bool CaseSensitive = false, int32 nLen = -1) const
	{
		return Compare(pData, (const OTHERTYPE*)other, CaseSensitive, nLen);
	}
	template <class OTHERTYPE>
	int32 CompareToWildcard(const OTHERTYPE* other, bool CaseSensitive = false, int32 nLen = -1) const
	{
		return CompareWildcard(pData, other, CaseSensitive, nLen);
	}
	template <class OTHERTYPE>
	int32 CompareToWildcard(const TString<OTHERTYPE>& other, bool CaseSensitive = false, int32 nLen = -1) const
	{
		return CompareWildcard(pData, (const OTHERTYPE*)other, CaseSensitive, nLen);
	}

	template <class OTHERTYPE>
	static int32 Compare(const TString<TYPE>& string, const TString<OTHERTYPE>& wildcard, bool CaseSensitive = false, int32 nLen = -1)
	{
		return Compare((const TYPE*)string, (const OTHERTYPE*)wildcard, CaseSensitive, nLen);
	}
	template <class OTHERTYPE>
	static int32 CompareWildcard(const TString<TYPE>& string, const TString<OTHERTYPE>& wildcard, bool CaseSensitive = false, int32 nLen = -1)
	{
		return CompareWildcard((const TYPE*)string, (const OTHERTYPE*)wildcard, CaseSensitive, nLen);
	}
	template <class OTHERTYPE>
	static int32 CompareWildcard(const TYPE* string, const TString<OTHERTYPE>& wildcard, bool CaseSensitive = false, int32 nLen = -1)
	{
		return CompareWildcard(string, (const OTHERTYPE*)wildcard, CaseSensitive, nLen);
	}
	template <class OTHERTYPE>
	static int32 CompareWildcard(const TString<TYPE>& string, const OTHERTYPE* wildcard, bool CaseSensitive = false, int32 nLen = -1)
	{
		return CompareWildcard((const TYPE*)string, wildcard, CaseSensitive, nLen);
	}

	template <class OTHERTYPE>
	static int32 Compare(const TYPE* first, const OTHERTYPE* second, bool CaseSensitive = false, int32 nLen = -1)
	{
		if (sizeof(TYPE) == sizeof(OTHERTYPE))
		{
			if ((OTHERTYPE*)first == second) return 0;
			if (!first) return 1;
			if (!second) return -1;

			if (sizeof(TYPE) == sizeof(char))
			{
				if( CaseSensitive )
				{
					if( nLen == -1 )
						return strcmp((const char*)first, (const char*)second);
					else
						return strncmp((const char*)first, (const char*)second, nLen);
				} else {
					if( nLen == -1 )
						return stricmp((const char*)first, (const char*)second);
					else
						return strnicmp((const char*)first, (const char*)second, nLen);
				}
			} else if (sizeof(TYPE) == sizeof(wchar_t)) {
				if( CaseSensitive )
				{
					if( nLen == -1 )
						return wcscmp((const wchar_t*)first, (const wchar_t*)second);
					else
						return wcsncmp((const wchar_t*)first, (const wchar_t*)second, nLen);
				} else {
					if( nLen == -1 )
						return wcsicmp((const wchar_t*)first, (const wchar_t*)second);
					else
						return wcsnicmp((const wchar_t*)first, (const wchar_t*)second, nLen);
				}
			} else {
				ASSERT(!"what kind of whacky character format are you using?!@");
			}
		} else {
			int32 result;

			if (sizeof(OTHERTYPE) == sizeof(char))
			{
				return TString<OTHERTYPE>::Compare(second, first, CaseSensitive, nLen);
			} else {
				ASSERT(sizeof(OTHERTYPE) == sizeof(wchar_t) && sizeof(TYPE) == sizeof(char));

				
				result = Compare(first, TString<char>(second), CaseSensitive, nLen);
				
			}
			return result;
		}

		return 0;
	}
	template <class OTHERTYPE>
	static int32 CompareWildcard(const TYPE* string, const OTHERTYPE* wildcard, bool CaseSensitive = false, int32 nLen = -1)
	{
		if (sizeof(TYPE) == sizeof(OTHERTYPE))
		{
			if ((OTHERTYPE*)string == wildcard) return 0;
			if (!string) return 1;
			if (!wildcard) return -1;

			if( CaseSensitive )
				return Parser<TYPE>::strwildcmp((const TYPE*)wildcard, (const TYPE*)string, nLen);
			else
				return Parser<TYPE>::striwildcmp((const TYPE*)wildcard, (const TYPE*)string, nLen);
		} else {
			int32 result;

			if (sizeof(OTHERTYPE) == sizeof(char))
			{
				result = TString<char>::CompareWildcard((const char*)TString<char>(string), (const char*)wildcard, CaseSensitive, nLen);
			} else {
				ASSERT(sizeof(OTHERTYPE) == sizeof(wchar_t) && sizeof(TYPE) == sizeof(char));

				result = TString<char>::CompareWildcard((const char*)string, (const char*)TString<char>(wildcard), CaseSensitive, nLen);
			}
			return result;
		}

		return 0;
	}

	int32 SubStrIndex( const TString<TYPE>& Str, bool CaseSensitive = false, int32 Start = 0 ) const
	{
		const TYPE* cur, * start, * other;
		TYPE c;
		int32 len;

        start = cur = pData;
        other = Str;
		if( cur )
		{
			c = ((const TYPE*)Str)[0];
    		len = Str.Len();
            if( Start > len )
                return -1;
            cur += Start;
            if( sizeof(TYPE) == sizeof(char) )
            {
                if( !CaseSensitive )
                {
                    c = tolower(c);
	    	        while( *cur )
		            {
			            if( tolower(*cur) == c )
			            {
				            if( !strnicmp((const char*)cur, (const char*)other, len) )
					            return cur-start;
			            }
			            cur++;
		            }
                } else {
	    	        while( *cur )
		            {
			            if( *cur == c )
			            {
				            if( !strncmp((const char*)cur, (const char*)other, len) )
					            return cur-start;
			            }
			            cur++;
		            }
                }
            } else if( sizeof(TYPE) == sizeof(wchar_t) ) {
                if( !CaseSensitive )
                {
                    c = (TYPE)towlower(c);
	    	        while( *cur )
		            {
			            if( towlower(*cur) == c )
			            {
				            if( !wcsnicmp((const wchar_t*)cur, (const wchar_t*)other, len) )
					            return cur-start;
			            }
			            cur++;
		            }
                } else {
	    	        while( *cur )
		            {
			            if( *cur == c )
			            {
				            if( !wcsncmp((const wchar_t*)cur, (const wchar_t*)other, len) )
					            return cur-start;
			            }
			            cur++;
		            }
                }
            }
		}

		return -1;
	}
    bool ContainsSubStr( const TString<TYPE>& Str, bool CaseSensitive = false ) const
    {
        return SubStrIndex( Str, CaseSensitive ) != -1;
    }

	TYPE	Tail() const
	{
		uint32 nLen = Len();
		if( nLen )
		{
			return pData[nLen-1];
		}
		ASSERT(0); // no string
		return 0;
	}

	TString<TYPE> SubString(int32 Start, int32 Length = -1) const
	{
		if( Start == 0 && Length == -1 )
			return *this;
		if( Start < 0)
			Start = Start + Len() + 1;

		if( Length < 0 )
			Length = Length + Len() - Start + 1;

		return TString<TYPE>(pData+Start, Length);
	}

	int32 SubStrPos(const TYPE* String) const	// similar to strstr
	{
		if( sizeof(TYPE) == sizeof(char) )
		{
			char* pos = strstr((char*)pData, (const char*)String);
			if( pos )
				return pos - (char*)pData;
			else
				return -1;
		} else if( sizeof(TYPE) == sizeof(wchar_t) ) {
			wchar_t* pos = wcsstr((wchar_t*)pData, (const wchar_t*)String);
			if( pos )
				return pos - (wchar_t*)pData;
			else
				return -1;
		} else {
			ASSERT(0);
			return -1;
		}
	}

	TString<TYPE>& AppendChars(TYPE chr, int32 Num)
	{
		int32 oldSize;
		TYPE* Dest;

		oldSize = Len();

		if( Num+oldSize+1 > NSIZE() )
		{
			SetBufferSize( Num+oldSize+1 );
		} else {
			TD()->nLength = Num+oldSize+1;
		}

		Dest = pData+oldSize;
		while( Num-- )
		{
			*Dest = chr;
			Dest++;
		}
		*Dest = CONSTCHAR('\0',TYPE);

		return *this;
	}

	TString<TYPE>& ToLower()
	{
		if( pData )
		{
			PrepareForWrite();
			if( sizeof(TYPE) == sizeof(char) )
			{
				TYPE* Ptr = pData;
				while( *Ptr )
				{
					*Ptr = (TYPE)tolower(*Ptr);
					Ptr++;
				}
			} else if( sizeof(TYPE) == sizeof(wchar_t) ) {
				TYPE* Ptr = pData;
				while( *Ptr )
				{
					*Ptr = (TYPE)towlower(*Ptr);
					Ptr++;
				}
			} else {
				ASSERT(!"uh...");
			}
		}
		return *this;
	}
	TString<TYPE>& ToUpper()
	{
		if( pData )
		{
			PrepareForWrite();
			if( sizeof(TYPE) == sizeof(char) )
			{
				TYPE* Ptr = pData;
				while( *Ptr )
				{
					*Ptr = (TYPE)toupper(*Ptr);
					Ptr++;
				}
			} else if( sizeof(TYPE) == sizeof(wchar_t) ) {
				TYPE* Ptr = pData;
				while( *Ptr )
				{
					*Ptr = (TYPE)towupper(*Ptr);
					Ptr++;
				}
			} else {
				ASSERT(!"uh...");
			}
		}
		return *this;
	}
	TString<TYPE>& Capitalise()
	{
		if( pData )
		{
			PrepareForWrite();
			if( sizeof(TYPE) == sizeof(char) )
			{
				*pData = (TYPE)toupper(*pData);
			} else if( sizeof(TYPE) == sizeof(wchar_t) ) {
				*pData = (TYPE)towupper(*pData);
			} else {
				ASSERT(!"uh...");
			}
		}
		return *this;
	}

	int32 dovprint( const TYPE* wcFormat, int32 InsertPoint, int32 ReplaceLen, va_list args )
	{
		int len;

		//ASSERT( sizeof(TYPE) == sizeof(wchar_t) );

		len = vstprint( (TYPE*)NULL, 0, (TYPE*)wcFormat, args );
		if( len >= 0 )
		{
			int32 oldSize;

			oldSize = NSIZE()-1;
			if( oldSize < 0 )
				oldSize = 0;
			if( InsertPoint == -1 ) // -1 means at the end
				InsertPoint = oldSize;
			if( ReplaceLen == -1 )  // -1 means the whole thing.
				ReplaceLen = oldSize;
			ASSERT(InsertPoint >= 0 && ReplaceLen >= 0 && InsertPoint+ReplaceLen <= oldSize);

			if( len-ReplaceLen < 0 )
				memmove( (TYPE*)pData+InsertPoint+len, (TYPE*)pData+InsertPoint+ReplaceLen, min(oldSize-(InsertPoint+(ReplaceLen-len)), oldSize-(InsertPoint+ReplaceLen))*sizeof(TYPE) );
			SetBufferSize( oldSize+len-ReplaceLen+1 );
			if( len-ReplaceLen > 0 )
				memmove( (TYPE*)pData+InsertPoint+len, (TYPE*)pData+InsertPoint+ReplaceLen, min(oldSize-(InsertPoint+(ReplaceLen-len)), oldSize-(InsertPoint+ReplaceLen))*sizeof(TYPE) );
			VERIFY( (int)vstprint( (TYPE*)pData+InsertPoint, len, (TYPE*)wcFormat, args ) == len );
			if( InsertPoint == oldSize-ReplaceLen )
				((TYPE*)pData)[InsertPoint+len] = CONSTCHAR('\0',TYPE);
		}

		return len;
	}
	// print a string, replacing a substring (starting at Index, length Len)
	int32 vprintr( int32 Index, int32 Len, const TYPE* wcFormat, va_list args )
	{
		return dovprint( wcFormat, Index, Len, args );
	}
	int32 printr( int32 Index, int32 Len, const TYPE* wcFormat, ... )
	{
		size_t len;
		va_list args;

		va_start( args, wcFormat );
		len = vprintr(Index, Len, wcFormat, args);
		va_end( args );

		return len;
	}
	// print a string and insert into this string (at index Index)
	int32 vprinti( int32 Index, const TYPE* wcFormat, va_list args )
	{
		return dovprint( wcFormat, Index, 0, args );
	}
	int32 printi( int32 Index, const TYPE* wcFormat, ... )
	{
		size_t len;
		va_list args;

		va_start( args, wcFormat );
		len = vprinti(Index, wcFormat, args);
		va_end( args );

		return len;
	}
	// print a string and append it to the end of this one
	int32 vprinta( const TYPE* wcFormat, va_list args )
	{
		return dovprint( wcFormat, -1, 0, args );
	}
	int32 printa( const TYPE* wcFormat, ... )
	{
		size_t len;
		va_list args;

		va_start( args, wcFormat );
		len = vprinta(wcFormat, args);
		va_end( args );

		return len;
	}
	// print a string, replacing this entire string.
	int32 vprint( const TYPE* wcFormat, va_list args )
	{
		return dovprint( wcFormat, 0, -1, args );
	}
	int32 print( const TYPE* wcFormat, ... )
	{
		size_t len;
		va_list args;

		va_start( args, wcFormat );
		len = vprint(wcFormat, args);
		va_end( args );

		return len;
	}

	void Allocate( int32 uiSize )
	{
#ifdef _DEBUG
		uint32 OldLen = Len();
#endif
		if( pData &&  TD()->nRefCount > 1 )
		{
			if( !AtomicDecrement(&TD()->nRefCount) )
			{
				Dealloc(pData);
			}
			pData = NULL;
		}
		SetBufferSize( uiSize+1 );
#ifdef _DEBUG
		//stop asserts happening
		for( uint32 i = OldLen; i < (uint32)uiSize; i++ )
			((TYPE*)pData)[i] = CONSTCHAR(' ',TYPE);
#endif
		((TYPE*)pData)[uiSize] = CONSTCHAR('\0',TYPE);
	}
	void SetLength( int32 uiSize )
	{
		SetBufferSize( uiSize+1 );
	}

	bool IsEqualTo( const TYPE* Other, bool bCaseSensitive, uint32 len ) const
	{
		if( Len() != len )
			return false;
		
		if( pData == Other )
			return true;

		if( bCaseSensitive )
			return 0==memcmp( Other, pData, Len()*sizeof(TYPE));
		else
			return 0==CompareTo( Other, false, len );
	}

	bool IsEqualTo( const TString& Other, bool bCaseSensitive ) const
	{
		return IsEqualTo( Other.pData, bCaseSensitive, Other.Len() );
	}

	template <class OTHERTYPE>
	static bool IsEqual(const TString<TYPE>& string, const TString<OTHERTYPE>& other, bool CaseSensitive)
	{
		return !Compare((const TYPE*)string, (const OTHERTYPE*)other, CaseSensitive);
	}

	template <class OTHERTYPE>
	static bool IsEqual(const TYPE* string, const OTHERTYPE* other, bool CaseSensitive)
	{
		return !Compare(string, other, CaseSensitive);
	}

	bool operator ==( const TYPE* Other ) const
	{
		return 0==CompareTo(Other, true);
	}

	bool operator ==( const TString& Other ) const
	{
		return IsEqualTo( Other.pData, true, Other.Len() );
	}

	bool operator >( const TString& Other )
	{
		return CompareTo(Other, true) > 0;
	}


protected:

	static void* Alloc( uint32 size, int32 (__stdcall *AllocateString)( void** ppData, uint32 nNewSize ) = NULL )
	{
		void* result = NULL;
		if( !AllocateString )
			AllocateString = &TStringData_AllocateString;
		if( (*AllocateString)( &result, size*sizeof(TYPE) ) )
		{
			TD(result)->nRefCount		= 1;		//refcount
//			TD(result)->nLength			= size;		//size
			TD(result)->nAllocated		= size;
			TD(result)->AllocateString	= AllocateString; // function
			return (void*)result;
		}
		return NULL;
	}
	static void Dealloc( void* pMe )
	{
		TD(pMe)->AllocateString( &pMe, 0 );
	}
	static bool Realloc( void** pMe,  uint32 newSize, int32 (__stdcall *AllocateString)( void** ppData, uint32 nNewSize ) = NULL )
	{
		if( !*pMe )
		{
			*pMe = Alloc(newSize, AllocateString);
			return *pMe != NULL;
		}

		if( !AllocateString )
			AllocateString = &TStringData_AllocateString;
		if( (*AllocateString)( pMe, newSize*sizeof(TYPE) ) )
		{
//			TD(*pMe)->nLength = newSize;
			TD(*pMe)->nAllocated = newSize;
			return true;
		}
		
		return false;
	}

	void PrepareForWrite()
	{
		SetBufferSize(NSIZE());
	}
	void SetBufferSize( uint32 uiSize, int32 (__stdcall *AllocateString)( void** ppData, uint32 nNewSize ) = NULL ) // does preserve buffer
	{
		if( !pData || TD(pData)->nRefCount == 1 )
		{
			if( uiSize > ((uint32)NALLOCATED()) || uiSize < (((uint32)NALLOCATED())/2) )
			{
				int32 NewSize;
				if( uiSize > ((uint32)NALLOCATED()) )
					NewSize = max(uiSize, (uint32)NALLOCATED()*2);
				else
					NewSize = uiSize;

				Realloc((void**)&pData, NewSize, pData ? TD(pData)->AllocateString : AllocateString);
			}
			TD(pData)->nLength = uiSize;
			pData[uiSize-1] = 0;
		} else {
			//we don't own this buffer, it HAS to be allocated
			void* NewBuffer = Alloc( uiSize, AllocateString );
			memcpy(NewBuffer, pData, max(min(uiSize,(uint32)NSIZE())-1,0)*sizeof(TYPE) );
			TD(NewBuffer)->nLength = uiSize;
			((TYPE*)NewBuffer)[uiSize-1] = 0;

			if( !AtomicDecrement(&TD(pData)->nRefCount) )
			{
				Dealloc(pData);
			}

			pData = (TYPE*)NewBuffer;
		}
	}
};


typedef TString< wchar_t >	WString;
typedef TString< char >		AString;


WString MultibyteToWideString(const char* str);
AString WideToMultibyteString(const wchar_t* str);

template <class CHARTYPE>
TString<CHARTYPE> StripWhiteSpace(const CHARTYPE* str);

template <class CHARTYPE>
TString<CHARTYPE> StripWhiteSpace(const TString<CHARTYPE>& str)
{
    return StripWhiteSpace((const CHARTYPE*)str);
}

// helper functions

int32 NetworkStringToWideString( WString& toHere, const char* pMem, uint32 nChars );

inline int32 NetworkStringLength( const WString& str )
{
	return str.Len()*sizeof(short);
}
inline int32 NetworkStringLength( const wchar_t* str )
{
	return wcslen(str)*sizeof(short);
}

int32 WideStringToNetworkString( char* toHere,const wchar_t* fromHere, uint32 nLen = 0xFFFFFFFF );
int32 ShortStringToWideString( WString& toHere, const short* pMem, uint32 nChars );

inline int32 ShortStringLength( const WString& str )
{
	return str.Len()*sizeof(short);
}
inline int32 ShortStringLength( const wchar_t* str )
{
	return wcslen(str)*sizeof(short);
}

inline int32 WideStringToShortString( short* toHere,const wchar_t* fromHere, uint32 nLen = 0xFFFFFFFF )
{
	ASSERT(toHere);
	
	short* pStart = toHere;
	while( nLen-- != 0 && *fromHere )
		*toHere++ = *fromHere++;

	return toHere - pStart;
}

template <class TYPE> TString<TYPE> URLToASCII(TString<TYPE> Original);

template <class TYPE>
inline TString<TYPE> BinaryToBase64(const uint8* pData, int32 lDataLen);

template <class TYPE>
inline TString<TYPE> BinaryToBase64(const Array<uint8>& Data)
{
	return BinaryToBase64<TYPE>(&Data[0], Data.GetCount());
}
template <class TYPE>
inline TString<TYPE> BinaryToBase64(const AString& Data)
{
	return BinaryToBase64<TYPE>( (uint8*)((const char*)Data), Data.Len()*sizeof(char));
}
template <class TYPE>
inline TString<TYPE> BinaryToBase64(const WString& Data)
{
	return BinaryToBase64<TYPE>( (uint8*)((const wchar_t*)Data), Data.Len()*sizeof(wchar_t));
}

template <class TYPE> Array<uint8> Base64ToBinary(const TString<TYPE>& Str);

template <class TYPE>
inline AString Base64ToAString(TString<TYPE> Str)
{
	Array<uint8> Array;
	Array = Base64ToBinary(Str);
	return AString(&Array[0], Array.GetCount()/sizeof(char));
}
template <class TYPE>
inline WString Base64ToWString(TString<TYPE> Str)
{
	Array<uint8> Array;
	Array = Base64ToBinary(Str);
	return WString(&Array[0], Array.GetCount()/sizeof(wchar_t));
}



template <class CHARTYPE>
class Parser
{
public:
	class ParseChars
	{
	public:
		ParseChars()
		{
		}
		ParseChars(CHARTYPE* _Chars, long _Num)
		{
			Chars = _Chars;
			Num = _Num;
		}
		CHARTYPE* Chars;
		long Num;
	};
	class ParseInfo
	{
	public:
		ParseInfo()
		{
		}
		ParseInfo(ParseChars _LineCommentChars, ParseChars _DelimitChars, ParseChars _StripChars, ParseChars _EndChars, ParseChars _EscapeChars, ParseChars _QuoteChars)
		{
			LineCommentChars = _LineCommentChars;
			DelimitChars = _DelimitChars;
			StripChars = _StripChars;
			EndChars = _EndChars;
			EscapeChars = _EscapeChars;
			QuoteChars = _QuoteChars;
		}
		ParseChars LineCommentChars;
		ParseChars DelimitChars;
		ParseChars StripChars;
		ParseChars EndChars;
		ParseChars EscapeChars;
		ParseChars QuoteChars;
	};

	static ParseInfo GetDefaultParse();
	static ParseInfo PasswdParse();
	static ParseInfo ConfigParse();
	static ParseInfo ArgumentListParse();
	static ParseInfo MiaConfParse();
	static ParseInfo PathParse();
    static ParseInfo CommandLineParse();

	static inline bool CharSetContains(const ParseChars& Set, CHARTYPE c)
	{
	   register CHARTYPE* cur, * end;
	   for (cur = Set.Chars, end = Set.Chars+Set.Num; cur<end; cur++)
		  if (*cur == c) return true;
	   return false;
	}

	static int strwildcmp ( const CHARTYPE *Wildcard, const CHARTYPE *String, int32 nLen = -1 );
	static int striwildcmp ( const CHARTYPE *Wildcard, const CHARTYPE *String, int32 nLen = -1 );
	static uint32 ParseStrings( const CHARTYPE* Input, int32 Len, const ParseInfo& Info, Array< TString<CHARTYPE> >& Result);

};

typedef Parser< wchar_t >	WParser;
typedef Parser< char >		AParser; 


#pragma warning(pop)
#endif 
