/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
/*****************************************************************************
*
*   $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtStr.cpp
*   
***/

#include "../Pch.h"
#pragma hdrstop


/*****************************************************************************
*
*   Private data
*
***/

// These random values were generated by the radioactive decay based
// random number generator at www.fourmilab.ch
static dword s_hashValue[] = {
    0xc30d2a72, 0x15efaec1, 0xd250c7d9, 0xaf3c60a8,
    0x9608ae8f, 0x452c0e11, 0xb6840ffd, 0x3e36c913,
    0x2864eace, 0x9b0a17d6, 0x108da74b, 0xf2c479c1,
    0x8b4dd597, 0x97199bc0, 0x621f0cce, 0x1658553e,
};


/****************************************************************************
*
*   Internal functions
*
***/

//===========================================================================
template<class chartype>
static chartype * IStrDup (const chartype str[]) {
    unsigned chars = IStrLen(str) + 1;
    chartype * buffer = (chartype *)ALLOC(chars * sizeof(chartype));
    IStrCopy(buffer, str, chars);
    return buffer;
}

//===========================================================================
template<class chartype>
static chartype * IStrDupLen (const chartype str[], unsigned chars) {
    unsigned len = IStrLen(str) + 1;
    if (len > chars)
        len = chars;
    chartype * buffer = (chartype *)ALLOC(len * sizeof(chartype));
    IStrCopy(buffer, str, len);
    return buffer;
}

//===========================================================================
template<class chartype, class findchartype>
static chartype * IStrChr (chartype * str, findchartype ch, unsigned chars) {
    for (; chars--; ++str)
        if (*str == ch)
            return str;
        else if (!*str)
            break;
    return nil;
}

//===========================================================================
template<class chartype, class findchartype>
static chartype * IStrChrR (chartype * str, findchartype ch) {
    chartype * start = str;
    for (; *str; ++str)
        NULL_STMT;
    while (str-- > start)
        if (*str == ch)
            return str;
    return NULL;
}

//===========================================================================
static inline bool ICharUnicodeToUtf8 (char ** dest, const wchar * source[], unsigned destChars) {
    unsigned ch     = *(*source)++;
    bool     result = false;
    if (ch < 0x80) {
        if (destChars >= 1) {
            *(*dest)++ = (char)ch;
            result = true;
        }
    }
    else if (ch < 0x800) {
        if (destChars >= 2) {
            *(*dest)++ = (char)(0xc0 | (ch >> 6));
            *(*dest)++ = (char)(0x80 | (ch & 0x3f));
            result = true;
        }
    }
    else {
        if (destChars >= 3) {
            *(*dest)++ = (char)(0xe0 | (ch >> 12));
            *(*dest)++ = (char)(0x80 | ((ch >> 6) & 0x3f));
            *(*dest)++ = (char)(0x80 | (ch & 0x3f));
            result = true;
        }
    }
    return result;
}

//===========================================================================
static inline void ICharUtf8ToUnicode (wchar ** dest, const char * source[]) {
    unsigned result, remaining;
    if ((**source & 0xf0) == 0xe0) {
        result    = *(*source)++ & 0x0f;
        remaining = 2;
    }
    else if ((**source & 0xe0) == 0xc0) {
        result    = *(*source)++ & 0x1f;
        remaining = 1;
    }
    else if ((**source & 0x80) == 0x00) {
        result    = *(*source)++;
        remaining = 0;
    }
    else {
        // unsupported code sequence (>0xffff)
        ++(*source);
        return;
    }
    for (; remaining-- && *source; ++*source)
        if ((**source & 0xc0) == 0x80)
            result = (result << 6) | (**source & 0x3f);
    *(*dest)++ = (wchar)result;
}

//===========================================================================
template<typename chartype>
static unsigned IStrPrintfValidate (chartype * dest, unsigned count, int result) {
    if (!count)
        return 0;
    ASSERT(result <= (int)count);
    if ((result < 0) || (result == (int)count)) {
        dest[count - 1] = 0;
        return count - 1;
    }
    else
        return (unsigned)result;
}

//===========================================================================
template<class chartype>
static int IStrCmp (const chartype str1[], const chartype str2[], unsigned chars) {
    for (; chars--; ++str1, ++str2) {
        if (*str1 != *str2)
            return (*str1 > *str2) ? 1 : -1;
        if (!*str1)
            return 0;
    }
    return 0;
}

