// QuickString.cpp: implementation of the COXQuickString class.
//
//////////////////////////////////////////////////////////////////////
// Version: 9.3


#include "stdafx.h"
#include "OXQuickString.h"

#include "UTBStrOp.h"
#include "UTB64Bit.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

COXQuickString::COXQuickString()
{
    Init();
}

COXQuickString::COXQuickString(LPCTSTR szText)
{
    Init();
    SetString(szText);
}

COXQuickString::COXQuickString(const COXQuickString& str)
{
    Init();
    SetString(str.GetString());
}

COXQuickString::~COXQuickString()
{
    Empty();
}

//////////////////////////////////////////////////////////////////////
// Operations
//////////////////////////////////////////////////////////////////////

void COXQuickString::Empty()
{
    delete [] m_szText;
    
    Init();
}

void COXQuickString::Init()
{
    m_szText      = NULL;
    m_nBufferSize = 0;
    m_nLength     = 0;
    m_nGrowBy     = 10;
}

LPTSTR COXQuickString::AllocBuffer(UINT nBufferSize)
{
    return new TCHAR[nBufferSize];
}

BOOL COXQuickString::IsEmpty() const
{
    return (!m_szText || !(*m_szText));
}

BOOL COXQuickString::SetString(LPCTSTR szText)
{
    // We do a fresh memory allocation when setting the string.
    Empty();

    if (!szText)
        return TRUE;

    m_nLength = PtrToUint(_tcslen(szText));
    m_nBufferSize = m_nLength + 1;

    m_szText = AllocBuffer(m_nBufferSize);
    if (!m_szText)
        return FALSE;

	UTBStr::tcscpy(m_szText, m_nBufferSize, szText);

    return TRUE;
}

BOOL COXQuickString::SetString(LPCTSTR szText, int nCount)
{
    // We do a fresh memory allocation when setting the string.
    Empty();

    if (!szText || nCount <= 0)
        return TRUE;

    m_nLength = min(PtrToUint(_tcslen(szText)), (UINT)nCount);
    m_nBufferSize = m_nLength + 1;

    m_szText = AllocBuffer(m_nBufferSize);
    if (!m_szText)
        return FALSE;

	UTBStr::tcsncpy(m_szText, m_nBufferSize, szText, m_nLength+1);
    m_szText[m_nLength] = TEXT('\0');

    return TRUE;
}

BOOL COXQuickString::Append(TCHAR ch)
{
    UINT nLength = m_nLength + 1;

    // We reuse memory (if possible) when adding to the string.
    if (nLength+1 > m_nBufferSize)
    {   
        LPTSTR tmp = AllocBuffer(nLength+m_nGrowBy);
        if (!tmp)
            return FALSE;
        tmp[0] = TEXT('\0');
        
        m_nBufferSize = m_nLength + m_nGrowBy;
        
        if (m_szText)
        {
			UTBStr::tcscpy(tmp, nLength+m_nGrowBy, m_szText);
            delete [] m_szText;
        }
        m_szText = tmp;
    }

    m_szText[m_nLength++] = ch;
    m_szText[m_nLength] = TEXT('\0');

    return TRUE;
}

BOOL COXQuickString::AddString(LPCTSTR szText)
{
    if (!szText)
        return FALSE;

    UINT nLength = m_nLength + _tcslen(szText);

    // We reuse memory (if possible) when adding to the string.
    if (nLength+1 > m_nBufferSize)
    {   
        LPTSTR tmp = AllocBuffer(nLength+1);
        if (!tmp)
            return FALSE;
        tmp[0] = TEXT('\0');
        
        m_nBufferSize = m_nLength + 1;
        
        if (m_szText)
        {
			UTBStr::tcscpy(tmp, nLength+1, m_szText);
            delete [] m_szText;
        }
        m_szText = tmp;
    }

	UTBStr::tcscat(m_szText, nLength+1, szText);
  
	m_nLength = nLength;

    return TRUE;
}

