#include "utils_string.hpp"

#include <sstream>
#include <cmath>
#include <algorithm>

/** \cond NOT_REMOVE_FROM_DOC
*/
namespace utils
{
typedef std::stringstream string_stream;

ustring UTF8_to_unicode(const string& s)
{
    static unsigned char MAX_ANSI = 127;
    static unsigned char ESC_C2   = 194;
    static unsigned char ESC_C3   = 195;

    ustring sResult;

    string::const_iterator iter;
    unsigned char cEscape = 0;

    foreach (iter, s)
    {
        unsigned char c = *iter;
        if (c <= MAX_ANSI)
        {
            sResult.push_back(c);
        }
        else
        {
            if (c == ESC_C2 || c == ESC_C3)
            {
                cEscape = c;
                continue;
            }

            if (cEscape != 0)
            {
                if (cEscape == ESC_C3)
                {
                    // 192 : offset of for "c3" (195) escaped characters (accentuated)
                    // 128 : start offset for these characters (the first one is "c3 80")
                    sResult.push_back(192 + c - 128);
                }
                else if (cEscape == ESC_C2)
                {
                    // 192 : offset of for "c2" (194) escaped characters (misc)
                    // 128 : start offset for these characters (the first one is "c2 80")
                    sResult.push_back(128 + c - 128);
                }
            }
        }
    }

    return sResult;
}

uint UTF8_to_unicode(const char& c)
{
    static unsigned char MAX_ANSI = 127;

    unsigned char uc = (unsigned char)c;

    if (uc <= MAX_ANSI)
        return uc;
    else
        return 0u;
}

string unicode_to_UTF8(const ustring& s)
{
    static uint MAX_ANSI = 127;
    static uint ESC_C2   = 194;
    static uint ESC_C3   = 195;

    string sResult;

    ustring::const_iterator iter;

    foreach (iter, s)
    {
        uint c = *iter;
        if (c <= MAX_ANSI)
        {
            sResult.push_back(c);
        }
        else
        {
            if (c < 192)
            {
                sResult.push_back((unsigned char)ESC_C2);
                sResult.push_back((unsigned char)(128 + c - 128));
            }
            else
            {
                sResult.push_back((unsigned char)ESC_C3);
                sResult.push_back((unsigned char)(128 + c - 192));
            }
        }
    }

    return sResult;
}

char unicode_to_UTF8(const uint& c)
{
    static uint MAX_ANSI = 127;
    if (c <= MAX_ANSI)
        return (unsigned char)c;
    else
        return '\0';
}

uint hex_to_uint(const string& s)
{
    uint i;
    string_stream ss;
    ss << s;
    ss >> std::hex >> i;
    return i;
}

long string_to_int(const string& s)
{
    long i;
    string_stream ss(s);
    ss >> i;
    return i;
}

long string_to_int(const ustring& s)
{
    return string_to_int(unicode_to_UTF8(s));
}

unsigned long string_to_uint(const string& s)
{
    unsigned long ui;
    string_stream ss(s);
    ss >> ui;
    return ui;
}

unsigned long string_to_uint(const ustring& s)
{
    return string_to_uint(unicode_to_UTF8(s));
}

double string_to_float(const string& s)
{
    double d;
    string_stream ss(s);
    ss >> d;
    return d;
}

double string_to_float(const ustring& s)
{
    return string_to_float(unicode_to_UTF8(s));
}

string int_to_string(const long& i)
{
    string_stream sStream;
    sStream << i;
    return sStream.str();
}

string uint_to_string(const unsigned long& ui)
{
    string_stream sStream;
    sStream << ui;
    return sStream.str();
}

string float_to_string(float f)
{
    string_stream sStream;
    //sStream.precision(s_float_t<T>::DIGIT);
    sStream << f;
    return sStream.str();
}

string double_to_string(double d)
{
    string_stream sStream;
    //sStream.precision(s_float_t<T>::DIGIT);
    sStream << d;
    return sStream.str();
}

string to_string(const string& s, uint uiNbr)
{
    string sReturn;

    for (uint uiCounter = 0; uiCounter < uiNbr; ++uiCounter)
        sReturn += s;

    return sReturn;
}

string to_string(const char* s)
{
    return s;
}

string to_string(const char* s, uint uiNbr)
{
    string sReturn;

    for (uint uiCounter = 0; uiCounter < uiNbr; ++uiCounter)
        sReturn += s;

    return sReturn;
}

string to_string(char* s)
{
    return s;
}

string to_string(char* s, uint uiNbr)
{
    string sReturn;

    for (uint uiCounter = 0; uiCounter < uiNbr; ++uiCounter)
        sReturn += s;

    return sReturn;
}

string to_string(char c)
{
    string s;
    s.push_back(c);
    return s;
}

string to_string(char c, uint uiNbr)
{
    return string(uiNbr, c);
}

string to_string(int i)
{
    return int_to_string(i);
}

string to_string(int i, uint uiCharNbr)
{
    string sReturn;

    if (i >= 0)
    {
        sReturn = to_string(i);

        while (sReturn.length() < uiCharNbr)
            sReturn = '0' + sReturn;
    }
    else
    {
        sReturn = to_string(-i);

        while (sReturn.length() < uiCharNbr)
            sReturn = '0' + sReturn;

        sReturn = '-' + sReturn;
    }

    return sReturn;
}

string to_string(uint ui)
{
    return uint_to_string(ui);
}

string to_string(uint ui, uint uiCharNbr)
{
    string sReturn = to_string(ui);

    while (sReturn.length() < uiCharNbr)
        sReturn = '0' + sReturn;

    return sReturn;
}

string to_string(float f)
{
    return float_to_string(f);
}

string to_string(float f, uint uiDigitNbr)
{
    string sReturn;

    float fTemp = f;
    if (fTemp < 0.0f)
    {
        sReturn.push_back('-');
        fTemp *= -1.0f;
    }

    if (fTemp < 1.0f)
    {
        sReturn += "0.";

        uint uiZeroes = 0;

        do
        {
            fTemp *= 10.0f;
            ++uiZeroes;
        }
        while (fTemp < 1.0f);

        sReturn.append(uiZeroes, '0');

        for (uint i = 0; i < uiDigitNbr; ++i)
            fTemp *= 10.0f;

        sReturn += to_string((uint)fTemp);
    }
    else
    {
        float fTemp2 = floor(fTemp);
        uint uiInt = (uint)fTemp2;
        sReturn += to_string(uiInt);

        uint uiSize = sReturn.size();
        if (sReturn[0] == '-')
            --uiSize;

        if (uiSize > uiDigitNbr)
        {
            uint uiExcess = uiSize - uiDigitNbr;

            sReturn.erase(sReturn.length() - uiExcess, uiExcess);
            sReturn.append(uiExcess, '0');
            return sReturn;
        }

        sReturn.push_back('.');

        uint uiRemaining = uiDigitNbr - uiSize;

        fTemp2 = fTemp - fTemp2;
        for (uint i = 0; i < uiRemaining; ++i)
            fTemp2 *= 10.0f;

        sReturn += to_string((uint)fTemp2, uiRemaining);
    }

    return sReturn;
}

string to_string(float f, uint uiIntCharNbr, uint uiFracCharNbr)
{
    string sReturn;

    float fTemp = f;
    if (fTemp < 0.0f)
    {
        sReturn.push_back('-');
        fTemp *= -1.0f;
    }

    float fTemp2 = floor(fTemp);

    sReturn = to_string((uint)fTemp2, uiIntCharNbr);

    if (uiFracCharNbr > 0)
    {
        fTemp2 = fTemp - fTemp2;
        for (uint i = 0; i < uiFracCharNbr; ++i)
            fTemp2 *= 10.0f;

        sReturn.push_back('.');
        sReturn += to_string((uint)fTemp2, uiFracCharNbr);
    }

    return sReturn;
}

string to_string(double f)
{
    return double_to_string(f);
}

string to_string(double f, uint uiDigitNbr)
{
    string sReturn;

    double fTemp = f;
    if (fTemp < 0.0)
    {
        sReturn.push_back('-');
        fTemp *= -1.0;
    }

    if (fTemp < 1.0)
    {
        sReturn += "0.";

        uint uiZeroes = 0;

        do
        {
            fTemp *= 10.0;
            ++uiZeroes;
        }
        while (fTemp < 1.0);

        sReturn.append(uiZeroes, '0');

        for (uint i = 0; i < uiDigitNbr; ++i)
            fTemp *= 10.0;

        sReturn += to_string((uint)fTemp);
    }
    else
    {
        double fTemp2 = floor(fTemp);
        uint uiInt = (uint)fTemp2;
        sReturn += to_string(uiInt);

        uint uiSize = sReturn.size();
        if (sReturn[0] == '-')
            --uiSize;

        if (uiSize > uiDigitNbr)
        {
            uint uiExcess = uiSize - uiDigitNbr;

            sReturn.erase(sReturn.length() - uiExcess, uiExcess);
            sReturn.append(uiExcess, '0');
            return sReturn;
        }

        sReturn.push_back('.');

        uint uiRemaining = uiDigitNbr - uiSize;

        fTemp2 = fTemp - fTemp2;
        for (uint i = 0; i < uiRemaining; ++i)
            fTemp2 *= 10.0;

        sReturn += to_string((uint)fTemp2, uiRemaining);
    }

    return sReturn;
}

string to_string(double f, uint uiIntCharNbr, uint uiFracCharNbr)
{
    string sReturn;

    double fTemp = f;
    if (fTemp < 0.0)
    {
        sReturn.push_back('-');
        fTemp *= -1.0;
    }

    double fTemp2 = floor(fTemp);

    sReturn = to_string((uint)fTemp2, uiIntCharNbr);

    if (uiFracCharNbr > 0)
    {
        fTemp2 = fTemp - fTemp2;
        for (uint i = 0; i < uiFracCharNbr; ++i)
            fTemp2 *= 10.0;

        sReturn.push_back('.');
        sReturn += to_string((uint)fTemp2, uiFracCharNbr);
    }

    return sReturn;
}

string to_string(bool b)
{
    return b ? "true" : "false";
}

void trim(string& s, char cPattern)
{
    while (!s.empty() && s[0] == cPattern)
        s.erase(0, 1);

    while (!s.empty() && s.back() == cPattern)
        s.erase(s.size()-2, 1);
}

std::vector<string> cut(const std::string& s, const std::string& sDelim)
{
    std::vector<string> lPieces;
    size_t uiPos = s.find(sDelim);
    size_t uiLastPos = 0u;
    size_t uiCurSize = 0u;

    while (uiPos != string::npos)
    {
        uiCurSize = uiPos - uiLastPos;
        if (uiCurSize != 0)
            lPieces.push_back(s.substr(uiLastPos, uiCurSize));
        uiLastPos = uiPos + sDelim.size();
        uiPos = s.find(sDelim, uiLastPos);
    }

    lPieces.push_back(s.substr(uiLastPos));

    return lPieces;
}

std::vector<string> cut(const string& s, const string& sDelim, uint uiMaxCut)
{
    std::vector<string> lPieces;
    size_t uiPos = s.find(sDelim);
    size_t uiLastPos = 0u;
    size_t uiCurSize = 0u;
    uint uiCount = 0u;

    while (uiPos != string::npos)
    {
        uiCurSize = uiPos - uiLastPos;
        if (uiCurSize != 0)
            lPieces.push_back(s.substr(uiLastPos, uiCurSize));
        uiLastPos = uiPos + sDelim.size();
        uiPos = s.find(sDelim, uiLastPos);
        ++uiCount;

        if (uiCount >= uiMaxCut)
            break;
    }

    lPieces.push_back(s.substr(uiLastPos));

    return lPieces;
}

std::vector<string> cut_each(const string& s, const string& sDelim)
{
    std::vector<string> lPieces;
    size_t uiPos = s.find(sDelim);
    size_t uiLastPos = 0u;
    size_t uiCurSize = 0u;

    while (uiPos != string::npos)
    {
        uiCurSize = uiPos - uiLastPos;
        lPieces.push_back(s.substr(uiLastPos, uiCurSize));
        uiLastPos = uiPos + sDelim.size();
        uiPos = s.find(sDelim, uiLastPos);
    }

    lPieces.push_back(s.substr(uiLastPos));

    return lPieces;
}

std::vector<string> cut_each(const string& s, const string& sDelim, uint uiMaxCut)
{
    std::vector<string> lPieces;
    size_t uiPos = s.find(sDelim);
    size_t uiLastPos = 0u;
    size_t uiCurSize = 0u;
    uint uiCount = 0u;

    while (uiPos != string::npos)
    {
        uiCurSize = uiPos - uiLastPos;
        lPieces.push_back(s.substr(uiLastPos, uiCurSize));
        uiLastPos = uiPos + sDelim.size();
        uiPos = s.find(sDelim, uiLastPos);

        if (uiCount >= uiMaxCut)
            break;
    }

    lPieces.push_back(s.substr(uiLastPos));

    return lPieces;
}

std::vector<string> cut(const string& s, const std::vector<char>& lDelims)
{
    std::vector<string> lPieces;

    string sTemp;
    string::const_iterator iter;
    foreach (iter, s)
    {
        if (std::find(lDelims.begin(), lDelims.end(), *iter) != lDelims.end())
        {
            if (!sTemp.empty())
            {
                lPieces.push_back(sTemp);
                sTemp.clear();
            }
        }
        else
            sTemp.push_back(*iter);
    }

    if (!sTemp.empty())
        lPieces.push_back(sTemp);

    return lPieces;
}
}
