#if !defined(__CStr__)
#define __CStr__
// include: cstr.h, stdarg.h

#if !defined(CHAR)
#define CHAR char
#endif // !defined(CHAR)

#if !defined(USE_STRLOCK)
#define USE_STRLOCK
#endif // !defined(USE_STRLOCK)

#if !defined(__QSortCompareProc_DEFINED__)
typedef int (__cdecl* QSortCompareProc)(const void* elem1, const void* elem2);
#define __QSortCompareProc_DEFINED__
#endif // !defined(__QSortCompareProc_DEFINED__)

int __cdecl str_memcmp(const void* elem1, const void* elem2);
#if !defined(WIN32)
#define lstrcmp strcmp
#define lstrcmpi strcasecmp
char* _strupr(char *string);
char* _strlwr(char *string);
#endif // !defined(WIN32)
int __cdecl str_Compare(const void* elem1, const void* elem2);
int __cdecl str_CompareReverse(const void* elem1, const void* elem2);
int __cdecl str_CompareIC(const void* elem1, const void* elem2);
int __cdecl str_CompareICReverse(const void* elem1, const void* elem2);

DECLARE_TARRAYREF(CStr);
class CStr
{
private:
    CHAR* m_psz;
// all data in this section must be INITIALIZED!!!!!!!!!!!!!
public:
    CStr();
    CStr(const CHAR* pcsz);
    CStr(const CHAR* pcsz, int nLen);
    CStr(CHAR c, int nLen);
    CStr(const CStr& that);
private:
    CStr(int nMax);
public:
    ~CStr();
public:
    operator const CHAR* () const { return m_psz; }
    CHAR* GetRawBuffer() const { return m_psz; } // be very cautious!!!!
    int GetLength() const { return strlen(m_psz); }
    BOOL IsEmpty() const { return m_psz[0] == '\0'; }
    const CHAR* String() const { return m_psz; }
    const CHAR* Empty();
	void Swap(CStr& that);

#ifdef USE_STRLOCK
    WORD IsLocked() const;
    CHAR* LockBuffer();
    WORD UnlockBuffer();
#endif // USE_STRLOCK

#ifndef _DEBUG
    #define CheckRange(nIndex)
#else // _DEBUG
    void CheckRange(int nIndex) const
    {
        ASSERT((0 <= nIndex) && (nIndex <= lstrlen(m_psz)));
    }
#endif // !_DEBUG
    CHAR operator () (int nIndex) const { CheckRange(nIndex); return m_psz[nIndex]; }
    const CHAR& operator [] (int nIndex) const { CheckRange(nIndex); return m_psz[nIndex]; }
    CHAR& operator [] (int nIndex) { CheckRange(nIndex); return m_psz[nIndex]; }
    CHAR GetAt(int nIndex) const { CheckRange(nIndex); return m_psz[nIndex]; }
    void SetAt(int nIndex, CHAR c) { CheckRange(nIndex); m_psz[nIndex] = c; }
#ifndef _DEBUG
    #undef CheckRange
#endif // !_DEBUG

    const CHAR* Assign(const CHAR* pcsz, int nLen = -1);
    CStr& operator = (const CHAR* pcsz) { Assign(pcsz); return *this; }
    CStr& operator = (const CStr& that) { Assign(that.m_psz); return *this; }
    BOOL operator == (const CHAR* pcsz) const { return pcsz ? strcmp(m_psz, pcsz) == 0: m_psz[0] == '\0'; }
    BOOL operator != (const CHAR* pcsz) const { return pcsz ? strcmp(m_psz, pcsz) != 0: m_psz[0] != '\0'; }
    BOOL operator > (const CHAR* pcsz) const { return pcsz ? strcmp(m_psz, pcsz) > 0: m_psz[0] != '\0'; }
    BOOL operator >= (const CHAR* pcsz) const { return pcsz ? strcmp(m_psz, pcsz) >= 0: TRUE; }
    BOOL operator < (const CHAR* pcsz) const { return pcsz ? strcmp(m_psz, pcsz) < 0: FALSE; }
    BOOL operator <= (const CHAR* pcsz) const { return pcsz ? strcmp(m_psz, pcsz) <= 0: m_psz[0] == '\0'; }
    CStr operator + (const CHAR* pcsz) const;
    CStr& operator += (const CHAR* pcsz);
    CStr operator + (CHAR c) const;
    CStr& operator += (CHAR c);

    void MakeUpper() { _strupr(m_psz); }
    void MakeLower() { _strlwr(m_psz); }
    void MakeReverse();

    const CHAR* Prune(const CHAR* pcszDELs = NULL);
    const CHAR* TrimLeft();
    const CHAR* TrimRight();
    const CHAR* TrimAll();
    void __cdecl Format(LPCTSTR lpszFormat, ...);
    void vFormat(LPCTSTR lpszFormat, va_list marker);

