/*******************************************************************************
*
*  (C) COPYRIGHT MICROSOFT CORPORATION, 1998
*
*  TITLE:       SIMSTR.H
*
*  VERSION:     1.0
*
*  AUTHOR:      ShaunIv
*
*  DATE:        5/12/1998
*
*  DESCRIPTION: Simple string classes
*
*******************************************************************************/
#ifndef _SIMSTR_H_INCLUDED
#define _SIMSTR_H_INCLUDED

/*
 * Simple string class.
 *
 * Template class:
 *   CSimpleStringBase<T>
 * Implementations:
 *   CSimpleStringBase<wchar_t> CSimpleStringWide
 *   CSimpleStringBase<char> CSimpleStringAnsi
 *   CSimpleString = CSimpleString[Ansi|Wide] depending on UNICODE macro
 * Inline functions:
 *   CSimpleStringAnsi CSimpleStringConvert::AnsiString(T n)
 *   CSimpleStringWide CSimpleStringConvert::WideString(T n)
 *   CSimpleString     CSimpleStringConvert::NaturalString(T n)
 * Macros:
 *   IS_CHAR(T)
 *   IS_WCHAR(T)
 */

#include <windows.h>
#include <stdarg.h>
#include <stdio.h>
#include <tchar.h>

#define IS_CHAR(x)     (sizeof(x)==sizeof(char))
#define IS_WCHAR(x)    (sizeof(x)==sizeof(wchar_t))

#ifndef ARRAYSIZE
#define ARRAYSIZE(x)   (sizeof(x) / sizeof(x[0]))
#endif

template <class T>
class CSimpleStringBase
{
private:
    enum
    {
        m_nDefaultGranularity  = 16,   // Default number of extra characters to allocate when we have to grow
        m_nMaxLoadStringBuffer = 1024, // Maximum length of .RC string
        m_nMaxAutoDataLength   = 128   // Length of non-dynamically allocated string
    };

private:
    T   *m_pstrData;
    T    m_pstrAutoData[m_nMaxAutoDataLength];
    UINT m_nMaxSize;
    UINT m_nGranularity;

private:
    static int Min( int a, int b )
    {
        return((a < b) ? a : b);
    }

public:
    // Replacements (in some cases just wrappers) for strlen, strcpy, ...
    static inline T   *GenericCopy( T *pstrTgtStr, const T *pstrSrcStr );
    static inline T   *GenericCopyLength( T *pstrTgtStr, const T *pstrSrcStr, UINT nSize );
    static inline UINT GenericLength( const T *pstrStr );
    static inline T   *GenericConcatenate( T *pstrTgtStr, const T *pstrSrcStr );
    static inline int  GenericCompare( const T *pstrTgtStr, const T *pstrSrcStr );
    static inline int  GenericCompareNoCase( const T *pstrStrA, const T *pstrStrB );
    static inline int  GenericCompareLength( const T *pstrTgtStr, const T *pstrSrcStr, UINT nLength );
    static inline T   *GenericCharNext( const T *pszStr );

public:
    // Constructors and destructor
    CSimpleStringBase( void );
    CSimpleStringBase( const CSimpleStringBase & );
    CSimpleStringBase( const T *szStr );
    CSimpleStringBase( T ch );
    CSimpleStringBase( UINT nResId, HMODULE hModule );
    virtual ~CSimpleStringBase(void);

    bool EnsureLength( UINT nMaxSize );
    UINT Length(void) const;
    void Concat( const CSimpleStringBase &other );
    int Resize(void);
    UINT Truncate( UINT nLen );
    bool Assign( const T *szStr );
    bool Assign( const CSimpleStringBase & );
    void DeleteStorage(void);
    static inline T *CreateStorage( UINT nCount );
    void Destroy(void);

    CSimpleStringBase &Format( const T *strFmt, ... );
    CSimpleStringBase &Format( int nResId, HINSTANCE hInst, ... );

    // Handy Win32 wrappers
    CSimpleStringBase &GetWindowText( HWND hWnd );
    bool SetWindowText( HWND hWnd );
    bool LoadString( UINT nResId, HMODULE hModule );
    bool Load( HKEY hRegKey, const T *pszValueName, const T *pszDefault=NULL );
    bool Store( HKEY hRegKey, const T *pszValueName, DWORD nType = REG_SZ );
    void SetAt( UINT nIndex, T chValue );
    CSimpleStringBase &operator=( const CSimpleStringBase &other );
    CSimpleStringBase &operator=( const T *other );
    CSimpleStringBase &operator+=( const CSimpleStringBase &other );
    CSimpleStringBase operator+( const CSimpleStringBase &other ) const;
    T &operator[](int index);
    const T &operator[](int index) const;

