#include "base/AT_StringFunctions.h"
#include <algorithm>
#include <windows.h>

//AZ:---------------------------------------------------------------------------

byte * UTL_String::GetToken(byte * str, uint & len, const byte * sep)
{
    if(!str || !*str || !sep)
        {quit: len = 0; return NULL; }
    if(len > 0)
        {
        str += len; // jump to the next
        len = 0;
        }
    // skip leading separators
    for(;*str && strchr((char *)sep, *str); ++str)
        ; // skip separators
    if(!*str)
        goto quit;
    // find end of the token:
    char *p;
    for(p = (char *)str;*p && !strchr((char *)sep, *p); ++len, ++p)
        ; // skip non separators
    for(;*p && strchr((char *)sep, *p); ++len, ++p)
        *p=0; // patch & skip separators

return str;
}
//---------------------------------------------------------------------------

uint UTL_String::ReplaceFromList(std::string & w, const byte * replace_list, const byte rplace_char,
    uint stop_len)
{
    uint replace_cnt = 0;
    if(!replace_list)
        return replace_cnt;

    std::string rlist((char *)replace_list);
    const char * b = w.c_str();
    while(*b)
        {
        uint pos = rlist.find(*b);
        if(pos != std::string::npos)
            {
            uint str_pos = b - w.c_str();
            if(rplace_char)
                w.replace(str_pos, 1, 1, (char)rplace_char);
            else
                {
                w.erase(str_pos, 1);
                if(str_pos)
                    --str_pos;
                }
            b = w.c_str() + str_pos;
            ++replace_cnt;
            if(w.length() <= stop_len)
                return replace_cnt;
            }
        ++b;
        }
return replace_cnt;
}
//---------------------------------------------------------------------------

uint UTL_String::ReplaceStrFromDelimList(std::string & w, const byte * delim_list,
  const byte * list_sep, const byte * rplace_str)
{
    uint replace_cnt = 0;
    if(!delim_list || !list_sep)
        return replace_cnt;

    uint rcnt;
    do
        {
        rcnt = 0;
        uint list_len = 0;
        std::string rlist((char *)delim_list);
        byte * rstr = (byte *)rlist.c_str();
        uint wlen = w.length();
        for(rstr = GetToken(rstr, list_len, list_sep); rstr; rstr = GetToken(rstr, list_len, list_sep))
            {
            uint rlen = std::strlen((char *)rstr);
            if(rlen)
                {
                if(IsAllString(rstr))
                    {
                    if(rplace_str)
                        w = (char *)rplace_str;
                    else
                        w.clear();
                    return wlen;
                    }
                uint fpos = FindInStrFromDelimList(w, rstr, (const byte *)"\0", 0);
                while(fpos != std::string::npos)
                    {
                    if(rplace_str)
                        w.replace(fpos, rlen, (char *)rplace_str);
                    else
                        w.erase(fpos, rlen);

                    ++replace_cnt;
                    ++rcnt;
                    fpos = FindInStrFromDelimList(w, rstr, (const byte *)"\0", fpos);
                    }
                }
            }
        }
    while (rcnt > 0);

return replace_cnt;
}
//---------------------------------------------------------------------------

uint UTL_String::RemoveStrFromDelimList(std::string & w, const byte * replace_list,
    const byte * list_sep)
{
    return ReplaceStrFromDelimList(w, replace_list, list_sep);
}
//---------------------------------------------------------------------------

uint UTL_String::FindInStr(std::string & w, const byte * str, uint pos)
{
    if(!str)
        return std::string::npos;
    if(!*str)
        return w.length();

    ic c(0);
    const byte * e = str + std::strlen((char *)str);
	std::string::iterator p = std::search(w.begin() + pos, w.end(), (const char *)str, (const char *)e, c);

    if(p != w.end())
        return p - w.begin();
        
return std::string::npos;
}
//---------------------------------------------------------------------------

uint UTL_String::CountInStr(std::string & w, const byte * str, uint pos)
{
    uint p;
    uint cnt = 0;
    for(p = FindInStr(w, str, pos); p != std::string::npos; p = FindInStr(w, str, p + 1), ++cnt );

return cnt;    
}
//---------------------------------------------------------------------------

