//------------------------------------------------------------------------------
// emString.h
//
// Copyright (C) 2004-2011,2014,2020 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef emString_h
#define emString_h

#ifndef emStd1_h
#include <emCore/emStd1.h>
#endif


//==============================================================================
//================================== emString ==================================
//==============================================================================

class emString {

public:

        // Class for a dynamically allocated null-terminated character string
        // with copy-on-write behavior. This string type is designed to minimize
        // memory consumption. That means, each change of the length requires
        // reallocation. If you plan to perform many length-changing operations
        // on a string, you may decide to do it with an emArray<char> for a
        // better performance.

        emString();
                // Construct an empty string.

        emString(const emString & s);
                // Construct a copied string.
                // Arguments:
                //   s - The string to be copied.

        emString(const char * p);
                // Construct a copied string.
                // Arguments:
                //   p - The null-terminated string to be copied.

        emString(const char * p, int len);
                // Construct a copied string.
                // Arguments:
                //   p   - The string to be copied.
                //   len - Number of bytes in p.

        emString(const char * p, int len, const char * p2, int len2);
                // Construct a string as a concatenated copy of two strings.
                // Arguments:
                //   p    - First source string.
                //   len  - Number of bytes in p.
                //   p2   - Second source string.
                //   len2 - Number of bytes in p2.

        emString(char c, int len=1);
                // Construct a string by filling it with a byte.
                // Arguments:
                //   c   - The byte to be used for filling.
                //   len - The length of the resulting string.

        ~emString();
                // Destructor.

        static emString Format(const char * format, ...) EM_FUNC_ATTR_PRINTF(1);
        static emString VFormat(const char * format, va_list args);
                // This function creates a formatted string.
                // Arguments:
                //   format - The format (like with printf).
                //   ...    - Arguments to the format (like with printf).
                // Returns: The formatted string.

        emString & operator = (const emString & s);
        emString & operator = (const char * p);
        emString & operator = (char c);
                // Copy a string or a byte to this string.

        int GetCount() const;
        int GetLen() const;
                // Get the number of bytes in this string, excluding the
                // terminating null. This really counts the bytes using strlen.

        operator const char * () const;
        const char * Get() const;
                // Get a pointer to the internal null-terminated string for
                // reading. At least because of the copy-on-write feature, the
                // pointer is valid only until calling any non-const method or
                // operator on this string, or giving this string as a non-const
                // argument to any call in the world. Hint: Even methods like
                // Add, Insert, Replace and GetSubString may make shallow
                // copies, like the copy operator and copy constructor do.

        char operator [] (int index) const;
        char Get(int index) const;
                // Get one byte from this string.
                // Arguments:
                //   index - The index of the desired byte. This must be within
                //           the range of 0 to GetLen().
                // Returns: The byte.

        char * GetWritable();
        char & GetWritable(int index);
                // Like Get() and Get(index), but for modifying the bytes.
                // There is no non-const version of the operator [], because
                // compilers would make use of it too often. The rules for the
                // validity of the pointer or reference are the same as with
                // Get(), but modification is allowed only until doing something
                // which could make a shallow copy of this string.

        char * SetLenGetWritable(int len);
                // Like GetWritable(), but even prepare for a new length of the
                // string.
                // Arguments:
                //   len - The length of the string you plan to produce.
                // Returns:
                //   A pointer to the internal null-terminated string buffer for
                //   writing. The buffer contains up to len bytes of the
                //   original string, then there is a null, then there may be
                //   garbage (if len>GetLen()+1), and in any case there is a
                //   null at the end (at index len). You may modify all
                //   bytes within the index range of 0 to len-1.

        void Add(const emString & s);
        void Add(const char * p);
        void Add(const char * p, int len);
        void Add(char c, int len=1);
        emString & operator += (const emString & s);
        emString & operator += (const char * p);
        emString & operator += (char c);
                // Add a copy of something to the end of this string. Source and
                // target memory may overlap.
                // Arguments:
                //   s   - A source string.
                //   p   - A source string, null-terminated if len not given.
                //   c   - A source byte.
                //   len - Length of string p, or how often to add byte c.