    CSimpleStringBase ToUpper(void) const;
    CSimpleStringBase ToLower(void) const;

    CSimpleStringBase &MakeUpper(void);
    CSimpleStringBase &MakeLower(void);
    CSimpleStringBase &TrimRight(void);
    CSimpleStringBase &TrimLeft(void);
    CSimpleStringBase &Trim(void);
    CSimpleStringBase &Reverse(void);

    int Find( T cChar ) const;
    int Find( const CSimpleStringBase &other, UINT nStart=0 ) const;
    int ReverseFind( T cChar ) const;
    int ReverseFind( const CSimpleStringBase &other ) const;

    CSimpleStringBase SubStr( int nStart, int nCount=-1 ) const;
    int CompareNoCase( const CSimpleStringBase &other, int nLength=-1 ) const;
    int Compare( const CSimpleStringBase &other, int nLength=-1 ) const;
    bool MatchLastCharacter( T cChar ) const;

    // Some useful inlines
    UINT Granularity( UINT nGranularity )                       { if (nGranularity>0) m_nGranularity = nGranularity;return m_nGranularity;}
    UINT Granularity( void )                          const     { return m_nGranularity;}
    CSimpleStringBase Left( int nCount )              const     { return SubStr( 0, nCount );}
    CSimpleStringBase Right( int nCount )             const     { return SubStr( max(0,(int)Length()-nCount), -1 );}
    bool operator<( const CSimpleStringBase &other )  const     { return(Compare(other) <  0);}
    bool operator<=( const CSimpleStringBase &other ) const     { return(Compare(other) <= 0);}
    bool operator==( const CSimpleStringBase &other ) const     { return(Compare(other) == 0);}
    bool operator!=( const CSimpleStringBase &other ) const     { return(Compare(other) != 0);}
    bool operator>=( const CSimpleStringBase &other ) const     { return(Compare(other) >= 0);}
    bool operator>( const CSimpleStringBase &other )  const     { return(Compare(other) >  0);}
    const T *String(void)                             const     { return m_pstrData;}
    operator const T *(void)                          const     { return String();}
    bool IsValid(void)                                const     { return(NULL != m_pstrData);}
};


template <class T>
inline T *CSimpleStringBase<T>::GenericCopy( T *pszDest, const T *pszSource )
{
    T *pCurr = pszDest;
    while (*pCurr++ = *pszSource++)
        ;
    return(pszDest);
}

template <class T>
inline T *CSimpleStringBase<T>::GenericCharNext( const T *pszStr )
{
    if (IS_CHAR(*pszStr))
        return (T*)CharNextA((LPCSTR)pszStr);
    else if (!*pszStr)
        return (T*)pszStr;
    else return (T*)((LPWSTR)pszStr + 1);
}

template <class T>
inline T *CSimpleStringBase<T>::GenericCopyLength( T *pszDest, const T *source, UINT count )
{
    T *start = pszDest;
    while (count && (*pszDest++ = *source++))
        count--;
    if (count)
        while (--count)
            *pszDest++ = 0;
    return(start);
}

template <class T>
inline UINT CSimpleStringBase<T>::GenericLength( const T *pszString )
{
    const T *eos = pszString;

    while (*eos++)
        ;
    return((UINT)(eos - pszString - 1));
}

template <class T>
inline T*CSimpleStringBase<T>::GenericConcatenate( T *pszDest, const T *pszSource )
{
    T *pCurr = pszDest;

    while (*pCurr)
        pCurr++;

    while (*pCurr++ = *pszSource++)
        ;

    return( pszDest );
}

template <class T>
inline int CSimpleStringBase<T>::GenericCompare( const T *pszSource, const T *pszDest )
{
#if defined(DBG) && !defined(UNICODE) && !defined(_UNICODE)
    if (sizeof(T) == sizeof(wchar_t))
    {
        OutputDebugString(TEXT("CompareStringW is not supported under win9x, so this call is going to fail!"));
    }
#endif
    int nRes = IS_CHAR(*pszSource) ?
               CompareStringA( LOCALE_USER_DEFAULT, 0, (LPCSTR)pszSource, -1, (LPCSTR)pszDest, -1 ) :
               CompareStringW( LOCALE_USER_DEFAULT, 0, (LPCWSTR)pszSource, -1, (LPCWSTR)pszDest, -1 );
    switch (nRes)
    {
    case CSTR_LESS_THAN:
        return -1;
    case CSTR_GREATER_THAN:
        return 1;
    default:
        return 0;
    }
}


