#pragma once

#include "util/AgObject.h"
#include "util/Comparable.h"
#include "util/Cloneable.h"
#include "util/Pointers.h"
#include <vector>
#include <stdio.h>
#include <string.h>

typedef uint16_t AgChar;

struct char16_traits
{
    typedef AgChar _E;
    typedef _E char_type;
    typedef int int_type;
    typedef std::streampos pos_type;
    typedef std::streamoff off_type;
    typedef std::mbstate_t state_type;
    static void assign(_E& _X, const _E& _Y)
    {_X = _Y; }
    static bool eq(const _E& _X, const _E& _Y)
    {return (_X == _Y); }
    static bool lt(const _E& _X, const _E& _Y)
    {return (_X < _Y); }
    static int compare(const _E *_U, const _E *_V, size_t _N)
    {return (memcmp(_U, _V, _N * 2)); }
    static size_t length(const _E *_U)
    {
        size_t count = 0;
        while(_U[count] != 0)
        {
            count++;
        }
        return count;
    }
    static _E * copy(_E *_U, const _E *_V, size_t _N)
    {return ((_E *)memcpy(_U, _V, _N * 2)); }
    static const _E * find(const _E *_U, size_t _N, const _E& _C)
    {
        for(size_t i = 0; i < _N; ++i) {
            if(_U[i] == _C) {
                return &_U[i];
            }
        }
        return 0;
    }
    static _E * move(_E *_U, const _E *_V, size_t _N)
    {return ((_E *)memmove(_U, _V, _N * 2)); }
    static _E * assign(_E *_U, size_t _N, const _E& _C)
    {
        for(size_t i = 0; i < _N; ++i) {
            assign(_U[i], _C);
        }
        return _U;
    }
    static _E to_char_type(const int_type& _C)
    {return ((_E)_C); }
    static int_type to_int_type(const _E& _C)
    {return ((int_type)(_C)); }
    static bool eq_int_type(const int_type& _X, const int_type& _Y)
    {return (_X == _Y); }
    static int_type eof()
    {return (EOF); }
    static int_type not_eof(const int_type& _C)
    {return (_C != eof() ? _C : !eof()); }
};

typedef std::basic_string<AgChar, char16_traits> utf16string;

enum CharSet {UTF8_CHARSET, ASCII_CHARSET};

// UTF-16 String
class AgString : public utf16string, public AgObject, public Comparable, public Cloneable
{
public:

    AgString();

    AgString(const char * str);
	AgString(const AgChar * str);
    AgString(const std::string &str);
    AgString(const AgString & str);
    AgString(const utf16string & str);
    AgString(char c);
    AgString(AgChar c);

    int toInt() const;
    int64_t toInt64() const;
    float toFloat() const;
    double toDouble() const;

    bool startsWith(const AgString &s) const;
    bool endsWith(const AgString &s) const;

    static AgString fromInt(int number);
    static AgString fromInt64(int64_t number);
    static AgString fromFloat(float number);

    static AgString fromBytes(std::vector<unsigned char> &bytes, CharSet charset);

    std::string toUtf8() const;
    std::vector<unsigned char> getBytes(CharSet charset) const;

    void replaceStr(const AgString &toReplace, const AgString &replaceStr);

    AgString trim();

    AgObjectPtr toObjectPtr();

    int IndexOf(char sym);

    int LastIndexOf(char sym);

    int Length();

    char CharAt(int pos);

    AgString Substring(int pos, int length);

    int compareTo(Comparable *obj);
    virtual std::string getClass();
    virtual int hashCode();
    virtual bool equals(AgObject* obj);
    virtual AgString toString();
    virtual Cloneable *clone();

    bool operator ==(const char value);

private:
    std::string utf8_to_latin9(const std::string &input) const;
    inline unsigned int to_latin9(const unsigned int code) const;


};

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