uint UTL_String::FindInStrFromDelimList(std::string & w, const byte * delim_list,
        const byte * list_sep, uint pos)
{
    if(!delim_list || !list_sep)
        return std::string::npos;

    std::string inclusions((char *)delim_list);
    uint in_len = 0;
    byte * sep = (byte *)inclusions.c_str();
    for(sep = GetToken(sep, in_len, list_sep); sep; sep = GetToken(sep, in_len, list_sep))
        {
        if(IsAllString(sep))
            return w.length() - 1;
        uint pp = FindInStr(w, sep, pos);
        if(pp != std::string::npos)
            return pp;
        }

return std::string::npos;
}
//---------------------------------------------------------------------------

uint UTL_String::InsertBefore(std::string & w, const byte * sbefore, const byte * s, const byte * sep)
{
    std::string temp;
    if(sep)
        temp = (char *)sep;
    temp += (char *)s;

    uint ipos = w.find((char *)sbefore);
    if(ipos == std::string::npos)
        w += temp;
    else
        w.insert(ipos, temp);

return w.length();
}
//---------------------------------------------------------------------------

uint UTL_String::RInsertBefore(std::string & w, const byte * sbefore, const byte * s, const byte * sep)
{
    std::string temp;
    if(sep)
        temp = (char *)sep;
    temp += (char *)s;

    uint ipos = w.rfind((char *)sbefore);
    if(ipos == std::string::npos)
        w += temp;
    else
        w.insert(ipos, temp);

return w.length();
}
//---------------------------------------------------------------------------

uint UTL_String::RemoveFromList(std::string & w, const byte * replace_list, uint stop_len)
{
return ReplaceFromList(w, replace_list, NULL, stop_len);
}
//---------------------------------------------------------------------------

uint UTL_String::GetSubStrRight(const std::string & w, const byte * find, std::string & res)
{
    res.clear();

    if(!find)
        return std::string::npos;
    uint pos = w.find((char *)find);
    if(pos == std::string::npos)
        return pos;

    uint find_len = std::strlen((const char *)find);
    res = w.substr(pos + find_len);

return pos;
}
//---------------------------------------------------------------------------

uint UTL_String::RGetSubStrRight(const std::string & w, const byte * find, std::string & res)
{
    res.clear();

    if(!find)
        return std::string::npos;
    uint pos = w.rfind((char *)find);
    if(pos == std::string::npos)
        return pos;

    uint find_len = std::strlen((const char *)find);
    res = w.substr(pos + find_len);

return pos;
}
//---------------------------------------------------------------------------

uint UTL_String::GetSubStrLeft(const std::string & w, const byte * find, std::string & res)
{
    res.clear();

    if(!find)
        return std::string::npos;
    uint pos = w.find((char *)find);
    if(pos == std::string::npos)
        return pos;

    res = w.substr(0, pos);

return pos;
}
//---------------------------------------------------------------------------

uint UTL_String::RGetSubStrLeft(const std::string & w, const byte * find, std::string & res)
{
    res.clear();

    if(!find)
        return std::string::npos;
    uint pos = w.rfind((char *)find);
    if(pos == std::string::npos)
        return pos;

    res = w.substr(0, pos);

return pos;
}
//---------------------------------------------------------------------------

uint UTL_String::GetTokBetween(uint pos, const std::string & w, const byte * delims,
        const byte * between, std::string & res)
{
    res.clear();
    
    if(!delims)
        // no delims
        return std::string::npos;

    if(pos == std::string::npos)
        // reset search
        pos = 0;

    uint len = w.length();
    if(pos >= len)
        // invalid pos
        return std::string::npos;

    const byte * s = (const byte *)w.c_str() + pos;
    len -= pos;
    uint ignore = 0;
    bool append;

    while(len)
        {
        if(between && std::strchr((const char *)between, *s))
            {
            ++ignore;
            append = false;
            }
        else
            append = true;
            
        if(ignore > 1 && std::strchr((const char *)delims, *s))
            break;

        if(append)
            res.append(1, *s);

        ++s;
        --len;
        }

return (const char *)s - w.c_str();
}
//---------------------------------------------------------------------------

void UTL_String::CheckPath(std::string & path)
{
    uint len = path.length();
    if(!len)
        return;
    uint pos = path.rfind('\\');
    if((pos == std::string::npos) || (pos != len - 1))
        path += "\\";
}
//---------------------------------------------------------------------------