//===========================================================================
template<class chartype>
static int IStrCmpI (const chartype str1[], const chartype str2[], unsigned chars) {
    while (chars--) {
        chartype ch1 = CharLowerFast(*str1++);
        chartype ch2 = CharLowerFast(*str2++);
        if (ch1 != ch2)
            return (ch1 > ch2) ? 1 : -1;
        if (!ch1)
            return 0;
    }
    return 0;
}

//===========================================================================
template<class chartype>
static void IStrCopy (chartype * dest, const chartype source[], unsigned chars) {
    while ((chars > 1) && ((*dest = *source++) != 0)) {
        --chars;
        ++dest;
    }
    if (chars)
        *dest = 0;
}

//===========================================================================
// returns StrLen(dest)
template<class chartype>
static unsigned IStrCopyLen (chartype * dest, const chartype source[], unsigned chars) {
    chartype * const start = dest;
    while ((chars > 1) && ((*dest = *source++) != 0)) {
        --chars;
        ++dest;
    }
    if (chars)
        *dest = 0;
    return dest - start;
}

//===========================================================================
template<class chartype>
static void IStrPack (chartype * dest, const chartype source[], unsigned chars) {
    while ((chars > 1) && *dest) {
        --chars;
        ++dest;
    }
    while ((chars > 1) && ((*dest = *source++) != 0)) {
        --chars;
        ++dest;
    }
    if (chars)
        *dest = 0;
}

//===========================================================================
template<class chartype>
static chartype * IStrStr (chartype source[], const chartype match[]) {
    if (!*match)
        return source;

    for (chartype * curr = source; *curr; ++curr) {
        chartype * s1       = curr;
        const chartype * s2 = match;
        while (*s1 && *s2 && *s1 == *s2)
            s1++, s2++;
        if (!*s2)
            return curr;
    }

    return nil;
}


//===========================================================================
template<class chartype>
static chartype * IStrStrI (chartype source[], const chartype match[]) {
    if (!*match)
        return source;

    for (chartype * curr = source; *curr; ++curr) {
        chartype * s1       = curr;
        const chartype * s2 = match;
        while (*s1 && *s2 && (CharLowerFast(*s1) == CharLowerFast(*s2)))
            s1++, s2++;
        if (!*s2)
            return curr;
    }

    return nil;
}

//===========================================================================
template<class chartype>
static unsigned IStrLen (const chartype str[]) {
    unsigned chars = 0;
    for (; *str++; ++chars)
        NULL_STMT;
    return chars;
}

//===========================================================================
template<class chartype>
static void IStrLower (chartype * dest, unsigned chars) {
    while ((chars > 1) && ((*dest = CharLowerFast(*dest)) != 0)) {
        --chars;
        ++dest;
    }
}

//===========================================================================
template<class chartype>
static void IStrLower (chartype * dest, const chartype source[], unsigned chars) {
    while ((chars > 1) && ((*dest = CharLowerFast(*source)) != 0)) {
        --chars;
        ++dest;
        ++source;
    }
    if (chars)
        *dest = 0;
}

//===========================================================================
template<class chartype>
static dword IStrHash (const chartype str[], unsigned chars) {
    dword temp0  = 0xE2C15C9D;
    dword temp1  = 0x2170A28A;
    dword result = 0x325D1EAE;
    for (unsigned ch; chars-- && ((ch = (unsigned)*str) != 0); ++str) {
        temp0   = (temp0 << 3) ^ ch;
        temp1  += s_hashValue[temp0 & 0x0F];
        result ^= temp0 + temp1;
    }
    return result;
}

//===========================================================================
template<class chartype>
static dword IStrHashI (const chartype str[], unsigned chars) {
    dword temp0  = 0xE2C15C9D;
    dword temp1  = 0x2170A28A;
    dword result = 0x325D1EAE;
    for (unsigned ch; chars-- && ((ch = (unsigned)*str) != 0); ++str) {
        if ((ch >= 'a') && (ch <= 'z'))
            ch = ch + 'A' - 'a';
        temp0   = (temp0 << 3) ^ ch;
        temp1  += s_hashValue[temp0 & 0x0F];
        result ^= temp0 + temp1;
    }
    return result;
}