        emString operator + (const emString & s) const;
        emString operator + (const char * p) const;
        emString operator + (char c) const;
        friend emString operator + (const char * p, const emString & s);
        friend emString operator + (char c, const emString & s);
                // Add strings...

        void Insert(int index, const emString & s);
        void Insert(int index, const char * p);
        void Insert(int index, const char * p, int len);
        void Insert(int index, char c, int len=1);
                // Insert a copy of something somewhere in this string. Source
                // and target memory may overlap.
                // Arguments:
                //   index - Position of insertion. This should be within the
                //           range of 0 to GetLen(), otherwise it is clipped
                //           accordingly.
                //   s     - A source string to be copied for insertion.
                //   p     - A source string to be copied for insertion,
                //           null-terminated if len not given.
                //   c     - A source byte to be copied for insertion.
                //   len   - Length of string p, or how often to insert
                //           byte c.

        void Replace(int index, int exLen, const emString & s);
        void Replace(int index, int exLen, const char * p);
        void Replace(int index, int exLen, const char * p, int len);
        void Replace(int index, int exLen, char c, int len=1);
                // Replace something in this string by a copy of something.
                // Source and target memory may overlap.
                // Arguments:
                //   index - Position of replacement. This should be within the
                //           range of 0 to GetLen()-exLen, otherwise index and
                //           exLen are clipped accordingly.
                //   exLen - Number of bytes to be removed.
                //   s     - A source string to be copied for insertion.
                //   p     - A source string to be copied for insertion,
                //           null-terminated if len not given.
                //   c     - A source byte to be copied for insertion.
                //   len   - Length of string p, or how often to insert
                //           byte c.

        emString GetSubString(int index, int len) const;
                // Get a sub-string.
                // Arguments:
                //   index - Index of the first byte of the sub-string.
                //           This should be within the range of 0 to
                //           GetLen()-len, otherwise index and len are clipped
                //           accordingly.
                //   len   - Length of the sub-string.
                // Returns: The sub-string.

        emString Extract(int index, int len);
                // Like GetSubString, but remove the sub-string from this
                // string.

        void Remove(int index, int len=1);
                // Like Extract, but without returning the sub-string.

        void Clear();
                // Empty this string.

        bool IsEmpty() const;
                // Ask whether this string is empty.

        bool operator == (const emString & s) const;
        bool operator != (const emString & s) const;
        bool operator <= (const emString & s) const;
        bool operator >= (const emString & s) const;
        bool operator < (const emString & s) const;
        bool operator > (const emString & s) const;
        bool operator == (const char * p) const;
        bool operator != (const char * p) const;
        bool operator <= (const char * p) const;
        bool operator >= (const char * p) const;
        bool operator < (const char * p) const;
        bool operator > (const char * p) const;
        friend bool operator == (const char * p, const emString & s);
        friend bool operator != (const char * p, const emString & s);
        friend bool operator <= (const char * p, const emString & s);
        friend bool operator >= (const char * p, const emString & s);
        friend bool operator < (const char * p, const emString & s);
        friend bool operator > (const char * p, const emString & s);
                // Compare null-terminated strings using strcmp.

        unsigned int GetDataRefCount() const;
                // Get number of references to the data behind this string.

        void MakeNonShared();
                // This must be called before handing the string to another
                // thread.

private:

        struct SharedData {
                unsigned int RefCount;
                char Buf[sizeof(unsigned int)];
        };

        emString(SharedData * d);
        void FreeData();
        void MakeWritable();
        void PrivRep(int oldLen, int index, int exLen, const char * p, int len);
        void PrivRep(int oldLen, int index, int exLen, char c, int len);

        static SharedData EmptyData;
        SharedData * Data;
};