    CHAR* GetBuffer(int nLen);
    const CHAR* ReleaseBuffer(int nLen = -1);
    CHAR* GetBufferSetLength(int nLen);

    void ChangeChars(CHAR cBefore, CHAR cAfter);
    BOOL AsDirectory(BOOL bRemoveLastDEL = FALSE);
    BOOL AddDirDEL();
    int FindExtPos() const;
    const char* FindExt() const;
    BOOL ChangeExt(const char* pcszExt, BOOL bForce = TRUE);

    const CHAR* Left(CStr& strLeft, int nCount) const;
    const CHAR* Right(CStr& strRight, int nCount) const;
    const CHAR* Mid(CStr& strMid, int nFirst) const;
    const CHAR* Mid(CStr& strMid, int nFirst, int nCount) const;

    CStr Left(int nCount) const;
    CStr Right(int nCount) const;
    CStr Mid(int nFirst) const;
    CStr Mid(int nFirst, int nCount) const;

    int FindNext(CHAR c, int nStart) const;
    int Find(CHAR c) const;
    int rFindNext(CHAR c, int nStart) const;
    int rFind(CHAR c) const;
    int Find(const CHAR* pcsz, int nStart) const;
    int Find(const CHAR* pcsz) const;
    int FindSet(const CHAR* pcszSet) const;
    int rFindSet(const CHAR* pcszSet) const;
#if defined(WIN32)
    BOOL GetWindowText(HWND hWnd);
#endif // defined(WIN32)
protected:
    const CHAR* AssignNoCheck(const CHAR* pcsz, int nLen = -1);
private:
    CHAR* Reset();
#ifdef USE_STRLOCK
    CHAR* ResetLocked();
#endif // USE_STRLOCK
};
#if !defined(USE_CSTR_ISTREAM) && defined(_INC_ISTREAM)
#define USE_CSTR_ISTREAM
#endif // !defined(USE_CSTR_ISTREAM) && defined(_INC_ISTREAM)
#if defined(NO_USE_CSTR_ISTREAM)
#undef USE_CSTR_ISTREAM
#endif // defined(NO_USE_CSTR_ISTREAM)

#ifdef USE_CSTR_ISTREAM
istream& operator >> (istream& s, CStr& str);
#endif // USE_CSTR_ISTREAM

#define LSTRS_TOLOWER   0x0001
#define LSTRS_TOUPPER   0x0002
#define LSTRS_ALLOWDUP  0x0004
#define LSTRS_DOSORT    0x0008

class CStrArray : public TARRAYREF(CStr)
{
public:
    int GetTotalLength() const;
    int Find(const char* pcsz) const;
    int AddIfNotFound(const char* pcsz);
    int AddIfNotFound(const CStr astrs[], int nCount);
    void Sort(QSortCompareProc pQSortCompareProc = (QSortCompareProc)NULL);
    BOOL LoadStrs(const char* pcszFile, const char* pcszVals = NULL, UINT uFlags = 0);
    BOOL LoadStrs(FILE* pFile, const char* pcszVals = NULL, UINT uFlags = 0);
    void LSTRS_CheckAdd(CStr& str, UINT uFlags);
};

#ifdef _DEBUG
#define ASSERT_VALIDSTRING(pcszStr) ASSERT(IsValidString(pcszStr))
#define ASSERT_VALIDSTRINGSAFE(pcszStr) ASSERT(IsValidStringSafe(pcszStr))
#define ASSERT_CLSPTR(p) ASSERT(IsValidMemory(p, sizeof(*p), TRUE))

BOOL IsValidString(const CHAR* pcszStr, int nLen = -1);
BOOL IsValidStringSafe(const CHAR* pcszStr, int nLen = -1);
BOOL IsValidMemory(const void* pMem, UINT uBytes, BOOL bWriteCheck = TRUE);
BOOL IsValidMemorySafe(const void* pMem, UINT uBytes, BOOL bWriteCheck = TRUE);
#else // !_DEBUG
#define ASSERT_VALIDSTRING(pcszStr)
#define ASSERT_VALIDSTRINGSAFE(pcszStr)
#define ASSERT_CLSPTR(p)

inline BOOL __RelIsValidAddr(const void* p, int nLen = -1) { &p, &nLen; return TRUE; }
#define IsValidString 1 ? 1: __RelIsValidAddr
#define IsValidStringSafe 1 ? 1: __RelIsValidAddr
#define IsValidMemory 1 ? 1: __RelIsValidAddr
#define IsValidMemorySafe 1 ? 1: __RelIsValidAddr
#endif // _DEBUG

#ifdef _DEBUG
void CStrTestMain();
#endif // _DEBUG

#endif // !defined(__CStr__)