std::string UTL_String::CheckPath(const byte * path)
{
    std::string temp(path ? (char *)path : "");
    CheckPath(temp);
return temp;
}
//---------------------------------------------------------------------------

bool UTL_String::GetBool(const byte * str)
{
    if(!str || !*str)
        return false;
    if(!_stricmp((const char *)str,"y") ||
        !_stricmp((const char *)str,"yes") ||
        !_stricmp((const char *)str,"on") ||
        !_stricmp((const char *)str,"true"))
        return true;

    if(isdigit(*str))
        return atoi((const char *)str);

return false;
}
//---------------------------------------------------------------------------

uint UTL_String::GetFullPathNameStr(std::string & fname)
{
    if(fname.find(":") != std::string::npos)
        return fname.length();

    if(fname.find("\\\\") == 0)
        // UNC path - return
        return fname.length();

    if(fname.find("\\") == 0)
        {
        // add current drive letter
        byte drive[255];
        uint sz = GetCurrentDirectoryA(sizeof(drive), (char *)drive);
        byte * colon = (byte *)std::strchr((char *)drive, ':');
        if(colon)
            {
            ++colon;
            *colon = 0;
            }
        if(sz)
            fname.insert(0, (char *)drive);
        }

return fname.length();        
}
//---------------------------------------------------------------------------

bool UTL_String::IsNumeric(const byte * str)
{
    if(!str)
        return false;

    while(*str)
        {
        if(!isdigit(*str))
            return false;
        ++str;
        }
        
return true;
}
//---------------------------------------------------------------------------

bool UTL_String::IsBool(const byte * str)
{
    if(!str)
        return false;

    uint len = std::strlen((const char *)str);
    if(!_strnicmp((const char *)str, "true", max(4, len)))
        return true;
    if(!_strnicmp((const char *)str, "yes", max(3, len)))
        return true;
    if(!_strnicmp((const char *)str, "y", max(1, len)))
        return true;
    if(!_strnicmp((const char *)str, "on", max(2, len)))
        return true;
    if(!_strnicmp((const char *)str, "false", max(5, len)))
        return true;
    if(!_strnicmp((const char *)str, "no", max(2, len)))
        return true;
    if(!_strnicmp((const char *)str, "n", max(1, len)))
        return true;
    if(!_strnicmp((const char *)str, "off", max(3, len)))
        return true;
    if(IsNumeric(str))
        return true;

return false;
}
//---------------------------------------------------------------------------

uint UTL_String::RTrim(std::string & w, const byte * ignore_chars)
{
    // remove trailing spaces
    uint len = w.length();
    const char * ic = ignore_chars ? (const char *)ignore_chars : DEFAULT_TRIM;

    while(len && std::strchr(ic, w[len - 1]))
        {
        w.erase(len - 1, 1);
        --len;
        }
        
return len;
}
//---------------------------------------------------------------------------

uint UTL_String::LTrim(std::string & w, const byte * ignore_chars)
{
    // remove leading spaces
    const char * ic = ignore_chars ? (const char *)ignore_chars : DEFAULT_TRIM;
    uint i, len = w.length();
    for(i = 0; i < len && std::strchr(ic, w[i]); ++i);
    if(i)
        w.erase(0, i);
                
return w.length();
}
//---------------------------------------------------------------------------

uint UTL_String::Trim(std::string & w, const byte * ignore_chars)
{
    RTrim(w, ignore_chars);
    LTrim(w, ignore_chars);

return w.length();
}
//---------------------------------------------------------------------------

const byte * UTL_String::LTrim(const byte *in, uint *out_len, uint in_len, const byte * ignore_chars)
{
    // remove leading spaces
    const char * ic = ignore_chars ? (const char *)ignore_chars : DEFAULT_TRIM;
    uint len = in_len ? in_len : strlen((const char *)in);
    uint c;
    for (c = 0; c < len && std::strchr(ic, in[c]); ++c);

    *out_len = len - c;
    if (c == len)
        return NULL;

return &in[c];
}
//---------------------------------------------------------------------------

