#ifndef _ZSTRING_H_
#define _ZSTRING_H_

//
//  Thin wrapper around a char* string.
//  Allocates exactly as much space as is needed.
//

class ZString
{
public:
    void requireSize(int);
protected:
    void resize(int);
    static void recycle(char*,int);
public:
    void strcpy(const char*);
    void strcpy(const char*,int);
    void strcat(const char*);
    void strcat(const char*,int);
    void appendDecimal(int);
    void trimRight();
    void trimLeft();
    void trim();
    void replaceChars(char,char);
public:
    // For working with BSTR
    void cloneFrom(BSTR);
    void cloneTo(BSTR*);
public:
    // Assignable access to characters within the string.
    // An index outside the string returns a reference to a junk character.
    char& operator[] (int i) { 
        if ((i < 0) || ((int)strlen(m_sBuffer) <= i)) { static char c; return c; }
        return m_sBuffer[i];
    }
public:
    ZString& operator = (const char* s) { strcpy(s); return *this; }
    ZString& operator = (const ZString& s) { strcpy(s); return *this; }
    ZString& operator += (const char* s) { strcat(s); return *this; }
    ZString operator + (const char* s) const { ZString v(m_sBuffer); v.strcat(s); return v; }
public:
    int operator == (const char* s) const { return 0 == strcmp(m_sBuffer,s); }
    operator const char*() const { return m_sBuffer; }
    operator _bstr_t() const { return _bstr_t(m_sBuffer); }
    bool isEmpty() { return 0 == *m_sBuffer; }
public:
    int   getBufferMax() const { return m_cbBufferMax; }
    char* getBuffer()       { return m_sBuffer; }
    char* getBuffer(int n)  { requireSize(n); return m_sBuffer; }
public:
    ZString() : m_sBuffer(0), m_cbBufferMax(0) { strcpy(""); }
    ZString(const char* s) : m_sBuffer(0), m_cbBufferMax(0) { strcpy(s); }
    ZString(const ZString& s) : m_sBuffer(0), m_cbBufferMax(0) { strcpy(s); }
    ZString(BSTR bs) : m_sBuffer(0), m_cbBufferMax(0) { strcpy(_bstr_t(bs)); }
    ~ZString();
protected:
    char* m_sBuffer;
    int   m_cbBufferMax;
};

//
//  Upsize the string buffer to at least the given size.
//

inline void ZString::requireSize(int cbNew)
{
    if (m_cbBufferMax < cbNew) {
        resize(cbNew);
    }
}

//
//  Copy the given string into this string.
//

inline void ZString::strcpy(const char* s) 
{
    if (!s) s = "";
    requireSize(strlen(s)+1);
    ::strcpy(m_sBuffer,s);
}

//
//  Copy the given string into this string.
//

inline void ZString::strcpy(const char* s,int n) 
{
    if (!s) s = "";
    requireSize(n+1);
    ::strncpy(m_sBuffer,s,n);
    m_sBuffer[n] = 0;
}

//
//  Concatenate the given string onto this string.
//

inline void ZString::strcat(const char* s) 
{
    if (!s) s = "";
    requireSize(::strlen(m_sBuffer)+::strlen(s)+1);
    ::strcat(m_sBuffer,s);
}

//
//  Concatenate the given string onto this string.
//

inline void ZString::strcat(const char* s,int n) 
{
    if (!s) s = "";
    int n1 = ::strlen(m_sBuffer);
    int n2 = n1 + n;
    requireSize(n2+1);
    ::strncpy(m_sBuffer+n1,s,n);
    m_sBuffer[n2] = 0;
}

//
//  Concatenate the given integer (in decimal) onto this string.
//

inline void ZString::appendDecimal(int n)
{
    char s[40];
    ::wsprintfA(s,"%d",n);
    strcat(s);
}

//
//  Replace all occurances of the given character with a substitute.
//

inline void ZString::replaceChars(char c1,char c2)
{
    for (char* p = getBuffer(); *p; ++p) {
        if (c1 == *p) *p = c2;
    }
}

#endif
