/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/

#pragma once

#include "glmCoreAPI.h"
#include "glmAssert.h"
#include "glmMemory.h"
#include "glmPODArray.h"
#include <ostream>
#include <istream>
#include <string>
#ifdef GLM_IS_COMPILER_GCC
#include <cstring>
#endif

namespace glm
{
class GLM_CORE_API GlmString
{

    typedef char* iterator;
    typedef const char* const_iterator;

  public:
    GLM_CLASS_STAT_ID( glm::MemoryStatIds::GolaemShared)

    static const size_t npos = static_cast<size_t>(-1);

    GlmString();
    GlmString(const char* inputString);
    GlmString(const GlmString& str, size_t pos, size_t len = npos);
    GlmString(const GlmString& other);
    GlmString(const std::string& stdString); // allow implicit conversions to std::string, this could be misleading.

    GlmString& operator=(const GlmString& str);
    GlmString& operator=(const char* s);
    GlmString& operator=(char c);
    GlmString& operator=(const std::string& stdString);

    inline ~GlmString()
    {
    } // call PODArray for deletion

    inline iterator begin()
    {
        return _data.begin();
    }
    inline const_iterator begin() const
    {
        return _data.begin();
    }

    inline iterator end()
    {
        return _data.end() - 1;
    }
    inline const_iterator end() const
    {
        return _data.end() - 1;
    }

    // TODO
    //rbegin
    //rend
    //cbegin
    //cend
    //crbegin
    //crend

    inline size_t size() const
    {
        return _data.size() - 1;
    }
    inline size_t length() const
    {
        return _data.size() - 1;
    }
    //inline size_t max_size() const { return npos; }
    inline size_t capacity() const
    {
        return _data.capacity() - 1;
    }
    inline void resize(size_t n)
    {
        _data.resize(n + 1);
        _data.back() = 0;
    }
    inline void resize(size_t n, char c)
    {
        _data.resize(n + 1, c);
        _data.back() = 0;
    }
    inline void reserve(size_t n = 0)
    {
        _data.reserve(n + 1);
    }
    inline void clear()
    {
        _data.clear();
        _data.push_back(0);
    }
    inline bool empty() const
    {
        return _data[0] == 0;
    }

    inline char& operator[](size_t pos)
    {
        GLM_DEBUG_ASSERT(pos < _data.size() - 1);
        return _data[pos];
    }
    inline const char& operator[](size_t pos) const
    {
        GLM_DEBUG_ASSERT(pos < _data.size() - 1);
        return _data[pos];
    }

    inline char& at(size_t pos)
    {
        GLM_DEBUG_ASSERT(pos < _data.size() - 1);
        return _data.at(pos);
    }
    inline const char& at(size_t pos) const
    {
        GLM_DEBUG_ASSERT(pos < _data.size() - 1);
        return _data.at(pos);
    }

    GlmString& operator+=(const GlmString& str);
    GlmString& operator+=(const char* s);
    inline GlmString& operator+=(char c)
    {
        _data.back() = c;
        _data.push_back(0);
        return *this;
    }
    GlmString& operator+=(int value);
	GlmString& operator+=(int64_t value);

    inline GlmString& append(const GlmString& str)
    {
        return operator+=(str);
    }
    //GlmString& append (const GlmString& str, size_t subpos, size_t sublen);
    inline GlmString& append(const char* s)
    {
        return operator+=(s);
    }
    GlmString& append(const char* s, size_t n);
    //GlmString& append (size_t n, char c);
    //template <class InputIterator> string& append (InputIterator first, InputIterator last);

    inline void push_back(char c)
    {
        _data.back() = c;
        _data.push_back(0);
    }

    //GlmString& assign (const GlmString& str);
    //GlmString& assign (const GlmString& str, size_t subpos, size_t sublen);
    GlmString& assign(const char* s);
    //GlmString& assign (const char* s, size_t n);
    //GlmString& assign (size_t n, char c);
    template <class InputIterator>
    GlmString& assign(InputIterator first, InputIterator last);

    //GlmString& insert (size_t pos, const GlmString& str);
    //GlmString& insert (size_t pos, const GlmString& str, size_t subpos, size_t sublen);
    GlmString& insert(size_t pos, const char* s);
    GlmString& insert(size_t pos, const char* s, size_t n);
    //GlmString& insert (size_t pos, size_t n, char c);
    //void insert (iterator p, size_t n, char c);
    //iterator insert (iterator p, char c);
    //template <class InputIterator> void insert (iterator p, InputIterator first, InputIterator last);

    GlmString& erase(size_t pos = 0, size_t len = npos);
    //iterator erase (iterator p);
    //iterator erase (iterator first, iterator last);

    GlmString& replace(size_t pos, size_t len, const GlmString& str);
    //GlmString& replace (iterator i1, iterator i2, const GlmString& str);
    GlmString& replace(size_t pos, size_t len, const GlmString& str, size_t subpos, size_t sublen);
    //GlmString& replace (size_t pos,  size_t len,  const char* s);
    //GlmString& replace (iterator i1, iterator i2, const char* s);
    //GlmString& replace (size_t pos,  size_t len,  const char* s, size_t n);
    //GlmString& replace (iterator i1, iterator i2, const char* s, size_t n);
    //GlmString& replace (size_t pos,  size_t len,  size_t n, char c);
    //GlmString& replace (iterator i1, iterator i2, size_t n, char c);
    //template <class InputIterator> GlmString& replace (iterator i1, iterator i2, InputIterator first, InputIterator last);

	void replaceAll(char charToReplace,char charReplacement);

