#include <strtool.h>

#include <wctype.h>

char* StrTool::new_str(const char* s, int l)
{
    if (l == 0)
        l = strlen(s);
    char* news = new char[l+1];
    strncpy(news, s, l);
    news[l] = 0;
    
    return news;
}

wchar_t* StrTool::new_str(const wchar_t* s, int l)
{
    if (l == 0)
        l = wcslen(s);
    wchar_t* news = new wchar_t[l+1];
    wcsncpy(news, s, l);
    news[l] = 0;
    
    return news;
}

char** StrTool::tokenize(char* s, char sep, int* count)
{
    int nTokens = 1;
    // count the number of tokens
    char* p = s;
    while (*p) {
        if (*p == sep)
            nTokens++;
        p++;
    }
    *count = nTokens;
    
    char** tokens = new char*[nTokens];
    memset(tokens, 0, sizeof(char*)*nTokens);
    if (nTokens == 1) {
        tokens[0] = s;
        return tokens;
    }
    
    int ct = 0;
    p = s;
    while (*p) {
        char* x = p;
        while (*x && *x != sep)
            x++;
        tokens[ct++] = (x-p) ? p : NULL;
        if (*x) {
            *x = 0;
            p = x+1;
        } else 
            p = x;
    }
    
    return tokens;
}

int StrTool::tokenize(char* s, char sep, char** tokens, int maxt)
{
    int nTokens = 1;
    // count the number of tokens
    char* p = s;
    while (*p) {
        if (*p == sep)
            nTokens++;
        p++;
    }
    if (nTokens > maxt)
        return -1;
    
    memset(tokens, 0, sizeof(char*)*nTokens);
    if (nTokens == 1) {
        tokens[0] = s;
        return 1;
    }
    
    int ct = 0;
    p = s;
    while (*p) {
        char* x = p;
        while (*x && *x != sep)
            x++;
        tokens[ct++] = (x-p) ? p : NULL;
        if (*x) {
            *x = 0;
            p = x+1;
        } else 
            p = x;
    }
    
    return nTokens;
}

int StrTool::levenshtein(const char *str1, const char *str2, bool confLevel)
{
    unsigned int i, j, diagonal, cost, s1len, s2len;
    unsigned int *arr;
    
    s1len = strlen(str1);
    s2len = strlen(str2);
    
    if (s1len * s2len == 0)
        return (confLevel) ? 0 : s1len + s2len;
    
    j = s1len + 1;
    arr = new unsigned[s1len + 1];
    
    for (i = 0; i < j; i ++)
        arr[i] = i + 1;
    
    for (i = 0; i < s2len; i ++)
    {
        diagonal = arr[0] - 1;
        arr[0] = i + 1;
        j = 0;
        while (j < s1len)
        {
            cost = diagonal;
            if (str1[j] != str2[i])
                cost ++;
            if (cost > arr[j])
                cost = arr[j];
            j++;
            if (cost > arr[j])
                cost = arr[j];
            diagonal = arr[j] - 1;
            arr[j] = cost + 1;
        }
    }
    
    cost = arr[j] - 1;
    delete [] arr;
    
    return (confLevel) ? 100*(s1len + s2len - cost) / (s1len + s2len) : cost;
}

int StrTool::utf8ToUtf16(wchar_t* out, int outlen, unsigned char* in, int inlen)
{
    wchar_t* outstart = out;
    wchar_t* outend = out+outlen;
    unsigned char* inend = in + (inlen ? inlen : strlen((char*)in));
    unsigned int c, d, trailing;

    while (in < inend) {
        d = *in++;
        if (d < 0x80) {
            c = d; 
            trailing = 0;
        } else if (d < 0xC0) 
            return -2;    // trailing byte in leading position
        else if (d < 0xE0) { 
            c = d & 0x1F; 
            trailing = 1;
        } else if (d < 0xF0) { 
            c = d & 0x0F; 
            trailing = 2; 
        } else if (d < 0xF8) { 
            c = d & 0x07; 
            trailing= 3;
        } else 
            return -2;    // no chance for this in UTF-16
    
        for ( ; trailing; trailing--) {
            if ((in >= inend) || (((d= *in++) & 0xC0) != 0x80))
                return -1;
            c <<= 6;
            c |= d & 0x3F;
        }
    
        // assertion: c is a single UTF-4 value 
        if (c < 0x10000) {
            if (out >= outend)
                return -1;
            *out++ = c;
        } else if (c < 0x110000) {
            if (out+1 >= outend)
                return -1;
            c -= 0x10000;
            *out++ = 0xD800 | (c >> 10);
            *out++ = 0xDC00 | (c & 0x03FF);
        } else
            return -1;
    }
    
    *out = 0;
    return out-outstart;
}