template <class T>
inline int CSimpleStringBase<T>::GenericCompareNoCase( const T *pszSource, const T *pszDest )
{
#if defined(DBG) && !defined(UNICODE) && !defined(_UNICODE)
    if (sizeof(T) == sizeof(wchar_t))
    {
        OutputDebugString(TEXT("CompareStringW is not supported under win9x, so this call is going to fail!"));
    }
#endif
    int nRes = IS_CHAR(*pszSource) ?
               CompareStringA( LOCALE_USER_DEFAULT, NORM_IGNORECASE, (LPCSTR)pszSource, -1, (LPCSTR)pszDest, -1 ) :
               CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE, (LPCWSTR)pszSource, -1, (LPCWSTR)pszDest, -1 );
    switch (nRes)
    {
    case CSTR_LESS_THAN:
        return -1;
    case CSTR_GREATER_THAN:
        return 1;
    default:
        return 0;
    }
}

template <class T>
inline int CSimpleStringBase<T>::GenericCompareLength( const T *pszStringA, const T *pszStringB, UINT nLength )
{
#if defined(DBG) && !defined(UNICODE) && !defined(_UNICODE)
    if (sizeof(T) == sizeof(wchar_t))
    {
        OutputDebugString(TEXT("CompareStringW is not supported under win9x, so this call is going to fail!"));
    }
#endif
    if (!nLength)
        return(0);
    int nRes = IS_CHAR(*pszStringA) ?
               CompareStringA( LOCALE_USER_DEFAULT, 0, (LPCSTR)pszStringA, Min(nLength,CSimpleStringBase<CHAR>::GenericLength((LPCSTR)pszStringA)), (LPCSTR)pszStringB, Min(nLength,CSimpleStringBase<CHAR>::GenericLength((LPCSTR)pszStringB)) ) :
               CompareStringW( LOCALE_USER_DEFAULT, 0, (LPWSTR)pszStringA, Min(nLength,CSimpleStringBase<WCHAR>::GenericLength((LPCWSTR)pszStringA)), (LPCWSTR)pszStringB, Min(nLength,CSimpleStringBase<WCHAR>::GenericLength((LPCWSTR)pszStringB)) );
    switch (nRes)
    {
    case CSTR_LESS_THAN:
        return -1;
    case CSTR_GREATER_THAN:
        return 1;
    default:
        return 0;
    }
}

template <class T>
bool CSimpleStringBase<T>::EnsureLength( UINT nMaxSize )
{
    //
    // If the string is already long enough, just return true
    //
    if (m_nMaxSize >= nMaxSize)
    {
        return true;
    }

    //
    // Save the old max size
    //
    UINT nOldMaxSize = m_nMaxSize;

    //
    // Get the new size
    //
    UINT nNewMaxSize = nMaxSize + m_nGranularity;

    //
    // Allocate the new buffer
    //
    T *pszTmp = CreateStorage(nNewMaxSize);

    //
    // Make sure the allocation succeded
    //
    if (pszTmp)
    {
        //
        // If we have an existing string, copy it and delete it
        //
        if (m_pstrData)
        {
            GenericCopy(pszTmp,m_pstrData);
            DeleteStorage();
        }

        //
        // Save the new max size
        //
        m_nMaxSize = nNewMaxSize;
        
        //
        // Save this new string
        //
        m_pstrData = pszTmp;

        //
        // Return success
        //
        return true;
    }
    
    //
    // Couldn't allocate memory
    //
    return false;
}

template <class T>
CSimpleStringBase<T> &CSimpleStringBase<T>::GetWindowText( HWND hWnd )
{
    Destroy();
    // Assume it didn't work
    bool bSuccess = false;
    int nLen = ::GetWindowTextLength(hWnd);
    if (nLen)
    {
        if (EnsureLength(nLen+1))
        {
            if (::GetWindowText( hWnd, m_pstrData, (nLen+1) ))
            {
                bSuccess = true;
            }
        }
    }
    if (!bSuccess)
        Destroy();
    return *this;
}