    //void swap (GlmString& str);

    inline const char* c_str() const
    {
        return _data.begin();
    }
    inline const char* data() const
    {
        return _data.begin();
    }

    //allocator_type get_allocator() const;

    //size_t copy (char* s, size_t len, size_t pos = 0) const;

    size_t find(const GlmString& str, size_t pos = 0) const;
    //size_t find (const char* s, size_t pos = 0) const;
    //size_t find (const char* s, size_t pos, size_t n) const;
    size_t find(char c, size_t pos = 0) const
    {
        return find_first_of(c, pos);
    }

    size_t rfind(const GlmString& str, size_t pos = npos) const;
    //size_t rfind (const char* s, size_t pos = npos) const;
    //size_t rfind (const char* s, size_t pos, size_t n) const;
    inline size_t rfind(char c, size_t pos = npos) const
    {
        return find_last_of(c, pos);
    }

    //size_t find_first_of (const GlmString& str, size_t pos = 0) const;
    size_t find_first_of(const char* s, size_t pos = 0) const;
    //size_t find_first_of (const char* s, size_t pos, size_t n) const;
    size_t find_first_of(char c, size_t pos = 0) const;

    //size_t find_last_of (const GlmString& str, size_t pos = npos) const;
    size_t find_last_of(const char* s, size_t pos = npos) const;
    //size_t find_last_of (const char* s, size_t pos, size_t n) const;
    size_t find_last_of(char c, size_t pos = npos) const;

    //find_first_not_of
    size_t find_first_not_of(const char* s, size_t pos = 0) const;
    size_t find_first_not_of(char c, size_t pos = 0) const;
    //find_last_not_of
    size_t find_last_not_of(const char* s, size_t pos = npos) const;
    size_t find_last_not_of(char c, size_t pos = npos) const;

    GlmString substr(size_t pos = 0, size_t len = npos) const;

    GlmString getStringBeforeFirst(char separator) const;
    GlmString getStringAfterlast(char separator) const;

    int compare(const GlmString& str) const;
    //int compare (size_t pos, size_t len, const GlmString& str) const;
    //int compare (size_t pos, size_t len, const GlmString& str, size_t subpos, size_t sublen) const;
    //int compare (const char* s) const;
    //int compare (size_t pos, size_t len, const char* s) const;
    //int compare (size_t pos, size_t len, const char* s, size_t n) const;

    bool operator==(const GlmString& otherString) const;
    bool operator==(const char* otherString) const;
    inline bool operator!=(const GlmString& otherString) const
    {
        return !operator==(otherString);
    }
    inline bool operator!=(const char* otherString) const
    {
        return !operator==(otherString);
    }

    inline const PODArray<char>& getData() const
    {
        return _data;
    }

    void toUpperSelf();
    void toLowerSelf();

    void ltrim(const GlmString& chars = "\t\n\r ");

    void rtrim(const GlmString& chars = "\t\n\r ");

    void trim(const GlmString& chars = "\t\n\r ");

  private:
    PODArray<char> _data;
};

extern GLM_CORE_API GlmString operator+(const GlmString& lhs, const GlmString& rhs);
extern GLM_CORE_API GlmString operator+(const GlmString& lhs, const char* rhs);
extern GLM_CORE_API GlmString operator+(const char* lhs, const GlmString& rhs);

extern GLM_CORE_API std::ostream& operator<<(std::ostream& out, const GlmString& glmString);
extern GLM_CORE_API std::istream& operator>>(std::istream& out, GlmString& glmString);

extern GLM_CORE_API bool operator<(const GlmString& lhs, const GlmString& rhs);
inline bool operator>(const GlmString& lhs, const GlmString& rhs)
{
    return rhs < lhs;
}
inline bool operator<=(const GlmString& lhs, const GlmString& rhs)
{
    return !(lhs > rhs);
}
inline bool operator>=(const GlmString& lhs, const GlmString& rhs)
{
    return !(lhs < rhs);
}
inline bool operator==(const char* lhs, const GlmString& rhs)
{
    return rhs.operator==(lhs);
}
inline bool operator!=(const char* lhs, const GlmString& rhs)
{
    return !operator==(lhs, rhs);
}

//-----------------------------------------------------------------------------
template <class InputIterator>
GlmString& GlmString::assign(InputIterator first, InputIterator last)
{
    _data.clear(); // clear all, including 0 end tag
    for (; first != last; first++)
    {
        _data.push_back(*first);
    }
    _data.push_back(0); // restore end tag
    return *this;
}

//-----------------------------------------------------------------------------

class GLM_CORE_API GlmStaticString
{
  public:
    GlmStaticString()
        : _ptr(NULL)
    {
    }
    GlmStaticString(const char* ptr)
        : _ptr(ptr)
    {
    }
    GlmStaticString(const GlmStaticString& other)
        : _ptr(other._ptr)
    {
    }

    GlmStaticString& operator=(const GlmStaticString& other)
    {
        _ptr = other._ptr;
        return *this;
    }

    bool operator<(const GlmStaticString& other)
    {
        return (strcmp(_ptr, other._ptr) < 0);
    }

    const char* c_str() const
    {
        return _ptr;
    }

  protected:
    const char* _ptr;
};

inline bool operator<(const glm::GlmStaticString& a, const glm::GlmStaticString& b)
{
    return (strcmp(a.c_str(), b.c_str()) < 0);
}
} // namespace glm

//operator+
//relational operators
//operator== x 3
//operator!= x 3
//operator< x3
//operator<= x3
//operator> x3
//operator>= x3
//swap
//operator>>
//operator<<
//getline
