#include "UtlString.hpp"

#define NOMINMAX
#include <Windows.h>
#include <stdio.h>
#define _UtlString_assert

//-----------------------------------------------------------------------------
// Base class, containing simple memory management
//-----------------------------------------------------------------------------
CUtlBinaryBlock::CUtlBinaryBlock(int growSize, int initSize) : m_Memory(growSize, initSize)
{
    m_nActualLength = 0;
}

CUtlBinaryBlock::CUtlBinaryBlock(void* pMemory, int nSizeInBytes, int nInitialLength) : m_Memory((unsigned char*)pMemory, nSizeInBytes)
{
    m_nActualLength = nInitialLength;
}

CUtlBinaryBlock::CUtlBinaryBlock(const void* pMemory, int nSizeInBytes) : m_Memory((const unsigned char*)pMemory, nSizeInBytes)
{
    m_nActualLength = nSizeInBytes;
}

CUtlBinaryBlock::CUtlBinaryBlock(const CUtlBinaryBlock& src)
{
    Set(src.Get(), src.Length());
}

void CUtlBinaryBlock::Get(void *pValue, int nLen) const
{
    _UtlString_assert(nLen > 0);
    if(m_nActualLength < nLen) {
        nLen = m_nActualLength;
    }

    if(nLen > 0) {
        memcpy(pValue, m_Memory.Base(), nLen);
    }
}

void CUtlBinaryBlock::SetLength(int nLength)
{
    _UtlString_assert(!m_Memory.IsReadOnly());

    m_nActualLength = nLength;
    if(nLength > m_Memory.NumAllocated()) {
        int nOverFlow = nLength - m_Memory.NumAllocated();
        m_Memory.Grow(nOverFlow);

        // If the reallocation failed, clamp length
        if(nLength > m_Memory.NumAllocated()) {
            m_nActualLength = m_Memory.NumAllocated();
        }
    }

#ifdef _DEBUG
    if(m_Memory.NumAllocated() > m_nActualLength) {
        memset(((char *)m_Memory.Base()) + m_nActualLength, 0xEB, m_Memory.NumAllocated() - m_nActualLength);
    }
#endif
}

void CUtlBinaryBlock::Set(const void *pValue, int nLen)
{
    _UtlString_assert(!m_Memory.IsReadOnly());

    if(!pValue) {
        nLen = 0;
    }

    SetLength(nLen);

    if(m_nActualLength) {
        if(((const char *)m_Memory.Base()) >= ((const char *)pValue) + nLen ||
            ((const char *)m_Memory.Base()) + m_nActualLength <= ((const char *)pValue)) {
            memcpy(m_Memory.Base(), pValue, m_nActualLength);
        } else {
            memmove(m_Memory.Base(), pValue, m_nActualLength);
        }
    }
}


CUtlBinaryBlock &CUtlBinaryBlock::operator=(const CUtlBinaryBlock &src)
{
    _UtlString_assert(!m_Memory.IsReadOnly());
    Set(src.Get(), src.Length());
    return *this;
}


bool CUtlBinaryBlock::operator==(const CUtlBinaryBlock &src) const
{
    if(src.Length() != Length())
        return false;

    return !memcmp(src.Get(), Get(), Length());
}


//-----------------------------------------------------------------------------
// Simple string class. 
//-----------------------------------------------------------------------------
CUtlString::CUtlString()
{
}

CUtlString::CUtlString(const char *pString)
{
    Set(pString);
}

CUtlString::CUtlString(const CUtlString& string)
{
    Set(string.Get());
}

// Attaches the string to external memory. Useful for avoiding a copy
CUtlString::CUtlString(void* pMemory, int nSizeInBytes, int nInitialLength) : m_Storage(pMemory, nSizeInBytes, nInitialLength)
{
}

CUtlString::CUtlString(const void* pMemory, int nSizeInBytes) : m_Storage(pMemory, nSizeInBytes)
{
}

void CUtlString::Set(const char *pValue)
{
    _UtlString_assert(!m_Storage.IsReadOnly());
    int nLen = pValue ? strlen(pValue) + 1 : 0;
    m_Storage.Set(pValue, nLen);
}

// Returns strlen
int CUtlString::Length() const
{
    return m_Storage.Length() ? m_Storage.Length() - 1 : 0;
}

// Sets the length (used to serialize into the buffer )
void CUtlString::SetLength(int nLen)
{
    _UtlString_assert(!m_Storage.IsReadOnly());

    // Add 1 to account for the NULL
    m_Storage.SetLength(nLen > 0 ? nLen + 1 : 0);
}

const char *CUtlString::Get() const
{
    if(m_Storage.Length() == 0) {
        return "";
    }

    return reinterpret_cast<const char*>(m_Storage.Get());
}