template <class T>
bool CSimpleStringBase<T>::SetWindowText( HWND hWnd )
{
    return(::SetWindowText( hWnd, String() ) != FALSE);
}

template <class T>
UINT CSimpleStringBase<T>::Truncate( UINT nLen )
{
    if (Length() < nLen)
        return Length();
    if (!nLen)
        return 0;
    m_pstrData[nLen-1] = 0;
    Resize();
    return Length();
}

template <class T>
int CSimpleStringBase<T>::Resize(void)
{
    m_nMaxSize = m_pstrData ? GenericLength(m_pstrData) : 0;
    ++m_nMaxSize;
    T *pszTmp = CreateStorage(m_nMaxSize);
    if (pszTmp)
    {
        if (m_pstrData)
        {
            GenericCopy(pszTmp,m_pstrData);
            DeleteStorage();
        }
        else *pszTmp = 0;
        m_pstrData = pszTmp;
    }
    return Length();
}

template <class T>
CSimpleStringBase<T>::CSimpleStringBase(void)
: m_pstrData(m_pstrAutoData),m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),m_nGranularity(m_nDefaultGranularity)
{
    m_pstrAutoData[0] = 0;
    T szTmp[1] = { 0};
    Assign(szTmp);
}

template <class T>
CSimpleStringBase<T>::CSimpleStringBase( const CSimpleStringBase &other )
: m_pstrData(m_pstrAutoData),m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),m_nGranularity(m_nDefaultGranularity)
{
    m_pstrAutoData[0] = 0;
    Assign(other.String());
}

template <class T>
CSimpleStringBase<T>::CSimpleStringBase( const T *szStr )
: m_pstrData(m_pstrAutoData),m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),m_nGranularity(m_nDefaultGranularity)
{
    m_pstrAutoData[0] = 0;
    Assign(szStr);
}

template <class T>
CSimpleStringBase<T>::CSimpleStringBase( T ch )
: m_pstrData(m_pstrAutoData),m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),m_nGranularity(m_nDefaultGranularity)
{
    m_pstrAutoData[0] = 0;
    T szTmp[2];
    szTmp[0] = ch;
    szTmp[1] = 0;
    Assign(szTmp);
}


template <class T>
CSimpleStringBase<T>::CSimpleStringBase( UINT nResId, HMODULE hModule )
: m_pstrData(m_pstrAutoData),m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),m_nGranularity(m_nDefaultGranularity)
{
    m_pstrAutoData[0] = 0;
    LoadString( nResId, hModule );
}


template <class T>
CSimpleStringBase<T> &CSimpleStringBase<T>::Format( const T *strFmt, ... )
{
    T szTmp[1024];
    va_list arglist;

    va_start(arglist, strFmt);
    int nRet = IS_CHAR(*m_pstrData) ?
               _vsnprintf((LPSTR)szTmp, ARRAYSIZE(szTmp), (LPCSTR)strFmt, arglist) :
               _vsnwprintf((LPWSTR)szTmp, ARRAYSIZE(szTmp), (LPCWSTR)strFmt, arglist);
    va_end(arglist);
    Assign(szTmp);
    return *this;
}

template <class T>
CSimpleStringBase<T> &CSimpleStringBase<T>::Format( int nResId, HINSTANCE hInst, ... )
{
    CSimpleStringBase<T> strFmt;
    va_list arglist;
    if (strFmt.LoadString(nResId,hInst))
    {
        T szTmp[1024];
        va_start(arglist, hInst);
        int nRet = IS_CHAR(*m_pstrData) ?
                   _vsnprintf((LPSTR)szTmp, ARRAYSIZE(szTmp), (LPCSTR)strFmt.String(), arglist) :
                   _vsnwprintf((LPWSTR)szTmp, ARRAYSIZE(szTmp), (LPCWSTR)strFmt.String(), arglist);
        va_end(arglist);
        Assign(szTmp);
    }
    else Assign(NULL);
    return *this;
}

template <class T>
bool CSimpleStringBase<T>::LoadString( UINT nResId, HMODULE hModule )
{
    if (!hModule)
        hModule = GetModuleHandle(NULL);
    T szTmp[m_nMaxLoadStringBuffer];
    int nRet = IS_CHAR(*m_pstrData) ?
               ::LoadStringA( hModule, nResId, (LPSTR)szTmp, ARRAYSIZE(szTmp)) :
               ::LoadStringW( hModule, nResId, (LPWSTR)szTmp, ARRAYSIZE(szTmp));
    if (nRet)
        return Assign(szTmp);
    else return Assign(NULL);
}