int StrTool::utf8ToUtf16(uchar16_t* out, int outlen, unsigned char* in, int inlen)
{
    uchar16_t* outstart = out;
    uchar16_t* outend = out+outlen;
    unsigned char* inend = in +  (inlen ? inlen : strlen((char*)in));
    unsigned int c, d, trailing;

    while (in < inend) {
        d = *in++;
        if (d < 0x80) {
            c = d; 
            trailing = 0;
        } else if (d < 0xC0) 
            return -2;    // trailing byte in leading position
        else if (d < 0xE0) { 
            c = d & 0x1F; 
            trailing = 1;
        } else if (d < 0xF0) { 
            c = d & 0x0F; 
            trailing = 2; 
        } else if (d < 0xF8) { 
            c = d & 0x07; 
            trailing= 3;
        } else 
            return -2;    // no chance for this in UTF-16
    
        for (; trailing; trailing--) 
        {
            if ((in >= inend) || (((d= *in++) & 0xC0) != 0x80))
                return -1;
            c <<= 6;
            c |= d & 0x3F;
        }
    
        // assertion: c is a single UTF-4 value 
        if (c < 0x10000) 
        {
            if (out >= outend)
                return -1;
            *out++ = c;
        } 
        else
            return -1;
    }
    
    *out = 0;
    return out-outstart;
}

int StrTool::utf8ToUtf16Lower(wchar_t* out, int outlen, unsigned char* in, int inlen)
{
    wchar_t* outstart = out;
    wchar_t* outend = out+outlen;
    unsigned char* inend = in+inlen;
    unsigned int c, d, trailing;

    while (in < inend) {
        d = *in++;
        if (d < 0x80) {
            c = d; 
            trailing = 0;
        } else if (d < 0xC0) 
            return -2;    // trailing byte in leading position
        else if (d < 0xE0) { 
            c = d & 0x1F; 
            trailing = 1;
        } else if (d < 0xF0) { 
            c = d & 0x0F; 
            trailing = 2; 
        } else if (d < 0xF8) { 
            c = d & 0x07; 
            trailing= 3;
        } else 
            return -2;    // no chance for this in UTF-16
    
        for ( ; trailing; trailing--) {
            if ((in >= inend) || (((d= *in++) & 0xC0) != 0x80))
                return -1;
            c <<= 6;
            c |= d & 0x3F;
        }
    
        // assertion: c is a single UTF-4 value
        if (c < 0x10000) {
            if (out >= outend)
                return -1;
            c = towlower(c);
            *out++ = c;
        } else if (c < 0x110000) {
            if (out+1 >= outend)
                return -1;
            c -= 0x10000;
            *out++ = 0xD800 | (c >> 10);
            *out++ = 0xDC00 | (c & 0x03FF);
        } else
            return -1;
    }
    
    *out = 0;
    return out-outstart;
}

int StrTool::u16CharToUtf8(unsigned ucode, char* uc)
{
    int l = 0;
    if (ucode < 128) {
        uc[l++] = ucode;
    } else if (ucode < 1024) {
        uc[l++] = (ucode >> 6) + 192;
        uc[l++] = (ucode & 63) + 128;
    } else if (ucode < 32768) {
        uc[l++] = (ucode >> 12) + 224;
        uc[l++] = ((ucode >> 6) & 63) + 128;
        uc[l++] = (ucode & 63) + 128;
    } else if (ucode < 2097152) {
        uc[0] = (ucode >> 18) + 240;
        uc[1] = ((ucode >> 12) & 63) + 128;
        uc[2] = (ucode >> 6) & 63 + 128;
        uc[3] = (ucode & 63) + 128;
        l = 4;
    }
    return l;
}

int StrTool::utf16ToUtf8(char* u8s, const uchar16_t* u16s)
{
    char uc[6];
    
    int soi = 0;
    u8s[0] = 0;
    const uchar16_t *p = u16s;
    while (*p) 
    {
        uchar16_t c = *p;

        int l = u16CharToUtf8(c, uc);
        for (int j = 0; j < l; j++)
            if (uc[j] != '?') 
                u8s[soi++] = uc[j];

        p++;
    }
    u8s[soi] = 0;

    return soi;
}

void StrTool::wcsupr(wchar_t* ws)
{
    wchar_t* wc = ws;
    while (*wc) {
        *wc = towupper(*wc);
        wc++;
    }
}

void StrTool::wcslwr(wchar_t* ws)
{
    wchar_t* wc = ws;
    while (*wc) {
        *wc = towlower(*wc);
        wc++;
    }
}