//===========================================================================
template<class chartype>
static bool IStrTokenize (const chartype * source[], chartype * dest, unsigned chars, const chartype whitespace[], unsigned maxWhitespaceSkipCount) {

    // Skip past leading whitespace
    bool inQuotes = false;
    unsigned whitespaceSkipped = 0;
    while (**source && IStrChr(whitespace, **source, (unsigned)-1) && whitespaceSkipped < maxWhitespaceSkipCount) {
        inQuotes = (**source == '\"');
        ++*source;
        ++whitespaceSkipped;
        if (inQuotes)
            break;
    }

    // Copy the token
    unsigned offset = 0;
    while (**source &&
           ((inQuotes && (**source != '\"')) || !IStrChr(whitespace, **source, (unsigned)-1))) {
        if (offset + 1 < chars)
            dest[offset++] = **source;
        ++*source;
    }

    // Skip past the terminating quote
    if (inQuotes && (**source == '\"'))
        ++*source;

    // Null terminate the destination buffer
    if (chars) {
        ASSERT(offset < chars);
        dest[offset] = 0;
    }

    // Upon return, 'source' is guaranteed to point to the first character
    // following the returned token (and following any closing quotes)

    return (offset || inQuotes);
}

//===========================================================================
template<class chartype>
static bool IStrTokenize (const chartype * source[], ARRAY(chartype) * destArray, const chartype whitespace[], unsigned maxWhitespaceSkipCount) {

    // Verify that the destination array is empty
    ASSERT(!destArray->Count());

    // Skip past leading whitespace
    bool inQuotes = false;
    unsigned whitespaceSkipped = 0;
    while (**source && IStrChr(whitespace, **source, (unsigned)-1) && whitespaceSkipped < maxWhitespaceSkipCount) {
        inQuotes = (**source == '\"');
        ++*source;
        ++whitespaceSkipped;
        if (inQuotes)
            break;
    }

    // Copy the token
    bool added = false;
    while (**source &&
           ((inQuotes && (**source != '\"')) || !IStrChr(whitespace, **source, (unsigned)-1))) {
        destArray->Add(**source);
        added = true;
        ++*source;
    }

    // Skip past the terminating quote
    if (inQuotes && (**source == '\"'))
        ++*source;

    // Null terminate the destination array
    destArray->Add(0);

    // Upon return, 'source' is guaranteed to point to the first character
    // following the returned token (and following any closing quotes)

    return (added || inQuotes);
}


/****************************************************************************
*
*   Exported functions
*
***/

//===========================================================================
char * StrDup (const char str[]) {
    return IStrDup(str);
}

//===========================================================================
wchar * StrDup (const wchar str[]) {
    return IStrDup(str);
}

//===========================================================================
char * StrDupLen (const char str[], unsigned chars) {
    return IStrDupLen(str, chars);
}

//===========================================================================
wchar * StrDupLen (const wchar str[], unsigned chars) {
    return IStrDupLen(str, chars);
}

//============================================================================
wchar * StrDupToUnicode (const char str[]) {
    unsigned bytes = StrBytes(str) * sizeof(wchar);
    wchar * dst = (wchar*)ALLOC(bytes);
    StrToUnicode(dst, str, bytes / sizeof(wchar));
    return dst;
}

//============================================================================
char * StrDupToAnsi (const wchar str[]) {
    unsigned bytes = StrBytes(str) / sizeof(wchar);
    char * dst = (char*)ALLOC(bytes);
    StrToAnsi(dst, str, bytes);
    return dst;
}

//===========================================================================
unsigned StrBytes (const char str[]) {  // includes space for terminator
    return (IStrLen(str) + 1) * sizeof(str[0]);
}

//===========================================================================
unsigned StrBytes (const wchar str[]) { // includes space for terminator
    return (IStrLen(str) + 1) * sizeof(str[0]);
}

//===========================================================================
char * StrChr (char * str, char ch, unsigned chars) {
    return IStrChr(str, ch, chars);
}

//===========================================================================
wchar * StrChr (wchar * str, wchar ch, unsigned chars) {
    return IStrChr(str, ch, chars);
}

//===========================================================================
const char * StrChr (const char str[], char ch, unsigned chars) {
    return IStrChr(str, ch, chars);
}