BOOL COXQuickString::AddString(LPCTSTR szText, int nCount)
{
    if (!szText)
        return FALSE;

    UINT nLength = m_nLength + min(_tcslen(szText), (UINT)nCount);

    // We reuse memory (if possible) when adding to the string.
    if (nLength+1 > m_nBufferSize)
    {
        LPTSTR tmp = AllocBuffer(nLength+1);
        if (!tmp)
            return FALSE;
        tmp[0] = TEXT('\0');

        m_nBufferSize = nLength + 1;

        if (m_szText)
        {
			UTBStr::tcscpy(tmp, nLength+1, m_szText);
            delete [] m_szText;
        }
        m_szText = tmp;
    }

	UTBStr::tcsncat(m_szText, nLength+1, szText, nCount);
    m_szText[nLength] = TEXT('\0');
    m_nLength     = nLength;

    return TRUE;
}

LPCTSTR COXQuickString::GetString() const
{
    static const char chNull = TEXT('\0');

    if (IsEmpty())
        return (LPCTSTR)&chNull;
    else
        return (LPCTSTR)m_szText;
}

UINT COXQuickString::GetLength() const
{
#ifdef _DEBUG
    UINT nLength = m_szText? PtrToUint(_tcslen(m_szText)) : 0;
    ASSERT(nLength == m_nLength);
#endif
    return m_nLength;
}

BOOL COXQuickString::SetLength(UINT nLength) 
{
    // Do easy cases and return immediately if no problems
    if (nLength == m_nBufferSize)
        return TRUE;

    if (!nLength)
    {
        Empty();
        return TRUE;
    }

    if (nLength < m_nBufferSize)
    {
        m_szText[nLength] = TEXT('\0');
        m_nLength = min(m_nLength, nLength);
        return TRUE;
    }

    // Memory (re)allocation needed

    LPTSTR pBuf = AllocBuffer(nLength);
    if (!pBuf)
        return FALSE;

    m_nBufferSize = nLength;
    if (m_szText) 
    {
		UTBStr::tcscpy(pBuf, nLength, m_szText);
        delete [] m_szText;
    }
    m_szText = pBuf;
    m_szText[m_nLength] = TEXT('\0');

    return TRUE;
}

void COXQuickString::SetGrowBy(UINT nGrowBy)
{
    m_nGrowBy = nGrowBy;
}

UINT COXQuickString::GetGrowBy() const
{
    return m_nGrowBy;
}

BOOL COXQuickString::Compare(LPCTSTR szText, BOOL bCaseSensitive /*=TRUE*/)
{
    if (!szText || IsEmpty())
        return FALSE;

    BOOL bSame = FALSE;
    if (!bCaseSensitive)
        bSame = (_tcsicmp(m_szText, szText) == 0);
    else
        bSame = (_tcscmp(m_szText, szText) == 0);

    return bSame;
}

BOOL COXQuickString::operator==(LPCTSTR szText)
{
    return Compare(szText, TRUE);
}

BOOL COXQuickString::operator==(COXQuickString& str)
{
    return Compare(str.GetString(), TRUE);
}

BOOL COXQuickString::operator!=(LPCTSTR szText)
{
    return !Compare(szText, TRUE);
}

BOOL COXQuickString::operator!=(COXQuickString& str)
{
    return !Compare(str.GetString(), TRUE);
}

COXQuickString::operator LPCTSTR() const
{
    return GetString();
}

void COXQuickString::operator=(LPCTSTR szText)
{
    SetString(szText);
}

void COXQuickString::operator=(COXQuickString& str)
{
    if (this == &str) 
        return;

    SetString(str.GetString());
}

void COXQuickString::operator+=(LPCTSTR szText)
{
    AddString(szText);
}

void COXQuickString::operator+=(COXQuickString& str)
{
    AddString(str.GetString());
}