const byte * UTL_String::RTrim(const byte *in, uint *out_len, uint in_len, const byte * ignore_chars)
{
    const char * ic = ignore_chars ? (const char *)ignore_chars : DEFAULT_TRIM;
    int len = in_len ? in_len : strlen((const char *)in);
    int c;
    for (c = len - 1; c >= 0 && std::strchr(ic, in[c]); --c);

    if (c == 0 && std::strchr(ic, in[c]))
        return NULL;

    // in[c + 1] = 0;
    *out_len = c + 1;

return in;
}
//---------------------------------------------------------------------------

const byte * UTL_String::Trim(const byte *in, uint *out_len, uint in_len, const byte * ignore_chars)
{
    byte *data = (byte *)LTrim((byte *)in, out_len, in_len, ignore_chars);
    if (data)
        return RTrim(data, out_len, *out_len, ignore_chars);

return NULL;
}
//---------------------------------------------------------------------------

bool UTL_String::ParseEnvLine(byte * line, byte *& key, byte *& val)
{
    if(!line)
        return false;

    key = NULL;
    if(!std::strchr((char *)line, '='))
        return true;

    uint len = 0;
    key = GetToken(line, len, (const byte *)"=");
    if(!key)
        return true;
    line += len;
    len = 0;
    val = GetToken(line, len, (const byte *)"=");
    if(!val)
        return true;

    len = std::strlen((const char *)key);
    uint l = len;
    // trim key
    for(; l && !UTL_String::IsBlank(key[l - 1]); --l)
    ;
    if(l == len)
        {
        key = NULL;
        return true;
        }
    key += l;

    len = std::strlen((const char *)val);
    // trim val
    for(l = len; l && UTL_String::IsBlank(val[l - 1]); --l)
    ;
    val[l] = 0;

return true;
}
//---------------------------------------------------------------------------

uint UTL_String::FindKey(const std::string * senv, uint sz, const byte * key)
{
    if(!senv)
        return MAXUINT;

    uint i;
    for(i = 0; i < sz; ++i)
        {
        if(CmpEnv((const byte *)senv[i].c_str(), key))
            return i;
        }

return MAXUINT;
}
//---------------------------------------------------------------------------

uint UTL_String::FindKey(const byte ** senv, uint sz, const byte * key)
{
    if(!senv)
        return MAXUINT;

    uint i;
    for(i = 0; i < sz; ++i)
        {
        if(CmpEnv(senv[i], key))
            return i;
        }

return MAXUINT;
}
//---------------------------------------------------------------------------

bool UTL_String::CmpEnv(const byte * env, const byte * key)
{
    if(!env)
        return false;
        
    std::string s((char *)env);
    uint sl = 0;
    const byte * ekey = GetToken((byte *)s.c_str(), sl, (const byte *)"=");
return CmpString(ekey, key);
}

uint UTL_String::PrintToString(std::string & str, const byte * fmt, ...)
{
    if(fmt)
        {
        va_list arg;
        va_start(arg, fmt);
        uint sz = vsnprintf(NULL, 0, (const char *)fmt, arg);
        if(sz)
            {
            char * buf = new char [sz + 1];
            sz = vsnprintf(buf, sz + 1, (const char *)fmt, arg);
            str = buf;
            delete [] buf;
            }
        else
            str.clear();

        va_end(arg);

        return sz;
        }

    str.clear();

return 0;
}
//---------------------------------------------------------------------------

byte * UTL_String::CheckName(const byte * name, uint max_len, byte q,
    const char * illigalchars, const byte * sep, byte * buf)
{
    if(!name || !max_len)
        return NULL;
        
    uint mlen = max_len;
    uint pos = 0;
    std::string temp((char *)name);
    if(name[0] == q )
        temp.assign((char *)name + 1, temp.length() - 2);

    ReplaceStrFromDelimList(temp, (const byte *)illigalchars, sep, (const byte *)"_");

    if(temp.length() > mlen)
        UTL_String::RemoveFromList(temp, (const byte *)"-", mlen);
    if(temp.length() > mlen)
        UTL_String::RemoveFromList(temp, (const byte *)"_", mlen);
    if(temp.length() > mlen)
        UTL_String::RemoveFromList(temp, (const byte *)" ", mlen);

    while(temp.length() > mlen)
        temp.erase(pos, 1);

    if(buf)
        strcpy((char *)buf, temp.c_str());

return buf;
}

//---------------------------------------------------------------------------
// eof