template <class T>
CSimpleStringBase<T>::~CSimpleStringBase(void)
{
    Destroy();
}

template <class T>
void CSimpleStringBase<T>::DeleteStorage(void)
{
    //
    // Only delete the string if it is non-NULL and not pointing to our non-dynamically allocated buffer
    //
    if (m_pstrData && m_pstrData != m_pstrAutoData)
    {
        delete[] m_pstrData;
    }
    m_pstrData = NULL;
}

template <class T>
T *CSimpleStringBase<T>::CreateStorage( UINT nCount )
{
    return new T[nCount];
}

template <class T>
void CSimpleStringBase<T>::Destroy(void)
{
    DeleteStorage();
    m_nMaxSize = 0;
}

template <class T>
UINT CSimpleStringBase<T>::Length(void) const
{
    return(m_pstrData ? GenericLength(m_pstrData) : 0);
}

template <class T>
CSimpleStringBase<T> &CSimpleStringBase<T>::operator=( const CSimpleStringBase &other )
{
    if (&other != this)
    {
        Assign(other.String());
    }
    return *this;
}

template <class T>
CSimpleStringBase<T> &CSimpleStringBase<T>::operator=( const T *other )
{
    if (other != String())
    {
        Assign(other);
    }
    return *this;
}

template <class T>
CSimpleStringBase<T> &CSimpleStringBase<T>::operator+=( const CSimpleStringBase &other )
{
    Concat(other.String());
    return *this;
}

template <class T>
CSimpleStringBase<T> CSimpleStringBase<T>::operator+( const CSimpleStringBase &other ) const
{
    CSimpleStringBase tmp(*this);
    tmp.Concat(other);
    return tmp;
}

template <class T>
bool CSimpleStringBase<T>::Assign( const T *szStr )
{
    if (szStr && EnsureLength(GenericLength(szStr)+1))
    {
        GenericCopy(m_pstrData,szStr);
    }
    else if (EnsureLength(1))
    {
        *m_pstrData = 0;
    }
    else Destroy();
    return(NULL != m_pstrData);
}

template <class T>
bool CSimpleStringBase<T>::Assign( const CSimpleStringBase &other )
{
    return Assign( other.String() );
}

template <class T>
void CSimpleStringBase<T>::SetAt( UINT nIndex, T chValue )
{
    //
    // Make sure we don't go off the end of the string or overwrite the '\0'
    //
    if (m_pstrData && Length() > nIndex)
    {
        m_pstrData[nIndex] = chValue;
    }
}


template <class T>
void CSimpleStringBase<T>::Concat( const CSimpleStringBase &other )
{
    if (EnsureLength( Length() + other.Length() + 1 ))
    {
        GenericConcatenate(m_pstrData,other.String());
    }
}

template <class T>
CSimpleStringBase<T> &CSimpleStringBase<T>::MakeUpper(void)
{
    //
    // Make sure the string is not NULL
    //
    if (m_pstrData)
    {
        IS_CHAR(*m_pstrData) ? CharUpperBuffA( (LPSTR)m_pstrData, Length() ) : CharUpperBuffW( (LPWSTR)m_pstrData, Length() );
    }
    return *this;
}

template <class T>
CSimpleStringBase<T> &CSimpleStringBase<T>::MakeLower(void)
{
    //
    // Make sure the string is not NULL
    //
    if (m_pstrData)
    {
        IS_CHAR(*m_pstrData) ? CharLowerBuffA( (LPSTR)m_pstrData, Length() ) : CharLowerBuffW( (LPWSTR)m_pstrData, Length() );
    }
    return *this;
}

template <class T>
CSimpleStringBase<T> CSimpleStringBase<T>::ToUpper(void) const
{
    CSimpleStringBase str(*this);
    str.MakeUpper();
    return str;
}

template <class T>
CSimpleStringBase<T> CSimpleStringBase<T>::ToLower(void) const
{
    CSimpleStringBase str(*this);
    str.MakeLower();
    return str;
}

template <class T>
T &CSimpleStringBase<T>::operator[](int nIndex)
{
    return m_pstrData[nIndex];
}