//===========================================================================
const wchar * StrChr (const wchar str[], wchar ch, unsigned chars) {
    return IStrChr(str, ch, chars);
}

//===========================================================================
char * StrChrR (char * str, char ch) {
    return IStrChrR(str, ch);
}

//===========================================================================
wchar * StrChrR (wchar * str, wchar ch) {
    return IStrChrR(str, ch);
}

//===========================================================================
const char * StrChrR (const char str[], char ch) {
    return IStrChrR(str, ch);
}

//===========================================================================
const wchar * StrChrR (const wchar str[], wchar ch) {
    return IStrChrR(str, ch);
}

//===========================================================================
unsigned StrPrintf (char * dest, unsigned count, const char format[], ...) {
    va_list argList;
    va_start(argList, format);
    int result = _vsnprintf((char *)dest, count, (const char *)format, argList);
    va_end(argList);
    return IStrPrintfValidate(dest, count, result);
}

//===========================================================================
unsigned StrPrintf (wchar * dest, unsigned count, const wchar format[], ...) {
    va_list argList;
    va_start(argList, format);
    int result = _vsnwprintf(dest, count, format, argList);
    va_end(argList);
    return IStrPrintfValidate(dest, count, result);
}

//===========================================================================
unsigned StrPrintfV (char * dest, unsigned count, const char format[], va_list args) {
    int result = _vsnprintf(dest, count, format, args);
    return IStrPrintfValidate(dest, count, result);
}

//===========================================================================
unsigned StrPrintfV (wchar * dest, unsigned count, const wchar format[], va_list args) {
    int result = _vsnwprintf(dest, count, format, args);
    return IStrPrintfValidate(dest, count, result);
}

//===========================================================================
int StrCmp (const char str1[], const char str2[], unsigned chars) {
    return IStrCmp(str1, str2, chars);
}

//===========================================================================
int StrCmp (const wchar str1[], const wchar str2[], unsigned chars) {
    return IStrCmp(str1, str2, chars);
}

//===========================================================================
int StrCmpI (const char str1[], const char str2[], unsigned chars) {
    return IStrCmpI(str1, str2, chars);
}

//===========================================================================
int StrCmpI (const wchar str1[], const wchar str2[], unsigned chars) {
    return IStrCmpI(str1, str2, chars);
}

//===========================================================================
void StrCopy (char * dest, const char source[], unsigned chars) {
    IStrCopy(dest, source, chars);
}

//===========================================================================
void StrCopy (wchar * dest, const wchar source[], unsigned chars) {
    IStrCopy(dest, source, chars);
}

//===========================================================================
unsigned StrCopyLen (char * dest, const char source[], unsigned chars) {
    return IStrCopyLen(dest, source, chars);
}

//===========================================================================
unsigned StrCopyLen (wchar * dest, const wchar source[], unsigned chars) {
    return IStrCopyLen(dest, source, chars);
}

//===========================================================================
void StrPack (char * dest, const char source[], unsigned chars) {
    IStrPack(dest, source, chars);
}

//===========================================================================
void StrPack (wchar * dest, const wchar source[], unsigned chars) {
    IStrPack(dest, source, chars);
}

//===========================================================================
char * StrStr (char * source, const char match[]) {
    return IStrStr(source, match);
}

//===========================================================================
const char * StrStr (const char source[], const char match[]) {
    return IStrStr<const char>(source, match);
}

//===========================================================================
wchar * StrStr (wchar * source, const wchar match[]) {
    return IStrStr(source, match);
}

//===========================================================================
const wchar * StrStr (const wchar source[], const wchar match[]) {
    return IStrStr<const wchar>(source, match);
}

//===========================================================================
char * StrStrI (char * source, const char match[]) {
    return IStrStrI(source, match);
}

//===========================================================================
const char * StrStrI (const char source[], const char match[]) {
    return IStrStrI<const char>(source, match);
}

//===========================================================================
wchar * StrStrI (wchar * source, const wchar match[]) {
    return IStrStrI(source, match);
}

//===========================================================================
const wchar * StrStrI (const wchar source[], const wchar match[]) {
    return IStrStrI<const wchar>(source, match);
}

//===========================================================================
unsigned StrLen (const char str[]) {
    return IStrLen(str);
}

//===========================================================================
unsigned StrLen (const wchar str[]) {
    return IStrLen(str);
}

