#include "util/simpleobject/AgString.h"
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/functional/hash.hpp>
#include "IllegalArgumentException.h"
#include "util/utf8/utf8.h"


AgString::AgString()
{
}

AgString::AgString(const char * str)
{
	int len = strlen(str);
	reserve(len);
    utf8::utf8to16(str, str + len, back_inserter(*this));
}
AgString::AgString(const AgChar * str) : utf16string(str)
{

}


AgString::AgString(const std::string &str)
{
	reserve(str.length());
    utf8::utf8to16(str.begin(), str.end(), back_inserter(*this));
}

AgString::AgString(const AgString &str) : utf16string(str)
{
}

AgString::AgString(const utf16string &str) : utf16string(str)
{
}

AgString::AgString(char c)
{
    resize(1);
    this->operator [](0) = c;
}

AgString::AgString(AgChar c)
{
    resize(1);
    this->operator [](0) = c;
}

int AgString::toInt() const
{
    std::string str = toUtf8();
    return boost::lexical_cast<int>(str);
}

int64_t AgString::toInt64() const
{
    std::string str = toUtf8();
    return boost::lexical_cast<int64_t>(str);
}

float AgString::toFloat() const
{
    std::string str = toUtf8();
    return boost::lexical_cast<float>(str);
}

double AgString::toDouble() const
{
    std::string str = toUtf8();
    return std::stod(str);
}

bool AgString::startsWith(const AgString &s) const
{
    if (compare(0, s.length(), s) == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool AgString::endsWith(const AgString &s) const
{
    if (s.size() > this->size()) return false;
        return std::equal(this->begin() + this->size() - s.size(), this->end(), s.begin());
}

AgString AgString::fromInt(int number)
{
    std::string str = boost::lexical_cast<std::string>(number);
    return AgString(str);
}

AgString AgString::fromInt64(int64_t number)
{
    std::string str = boost::lexical_cast<std::string>(number);
    return AgString(str);
}

AgString AgString::fromFloat(float number)
{
    std::string str = boost::lexical_cast<std::string>(number);
    return AgString(str);
}


AgString AgString::fromBytes(std::vector<unsigned char> &bytes, CharSet charset)
{
    AgString str;
	str.reserve(bytes.size());
	
	if (charset == UTF8_CHARSET)
    {
        utf8::utf8to16(bytes.begin(), bytes.end(), back_inserter(str));
		
    }
    else if (charset == ASCII_CHARSET)
    {
        std::string utf8;
        std::vector<unsigned char>::iterator it = bytes.begin();
        while (it != bytes.end())
        {
            if (*it<128)
            {
                utf8.push_back(*it);
                it++;
            }
            else {
                utf8.push_back(0xc2+(*it>0xbf));
                utf8.push_back((*it&0x3f)+0x80);
                it++;
            }
        }
        utf8::utf8to16(utf8.begin(), utf8.end(), back_inserter(str));
    }
    else
    {
        assert(0);
    }
    return str;
}

std::string AgString::toUtf8() const
{
    std::string utf8line;
	utf8line.reserve(length());
    utf8::utf16to8(this->begin(), this->end(), back_inserter(utf8line));
    return utf8line;
}

// UTF-8 to ISO-8859-1/ISO-8859-15 mapper.
// Return 0..255 for valid ISO-8859-15 code points, 256 otherwise.
//
unsigned int AgString::to_latin9(const unsigned int code) const
{
    // Code points 0 to U+00FF are the same in both.
    if (code < 256U)
        return code;
    switch (code) {
    case 0x0152U: return 188U; // U+0152 = 0xBC: OE ligature
    case 0x0153U: return 189U; // U+0153 = 0xBD: oe ligature
    case 0x0160U: return 166U; // U+0160 = 0xA6: S with caron
    case 0x0161U: return 168U; // U+0161 = 0xA8: s with caron
    case 0x0178U: return 190U; // U+0178 = 0xBE: Y with diaresis
    case 0x017DU: return 180U; // U+017D = 0xB4: Z with caron
    case 0x017EU: return 184U; // U+017E = 0xB8: z with caron
    case 0x20ACU: return 164U; // U+20AC = 0xA4: Euro
    default:      return 256U;
    }
}

// Convert an UTF-8 string to ISO-8859-15.
// All invalid sequences are ignored.
// Note: output == input is allowed,
// but   input < output < input + length
// is not.
// Output has to have room for (length+1) chars, including the trailing NUL byte.
//
std::string AgString::utf8_to_latin9(const std::string &input) const
{
    std::string output;
    //unsigned char             *out = (unsigned char *)output;
    const unsigned char       *in  = (const unsigned char *)input.c_str();
    const unsigned char *const end = (const unsigned char *)input.c_str() + input.length();
    unsigned int               c;



    while (in < end)
        if (*in < 128)
            output.push_back(*(in++)); // Valid codepoint
        else
        if (*in < 192)
            in++;               // 10000000 .. 10111111 are invalid
        else
        if (*in < 224) {        // 110xxxxx 10xxxxxx
            if (in + 1 >= end)
                break;
            if ((in[1] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x1FU)) << 6U)
                             |  ((unsigned int)(in[1] & 0x3FU)) );
                if (c < 256)
                    output.push_back(c);
            }
            in += 2;

        } else
        if (*in < 240) {        // 1110xxxx 10xxxxxx 10xxxxxx
            if (in + 2 >= end)
                break;
            if ((in[1] & 192U) == 128U &&
                (in[2] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x0FU)) << 12U)
                             | (((unsigned int)(in[1] & 0x3FU)) << 6U)
                             |  ((unsigned int)(in[2] & 0x3FU)) );
                if (c < 256)
                    output.push_back(c);
            }
            in += 3;

        } else
        if (*in < 248) {        // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
            if (in + 3 >= end)
                break;
            if ((in[1] & 192U) == 128U &&
                (in[2] & 192U) == 128U &&
                (in[3] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x07U)) << 18U)
                             | (((unsigned int)(in[1] & 0x3FU)) << 12U)
                             | (((unsigned int)(in[2] & 0x3FU)) << 6U)
                             |  ((unsigned int)(in[3] & 0x3FU)) );
                if (c < 256)
                    output.push_back(c);
            }
            in += 4;

        } else
        if (*in < 252) {        // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
            if (in + 4 >= end)
                break;
            if ((in[1] & 192U) == 128U &&
                (in[2] & 192U) == 128U &&
                (in[3] & 192U) == 128U &&
                (in[4] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x03U)) << 24U)
                             | (((unsigned int)(in[1] & 0x3FU)) << 18U)
                             | (((unsigned int)(in[2] & 0x3FU)) << 12U)
                             | (((unsigned int)(in[3] & 0x3FU)) << 6U)
                             |  ((unsigned int)(in[4] & 0x3FU)) );
                if (c < 256)
                    output.push_back(c);
            }
            in += 5;

        } else
        if (*in < 254) {        // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
            if (in + 5 >= end)
                break;
            if ((in[1] & 192U) == 128U &&
                (in[2] & 192U) == 128U &&
                (in[3] & 192U) == 128U &&
                (in[4] & 192U) == 128U &&
                (in[5] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x01U)) << 30U)
                             | (((unsigned int)(in[1] & 0x3FU)) << 24U)
                             | (((unsigned int)(in[2] & 0x3FU)) << 18U)
                             | (((unsigned int)(in[3] & 0x3FU)) << 12U)
                             | (((unsigned int)(in[4] & 0x3FU)) << 6U)
                             |  ((unsigned int)(in[5] & 0x3FU)) );
                if (c < 256)
                    output.push_back(c);
            }
            in += 6;

        } else
            in++;               // 11111110 and 11111111 are invalid

    return output;
}