template <class T>
const T &CSimpleStringBase<T>::operator[](int index) const
{
    return m_pstrData[index];
}

template <class T>
CSimpleStringBase<T> &CSimpleStringBase<T>::TrimRight(void)
{
    T *pFirstWhitespaceCharacterInSequence = NULL;
    bool bInWhiteSpace = false;
    T *pszPtr = m_pstrData;
    while (pszPtr && *pszPtr)
    {
        if (*pszPtr == L' ' || *pszPtr == L'\t' || *pszPtr == L'\n' || *pszPtr == L'\r')
        {
            if (!bInWhiteSpace)
            {
                pFirstWhitespaceCharacterInSequence = pszPtr;
                bInWhiteSpace = true;
            }
        }
        else
        {
            bInWhiteSpace = false;
        }
        pszPtr = GenericCharNext(pszPtr);
    }
    if (pFirstWhitespaceCharacterInSequence && bInWhiteSpace)
        *pFirstWhitespaceCharacterInSequence = 0;
    return *this;
}

template <class T>
CSimpleStringBase<T> &CSimpleStringBase<T>::TrimLeft(void)
{
    T *pszPtr = m_pstrData;
    while (pszPtr && *pszPtr)
    {
        if (*pszPtr == L' ' || *pszPtr == L'\t' || *pszPtr == L'\n' || *pszPtr == L'\r')
        {
            pszPtr = GenericCharNext(pszPtr);
        }
        else break;
    }
    Assign(CSimpleStringBase<T>(pszPtr).String());
    return *this;
}

template <class T>
inline CSimpleStringBase<T> &CSimpleStringBase<T>::Trim(void)
{
    TrimLeft();
    TrimRight();
    return *this;
}

//
// Note that this function WILL NOT WORK CORRECTLY for multi-byte characters in ANSI strings
//
template <class T>
CSimpleStringBase<T> &CSimpleStringBase<T>::Reverse(void)
{
    UINT nLen = Length();
    for (UINT i = 0;i<nLen/2;i++)
    {
        T tmp = m_pstrData[i];
        m_pstrData[i] = m_pstrData[nLen-i-1];
        m_pstrData[nLen-i-1] = tmp;
    }
    return *this;
}

template <class T>
int CSimpleStringBase<T>::Find( T cChar ) const
{
    T strTemp[2] = { cChar, 0};
    return Find(strTemp);
}


template <class T>
int CSimpleStringBase<T>::Find( const CSimpleStringBase &other, UINT nStart ) const
{
    if (!m_pstrData)
        return -1;
    if (nStart > Length())
        return -1;
    T *pstrCurr = m_pstrData+nStart, *pstrSrc, *pstrSubStr;
    while (*pstrCurr)
    {
        pstrSrc = pstrCurr;
        pstrSubStr = (T *)other.String();
        while (*pstrSrc && *pstrSubStr && *pstrSrc == *pstrSubStr)
        {
            pstrSrc = GenericCharNext(pstrSrc);
            pstrSubStr = GenericCharNext(pstrSubStr);
        }
        if (!*pstrSubStr)
            return static_cast<int>(pstrCurr-m_pstrData);
        pstrCurr = GenericCharNext(pstrCurr);
    }
    return -1;
}

template <class T>
int CSimpleStringBase<T>::ReverseFind( T cChar ) const
{
    T strTemp[2] = { cChar, 0};
    return ReverseFind(strTemp);
}

template <class T>
int CSimpleStringBase<T>::ReverseFind( const CSimpleStringBase &srcStr ) const
{
    int nLastFind = -1, nFind=0;
    while ((nFind = Find( srcStr, nFind )) >= 0)
    {
        nLastFind = nFind;
        ++nFind;
    }
    return nLastFind;
}

template <class T>
CSimpleStringBase<T> CSimpleStringBase<T>::SubStr( int nStart, int nCount ) const
{
    if (nStart >= (int)Length() || nStart < 0)
        return CSimpleStringBase<T>();
    if (nCount < 0)
        nCount = Length() - nStart;
    CSimpleStringBase<T> strTmp;
    T *pszTmp = CreateStorage(nCount+1);
    if (pszTmp)
    {
        GenericCopyLength( pszTmp, m_pstrData+nStart, nCount+1 );
        pszTmp[nCount] = 0;
        strTmp = pszTmp;
        delete[] pszTmp;
    }
    return strTmp;
}



