//------------------------------------------------------------------------------
// emString.h
//
// Copyright (C) 2004-2011,2014 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);
		// 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