// Converts to c-strings
CUtlString::operator const char*() const
{
    return Get();
}

char *CUtlString::Get()
{
    _UtlString_assert(!m_Storage.IsReadOnly());

    if(m_Storage.Length() == 0) {
        // In general, we optimise away small mallocs for empty strings
        // but if you ask for the non-const bytes, they must be writable
        // so we can't return "" here, like we do for the const version - jd
        m_Storage.SetLength(1);
        m_Storage[0] = '\0';
    }

    return reinterpret_cast<char*>(m_Storage.Get());
}

CUtlString &CUtlString::operator=(const CUtlString &src)
{
    _UtlString_assert(!m_Storage.IsReadOnly());
    m_Storage = src.m_Storage;
    return *this;
}

CUtlString &CUtlString::operator=(const char *src)
{
    _UtlString_assert(!m_Storage.IsReadOnly());
    Set(src);
    return *this;
}

bool CUtlString::operator==(const CUtlString &src) const
{
    return m_Storage == src.m_Storage;
}

bool CUtlString::operator==(const char *src) const
{
    return (strcmp(Get(), src) == 0);
}

CUtlString &CUtlString::operator+=(const CUtlString &rhs)
{
    _UtlString_assert(!m_Storage.IsReadOnly());

    const int lhsLength(Length());
    const int rhsLength(rhs.Length());
    const int requestedLength(lhsLength + rhsLength);

    SetLength(requestedLength);
    const int allocatedLength(Length());
    const int copyLength(allocatedLength - lhsLength < rhsLength ? allocatedLength - lhsLength : rhsLength);
    memcpy(Get() + lhsLength, rhs.Get(), copyLength);
    m_Storage[allocatedLength] = '\0';

    return *this;
}

CUtlString &CUtlString::operator+=(const char *rhs)
{
    _UtlString_assert(!m_Storage.IsReadOnly());

    const int lhsLength(Length());
    const int rhsLength(strlen(rhs));
    const int requestedLength(lhsLength + rhsLength);

    SetLength(requestedLength);
    const int allocatedLength(Length());
    const int copyLength(allocatedLength - lhsLength < rhsLength ? allocatedLength - lhsLength : rhsLength);
    memcpy(Get() + lhsLength, rhs, copyLength);
    m_Storage[allocatedLength] = '\0';

    return *this;
}

CUtlString &CUtlString::operator+=(char c)
{
    _UtlString_assert(!m_Storage.IsReadOnly());

    int nLength = Length();
    SetLength(nLength + 1);
    m_Storage[nLength] = c;
    m_Storage[nLength + 1] = '\0';
    return *this;
}

CUtlString &CUtlString::operator+=(int rhs)
{
    _UtlString_assert(!m_Storage.IsReadOnly());
    _UtlString_assert(sizeof(rhs) == 4);

    char tmpBuf[12];	// Sufficient for a signed 32 bit integer [ -2147483648 to +2147483647 ]
    snprintf(tmpBuf, sizeof(tmpBuf), "%d", rhs);
    tmpBuf[sizeof(tmpBuf) - 1] = '\0';

    return operator+=(tmpBuf);
}

CUtlString &CUtlString::operator+=(double rhs)
{
    _UtlString_assert(!m_Storage.IsReadOnly());

    char tmpBuf[256];	// How big can doubles be???  Dunno.
    snprintf(tmpBuf, sizeof(tmpBuf), "%lg", rhs);
    tmpBuf[sizeof(tmpBuf) - 1] = '\0';

    return operator+=(tmpBuf);
}

int CUtlString::Format(const char *pFormat, ...)
{
    _UtlString_assert(!m_Storage.IsReadOnly());

    char tmpBuf[4096];	//< Nice big 4k buffer, as much memory as my first computer had, a Radio Shack Color Computer

    va_list marker;

    va_start(marker, pFormat);
    int len = _vsnprintf_s(tmpBuf, 4096, sizeof(tmpBuf) - 1, pFormat, marker);
    va_end(marker);

    // Len < 0 represents an overflow
    if(len < 0) {
        len = sizeof(tmpBuf) - 1;
        tmpBuf[sizeof(tmpBuf) - 1] = 0;
    }

    Set(tmpBuf);

    return len;
}

//-----------------------------------------------------------------------------
// Strips the trailing slash
//-----------------------------------------------------------------------------
void CUtlString::StripTrailingSlash()
{
    if(IsEmpty())
        return;

    int nLastChar = Length() - 1;
    char c = m_Storage[nLastChar];
    if(c == '\\' || c == '/') {
        m_Storage[nLastChar] = 0;
        m_Storage.SetLength(m_Storage.Length() - 1);
    }
}