template <class T>
int CSimpleStringBase<T>::CompareNoCase( const CSimpleStringBase &other, int nLength ) const
{
    if (nLength < 0)
    {
        //
        // Make sure both strings are non-NULL
        //
        if (!String() && !other.String())
        {
            return 0;
        }
        else if (!String())
        {
            return -1;
        }
        else if (!other.String())
        {
            return 1;
        }
        else return GenericCompareNoCase(m_pstrData,other.String());
    }
    CSimpleStringBase<T> strSrc(*this);
    CSimpleStringBase<T> strTgt(other);
    strSrc.MakeUpper();
    strTgt.MakeUpper();
    //
    // Make sure both strings are non-NULL
    //
    if (!strSrc.String() && !strTgt.String())
    {
        return 0;
    }
    else if (!strSrc.String())
    {
        return -1;
    }
    else if (!strTgt.String())
    {
        return 1;
    }
    else return GenericCompareLength(strSrc.String(),strTgt.String(),nLength);
}


template <class T>
int CSimpleStringBase<T>::Compare( const CSimpleStringBase &other, int nLength ) const
{
    //
    // Make sure both strings are non-NULL
    //
    if (!String() && !other.String())
    {
        return 0;
    }
    else if (!String())
    {
        return -1;
    }
    else if (!other.String())
    {
        return 1;
    }

    if (nLength < 0)
    {
        return GenericCompare(String(),other.String());
    }
    return GenericCompareLength(String(),other.String(),nLength);
}

template <class T>
bool CSimpleStringBase<T>::MatchLastCharacter( T cChar ) const
{
    int nFind = ReverseFind(cChar);
    if (nFind < 0)
        return false;
    if (nFind == (int)Length()-1)
        return true;
    else return false;
}

template <class T>
bool CSimpleStringBase<T>::Load( HKEY hRegKey, const T *pszValueName, const T *pszDefault )
{
    bool bResult = false;
    Assign(pszDefault);
    DWORD nType=0;
    DWORD nSize=0;
    LONG nRet;
    if (IS_CHAR(*m_pstrData))
        nRet = RegQueryValueExA( hRegKey, (LPCSTR)pszValueName, NULL, &nType, NULL, &nSize);
    else nRet = RegQueryValueExW( hRegKey, (LPCWSTR)pszValueName, NULL, &nType, NULL, &nSize);
    if (ERROR_SUCCESS == nRet)
    {
        if ((nType == REG_SZ) || (nType == REG_EXPAND_SZ))
        {
            // Round up to the nearest 2
            nSize = ((nSize + 1) & 0xFFFFFFFE);
            T *pstrTemp = CreateStorage(nSize / sizeof(T));
            if (pstrTemp)
            {
                if (IS_CHAR(*m_pstrData))
                    nRet = RegQueryValueExA( hRegKey, (LPCSTR)pszValueName, NULL, &nType, (PBYTE)pstrTemp, &nSize);
                else nRet = RegQueryValueExW( hRegKey, (LPCWSTR)pszValueName, NULL, &nType, (PBYTE)pstrTemp, &nSize);
                if (ERROR_SUCCESS == nRet)
                {
                    Assign(pstrTemp);
                    bResult = true;
                }
                delete pstrTemp;
            }
        }
    }
    return bResult;
}

template <class T>
bool CSimpleStringBase<T>::Store( HKEY hRegKey, const T *pszValueName, DWORD nType )
{
    bool bResult = false;
    long nRet;
    if (Length())
    {
        if (IS_CHAR(*m_pstrData))
            nRet = RegSetValueExA( hRegKey, (LPCSTR)pszValueName, 0, nType, (PBYTE)m_pstrData, sizeof(*m_pstrData)*(Length()+1) );
        else nRet = RegSetValueExW( hRegKey, (LPCWSTR)pszValueName, 0, nType, (PBYTE)m_pstrData, sizeof(*m_pstrData)*(Length()+1) );
    }
    else
    {
        T strBlank = 0;
        if (IS_CHAR(*m_pstrData))
            nRet = RegSetValueExA( hRegKey, (LPCSTR)pszValueName, 0, nType, (PBYTE)&strBlank, sizeof(T) );
        else nRet = RegSetValueExW( hRegKey, (LPCWSTR)pszValueName, 0, nType, (PBYTE)&strBlank, sizeof(T) );
    }
    return(ERROR_SUCCESS == nRet);
}