std::vector<unsigned char> AgString::getBytes(CharSet charset) const
{
    std::vector<unsigned char> vec;
    if (charset == UTF8_CHARSET)
    {
        std::string utf8str = toUtf8();
        std::copy(utf8str.begin(), utf8str.end(), std::back_inserter(vec));
    }
    else if (charset == ASCII_CHARSET)
    {
        for (size_t i = 0; i < this->size(); ++i)
        {
            vec.push_back((unsigned char)this->operator [](i));
        }
    }
    else
    {
        assert(0);
    }
    return vec;
}


void AgString::replaceStr(const AgString &toReplace, const AgString &replaceStr)
{
    boost::replace_all(*this, toReplace, replaceStr);
}

AgString  AgString::trim()
{
    std::string str = toUtf8();
    boost::algorithm::trim( str );
    return str;
}

AgObjectPtr AgString::toObjectPtr()
{
    return AgObjectPtr(new AgString(*this));
}

int AgString::IndexOf(char sym)
{
    std::string cur = toUtf8();
    std::string fndInd;

    fndInd += sym;

    int res = (int) cur.find(sym);

    if (res > -1)
        return res;
    else
        return -1;
}

int AgString::LastIndexOf(char sym)
{
    std::string cur = toUtf8();
    std::string fndInd;

    fndInd += sym;

    int res = (int) cur.find_last_of(sym);

    if (res > -1)
        return res;
    else
        return -1;
}

int AgString::Length()
{
    std::string cur = toUtf8();

    return (int) cur.length();
}

char AgString::CharAt(int pos)
{
    std::string cur = toUtf8();

    return cur.at(pos);
}

AgString AgString::Substring(int pos, int length)
{
    std::string cur = toUtf8();

    std::string res = cur.substr(pos, length);

    return AgString(res);
}

int AgString::compareTo(Comparable *obj)
{
    AgString *other = static_cast<AgString *>(obj);
    return compare(*other);
}

std::string AgString::getClass()
{
    return typeid(AgString).name();
}

int AgString::hashCode()
{
    boost::hash<std::string> string_hash;
    return string_hash(toUtf8());
}

bool AgString::equals(AgObject *obj)
{
    if (obj == NULL)
        return false;

    if (obj == this)
        return true;

    AgString *other = dynamic_cast<AgString *>(obj);
    if (!other)
    {
        return false;
    }

    return *this == *other;
}

bool AgString::operator ==(const char value)
{
    if (this->size() != 1) return false;
    char b = this->operator [](0);
    return b == value;
}


AgString AgString::toString()
{
    return *this;
}

Cloneable *AgString::clone()
{
    return new AgString(*this);
}

AgString operator+(const char *a, const AgString &b)
{
    return AgString(a) + b;
}

AgString operator+(const AgString &a, const char *b)
{
    return a + AgString(b);
}

AgString operator+(const AgString &a, const AgString &b)
{
    AgString res(a);
    res = res.operator +=(b);
    return res;
}

AgString operator+(const AgString &a, char b)
{
    return a + AgString(b);
}