BOOL COXQuickString::Strip()
{
    const TCHAR chNBSP = TEXT(' ');  // This is character 160, NOT character 32

    if (IsEmpty())
        return TRUE;

    int nLength = 0;
    LPTSTR ptr = m_szText;
    if (!ptr || !ptr[0])
        return FALSE;

    // Remove leading whitespace (keep NBSP characters)
    while (_istspace(*ptr) && *ptr != chNBSP)
    {
        ptr++;
        nLength++;
    }

    // Find how many "good" characters we have (good = are not consequetive spaces)
    LPTSTR pStart = ptr;
    int nGoodChars = 0;
    while (*ptr)
    {
        nLength++;
        if (!_istspace(*ptr) || *ptr == chNBSP)
            nGoodChars++;
        else
        {
            if (!_istspace(*(ptr+1)) || *ptr == chNBSP)
                nGoodChars++;
        }
        ptr++;
    }

    // Copy over these "good" chars over to a new buffer
    LPTSTR szNewString = AllocBuffer(nGoodChars+1);
    if (!szNewString)
        return FALSE;

    ptr = pStart;
    int nCount = 0;
    while (*ptr && nCount < nGoodChars)
    {
        if (!_istspace(*ptr) || *ptr == chNBSP)
            szNewString[nCount++] = *ptr;
        else
        {
            if ( (!_istspace(*(ptr+1)) || *ptr == chNBSP) && *(ptr+1))
                szNewString[nCount++] = TEXT(' ');
        }
        ptr++;
    }

    delete [] m_szText;

    // Return the new string
    szNewString[nCount] = TEXT('\0');

    m_szText = szNewString;
    m_nLength = nCount;
    m_nBufferSize = nGoodChars+1;

    return TRUE;
}

BOOL COXQuickString::TrimRight()
{
    if (IsEmpty())
        return TRUE;

    LPTSTR ptr = m_szText+m_nLength-1;

    while (_istspace(*ptr) && ptr >= m_szText)
        ptr--;

    m_nLength = PtrToUint(ptr - m_szText + 1);
    m_szText[m_nLength] = TEXT('\0');

    return TRUE;
}

BOOL COXQuickString::TrimLeft()
{
    if (IsEmpty())
        return TRUE;

    LPTSTR ptr = m_szText;
    while (ptr - m_szText < (int)m_nLength && _istspace(*ptr))
        ptr++;

    if (ptr != m_szText)
    {
        int nLength = m_nLength-(ptr-m_szText);
        LPTSTR szNewString = AllocBuffer(nLength+1);
        if (!szNewString)
            return FALSE;

		UTBStr::tcscpy(szNewString, nLength+1, ptr);
        szNewString[nLength] = TEXT('\0');
        m_nLength = nLength;
        m_nBufferSize = nLength+1;

        delete [] m_szText;
        m_szText = szNewString;
    }

    return TRUE;
}

BOOL COXQuickString::Trim()
{
    return (TrimRight() && TrimLeft());
}

UINT COXQuickString::NumTokens(TCHAR chDelimiter)
{
    if (IsEmpty())
        return 0;

    int nNumTokens = 1;
    for (LPCTSTR ptr = m_szText; *ptr; ptr++)
    {
        if (*ptr == chDelimiter)
            nNumTokens++;
    }

    return nNumTokens;
}

const COXQuickString COXQuickString::GetToken(int nIndex, TCHAR chDelimiter) const
{
    COXQuickString strReturn;

    if (IsEmpty())
        return strReturn;

    LPCTSTR pstrStart  = m_szText;
    LPCTSTR pstrBuffer = pstrStart;

    int nCurrent = 0;
    int nStart = 0;
    int nEnd = 0;
    int nOldStart = 0;

    // Search for token
    while (nCurrent <= nIndex && *pstrBuffer != TEXT('\0'))
    {
        if (*pstrBuffer == chDelimiter)
        {
            nOldStart = nStart;
            nStart = nEnd+1;
            nCurrent++;
        }
        nEnd++;
        pstrBuffer++;
    }

    // May have reached the end of the string
    if (*pstrBuffer == TEXT('\0'))
    {
        nOldStart = nStart;
        nEnd++;
    }

    if (nCurrent < nIndex) 
    {
        //TRACE1("Warning: GetStringField - Couldn't find field %d.\n", nIndex);
        return strReturn;
    }

    strReturn.SetString(m_szText+nOldStart, nEnd-nOldStart-1);

    return strReturn;
}