typedef CSimpleStringBase<char>     CSimpleStringAnsi;
typedef CSimpleStringBase<wchar_t>  CSimpleStringWide;

#if defined(UNICODE) || defined(_UNICODE)
#define CSimpleString CSimpleStringWide
#else
#define CSimpleString CSimpleStringAnsi
#endif

namespace CSimpleStringConvert
{
    template <class T>
    CSimpleStringWide WideString(const T &str)
    {
        if (IS_WCHAR(str[0]))
            return CSimpleStringWide((LPCWSTR)str.String());
        else
        {
            if (!str.Length())
                return CSimpleStringWide(L"");
            int iLen = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, (LPCSTR)str.String(), str.Length()+1, NULL, 0 );
            CSimpleStringWide sswTmp;
            LPWSTR pwszTmp = new WCHAR[iLen];
            if (pwszTmp)
            {
                MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, (LPCSTR)str.String(), str.Length()+1, pwszTmp, iLen );
                sswTmp = pwszTmp;
                delete[] pwszTmp;
            }
            return sswTmp;
        }
    }

    template <class T>
    CSimpleStringAnsi AnsiString(const T &str)
    {
        if (IS_CHAR(str[0]))
            return CSimpleStringAnsi((LPCSTR)str.String());
        else
        {
            if (!str.Length())
                return CSimpleStringAnsi("");
            int iLen = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)str.String(), str.Length()+1, NULL, 0, NULL, NULL );
            CSimpleStringAnsi ssaTmp;
            LPSTR pszTmp = new CHAR[iLen];
            if (pszTmp)
            {
                WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)str.String(), str.Length()+1, pszTmp, iLen, NULL, NULL );
                ssaTmp = pszTmp;
                delete[] pszTmp;
            }
            return ssaTmp;
        }
    }

#if defined(_UNICODE) || defined(UNICODE)
    template <class T>
    CSimpleStringWide NaturalString(const T &str)
    {
        return WideString(str);
    }
#else
    template <class T>
    CSimpleStringAnsi NaturalString(const T &str)
    {
        return AnsiString(str);
    }
#endif

    inline CSimpleString NumberToString( int nNumber, LCID Locale=LOCALE_USER_DEFAULT )
    {
        TCHAR szTmp[MAX_PATH]=TEXT("");
        TCHAR szNumberStr[MAX_PATH]=TEXT("");
        TCHAR szDigitGrouping[32]=TEXT("");
        TCHAR szThousandsSeparator[32]=TEXT("");
        TCHAR szDecimalSeparator[32]=TEXT("");

        // Initialize the number format
        NUMBERFMT NumberFormat;
        NumberFormat.NumDigits = 0;
        NumberFormat.LeadingZero = 0;
        NumberFormat.NegativeOrder = 0;
        // This turns a string into a number, like so: 3;2;0=32 or 3;0 = 3 or 1;2;3;4;5;6;0 = 123456.  Got it?
        GetLocaleInfo( Locale, LOCALE_SGROUPING, szDigitGrouping, ARRAYSIZE(szDigitGrouping));
        NumberFormat.Grouping = 0;
        LPTSTR pszCurr = szDigitGrouping;
        while (*pszCurr && *pszCurr >= TEXT('1') && *pszCurr <= TEXT('9'))
        {
            NumberFormat.Grouping *= 10;
            NumberFormat.Grouping += (*pszCurr - TEXT('0'));
            pszCurr += 2;
        }
        GetLocaleInfo( Locale, LOCALE_STHOUSAND, szThousandsSeparator, ARRAYSIZE(szThousandsSeparator));
        NumberFormat.lpThousandSep = szThousandsSeparator;
        GetLocaleInfo( Locale, LOCALE_SDECIMAL, szDecimalSeparator, ARRAYSIZE(szDecimalSeparator));
        NumberFormat.lpDecimalSep = szDecimalSeparator;
        // Create the number string
        _sntprintf( szTmp, ARRAYSIZE(szTmp), TEXT("%d"), nNumber );
        if (GetNumberFormat( Locale, 0, szTmp, &NumberFormat, szNumberStr, ARRAYSIZE(szNumberStr)))
            return szNumberStr;
        else return TEXT("");
    }
}  // End CSimpleStringConvert namespace

#endif  // ifndef _SIMSTR_H_INCLUDED