//===========================================================================
unsigned StrUnicodeToUtf8 (char * dest, const wchar source[], unsigned destChars) {
    char * destCurr = dest;
    char * destTerm = dest + destChars;
    while (*source && (destCurr + 1 < destTerm))
        if (!ICharUnicodeToUtf8(&destCurr, &source, destTerm - destCurr - 1))
            break;
    if (destCurr < destTerm)
        *destCurr = 0;
    return destCurr - dest;  // dest chars not including null terminator
}

//===========================================================================
unsigned StrUtf8ToUnicode (wchar * dest, const char source[], unsigned destChars) {
    wchar * destCurr = dest;
    wchar * destTerm = dest + destChars;
    while (*source && (destCurr + 1 < destTerm))
        ICharUtf8ToUnicode(&destCurr, &source);
    if (destCurr < destTerm)
        *destCurr = 0;
    return destCurr - dest;  // dest chars not including null terminator
}

//============================================================================
float StrToFloat (const char source[], const char ** endptr) {
    return (float) strtod(source, const_cast<char **>(endptr));
}

//===========================================================================
float StrToFloat (const wchar source[], const wchar ** endptr) {
    return (float) wcstod(source, const_cast<wchar **>(endptr));
}

//===========================================================================
int StrToInt (const char source[], const char ** endptr) {
    return strtol(source, const_cast<char **>(endptr), 0);
}

//===========================================================================
int StrToInt (const wchar source[], const wchar ** endptr) {
    return wcstol(source, const_cast<wchar **>(endptr), 0);
}

//===========================================================================
unsigned StrToUnsigned (char source[], char ** endptr, int radix) {
    return strtoul(source, const_cast<char **>(endptr), radix);
}

//===========================================================================
unsigned StrToUnsigned (wchar source[], wchar ** endptr, int radix) {
    return wcstoul(source, const_cast<wchar **>(endptr), radix);
}

//===========================================================================
unsigned StrToUnsigned (const char source[], const char ** endptr, int radix) {
    return strtoul(source, const_cast<char **>(endptr), radix);
}

//===========================================================================
unsigned StrToUnsigned (const wchar source[], const wchar ** endptr, int radix) {
    return wcstoul(source, const_cast<wchar **>(endptr), radix);
}

//===========================================================================
void StrLower (char * dest, unsigned chars) {
    IStrLower(dest, chars);
}

//===========================================================================
void StrLower (wchar * dest, unsigned chars) {
    IStrLower(dest, chars);
}

//===========================================================================
void StrLower (char * dest, const char source[], unsigned chars) {
    IStrLower(dest, source, chars);
}

//===========================================================================
void StrLower (wchar * dest, const wchar source[], unsigned chars) {
    IStrLower(dest, source, chars);
}

//============================================================================
dword StrHash (const char str[], unsigned chars) {
    return IStrHash(str, chars);
}

//===========================================================================
dword StrHash (const wchar str[], unsigned chars) {
    return IStrHash(str, chars);
}

//===========================================================================
dword StrHashI (const char str[], unsigned chars) {
    return IStrHashI(str, chars);
}

//===========================================================================
dword StrHashI (const wchar str[], unsigned chars) {
    return IStrHashI(str, chars);
}

//===========================================================================
bool StrTokenize (const char * source[], char * dest, unsigned chars, const char whitespace[], unsigned maxWhitespaceSkipCount) {
    return IStrTokenize(source, dest, chars, whitespace, maxWhitespaceSkipCount);
}

//===========================================================================
bool StrTokenize (const wchar * source[], wchar * dest, unsigned chars, const wchar whitespace[], unsigned maxWhitespaceSkipCount) {
    return IStrTokenize(source, dest, chars, whitespace, maxWhitespaceSkipCount);
}

//===========================================================================
bool StrTokenize (const char * source[], ARRAY(char) * destArray, const char whitespace[], unsigned maxWhitespaceSkipCount) {
    return IStrTokenize(source, destArray, whitespace, maxWhitespaceSkipCount);
}

//===========================================================================
bool StrTokenize (const wchar * source[], ARRAY(wchar) * destArray, const wchar whitespace[], unsigned maxWhitespaceSkipCount) {
    return IStrTokenize(source, destArray, whitespace, maxWhitespaceSkipCount);
}