#ifndef EM_NO_DATA_EXPORT
inline emString::emString()
{
        Data=&EmptyData;
}
#endif

inline emString::emString(const emString & s)
{
        Data=s.Data;
        Data->RefCount++;
}

inline emString::~emString()
{
        if (!--Data->RefCount) FreeData();
}

inline emString & emString::operator = (const emString & s)
{
        s.Data->RefCount++;
        if (!--Data->RefCount) FreeData();
        Data=s.Data;
        return *this;
}

inline int emString::GetCount() const
{
        return strlen(Data->Buf);
}

inline int emString::GetLen() const
{
        return strlen(Data->Buf);
}

inline emString::operator const char * () const
{
        return Data->Buf;
}

inline const char * emString::Get() const
{
        return Data->Buf;
}

inline char emString::operator [] (int index) const
{
        return Data->Buf[index];
}

inline char emString::Get(int index) const
{
        return Data->Buf[index];
}

inline char * emString::GetWritable()
{
        if (Data->RefCount>1) MakeWritable();
        return Data->Buf;
}

inline char & emString::GetWritable(int index)
{
        if (Data->RefCount>1) MakeWritable();
        return Data->Buf[index];
}

inline emString & emString::operator += (const emString & s)
{
        Add(s);
        return *this;
}

inline emString & emString::operator += (const char * p)
{
        Add(p);
        return *this;
}

inline emString & emString::operator += (char c)
{
        Add(c);
        return *this;
}

#ifndef EM_NO_DATA_EXPORT
inline void emString::Clear()
{
        if (!--Data->RefCount) FreeData();
        Data=&EmptyData;
}
#endif

inline bool emString::IsEmpty() const
{
        return !*Data->Buf;
}

inline bool emString::operator == (const emString & s) const
{
        return strcmp(Data->Buf,s.Data->Buf)==0;
}

inline bool emString::operator != (const emString & s) const
{
        return strcmp(Data->Buf,s.Data->Buf)!=0;
}

inline bool emString::operator <= (const emString & s) const
{
        return strcmp(Data->Buf,s.Data->Buf)<=0;
}

inline bool emString::operator >= (const emString & s) const
{
        return strcmp(Data->Buf,s.Data->Buf)>=0;
}

inline bool emString::operator < (const emString & s) const
{
        return strcmp(Data->Buf,s.Data->Buf)<0;
}

inline bool emString::operator > (const emString & s) const
{
        return strcmp(Data->Buf,s.Data->Buf)>0;
}

inline bool emString::operator == (const char * p) const
{
        return strcmp(Data->Buf,p)==0;
}

inline bool emString::operator != (const char * p) const
{
        return strcmp(Data->Buf,p)!=0;
}

inline bool emString::operator <= (const char * p) const
{
        return strcmp(Data->Buf,p)<=0;
}

inline bool emString::operator >= (const char * p) const
{
        return strcmp(Data->Buf,p)>=0;
}

inline bool emString::operator < (const char * p) const
{
        return strcmp(Data->Buf,p)<0;
}

inline bool emString::operator > (const char * p) const
{
        return strcmp(Data->Buf,p)>0;
}

inline bool operator == (const char * p, const emString & s)
{
        return strcmp(p,s.Data->Buf)==0;
}

inline bool operator != (const char * p, const emString & s)
{
        return strcmp(p,s.Data->Buf)!=0;
}

inline bool operator <= (const char * p, const emString & s)
{
        return strcmp(p,s.Data->Buf)<=0;
}

inline bool operator >= (const char * p, const emString & s)
{
        return strcmp(p,s.Data->Buf)>=0;
}

inline bool operator < (const char * p, const emString & s)
{
        return strcmp(p,s.Data->Buf)<0;
}

inline bool operator > (const char * p, const emString & s)
{
        return strcmp(p,s.Data->Buf)>0;
}

inline void emString::MakeNonShared()
{
        MakeWritable();
}

inline emString::emString(SharedData * d)
{
        Data=d;
}


#endif