///////////////////////////////////////////////////////////////////////////////
/// @file deString.hpp
///
/// @brief object-oriented string manipulation class
///
/// @author Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Mar 2002
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#ifndef DESTRING_HPP
#define DESTRING_HPP

#include "deGlobalTypes.hpp"
#include "deArray.hpp"

#if defined(DEUTIL_DLL_EXPORTS) || defined(DESTINY3D_EXPORT_ALL)
#	define DESTRING_API DEDLL_EXPORT
#elif defined(DESTINY3D_STATIC_LINK)
#	define DESTRING_API
#else
#	define DESTRING_API DEDLL_IMPORT
#endif

#ifdef USING_DESTINY3D
#ifdef _DEBUG
#	ifdef DESTINY3D_STATIC_LINK
#		pragma comment(lib, "deUtil_sd")
#	else
#		pragma comment(lib, "deUtild")
#	endif //DESTINY3D_STATIC_LINK
#else
#	ifdef DESTINY3D_STATIC_LINK
#		pragma comment(lib, "deUtil_s")
#	else
#		pragma comment(lib, "deUtil")
#	endif //DESTINY3D_STATIC_LINK
#endif //_DEBUG
#endif //USING_DESTINY3D

/// string utility class.
/// deString2 offers better performance for string-intensive operations involving copies.
class DESTRING_API deString
{
public:
	deString();
	deString(const char * rhs);
	deString(const deString &rhs );
	~deString();

	long inline Length() const { return m_Length; }
	long inline size() const { return m_Length; }
	
	deString operator+(const deString & rhs) const;
	deString operator+(const char * rhs) const;

	const deString& operator+=(const deString & rhs);
	const deString& operator+=(const char * rhs);
	const deString& operator+=(const char rhs);

	const deString & operator=(const deString & rhs);
	const deString & operator=(const char * rhs);

	void SetCaseSensitivity(deBoolean Sensitive);
	deBoolean operator==(const deString & rhs) const;
	deBoolean operator==(const char * rhs) const;
	deBoolean operator!=(const deString & rhs) const;
	deBoolean operator!=(const char * rhs) const;

	char operator[](long index) const;
	char& operator[](long index);

	void operator << (char c);				///< concatenate a single character (signed)
	void operator << (long l);				///< concatenate an integer (signed)
	void operator << (unsigned long ul);	///< concatenate an integer (unsigned)
	void operator << (int i) {operator<<((long)i);}
	void operator << (unsigned int ui) {operator<<((unsigned long)ui);}
	void operator << (s64 i);
	void operator << (u64 ui);
	void operator << (float f);				///< concatenate a fp number (float)
	void operator << (double d);			///< concatenate a fp number (double)
	void operator << (const char * str);	///< concatenate a string (char*)
	void operator << (const deString& str);	///< concatenate a string (deString)

	int Format(const char * format, ...);

	int Find(char ToFind, deBoolean FindLast);
	void Replace(char Find, char Replace);
	void Split(char Splitter, deTArray <deString> &ResultStrings);
	deBoolean Strip(const deString &Stripped, deString &Result);
	deString SubString(int start, int stop); // half-closed range: [start,stop)

	const char * const_str() const { return m_String; }
	char * c_str() { return m_String; }
	char * Reserve(int Length);
	deBoolean Trim(int Length = -1);

#ifndef _CRTDBG_MAP_ALLOC
	void *operator new (size_t Size);
	void operator delete (void *p);
	void *operator new (size_t Size, void* buffer);
	void operator delete (void *p, void* buffer);
	void *operator new[] (size_t Size);
	void *operator new[] (size_t Size, int, char *, int);
	void operator delete[] (void *p);
	void operator delete[] (void *p, int, char *, int);
#endif

private:
	long m_Length;
	deBoolean m_CaseSensitive;
#ifndef DESTRING2
	char * m_String;
	long m_Alloc;
	char * Copy() const;
	void Resize(long newsize);
#else
	deTArray<char> m_StringArr;
#endif
};

/// A string class that uses a reference-counted inner class to manage the actual data.
/// Making unmodified copies of this class will have negligible impact on memory usage,
/// since they will all point at the same data. A copy-on-write mechanism allocates
/// space only when needed. Methods marked with [write] will cause a copy to be made
/// if more than one reference is held on the data.
class DESTRING_API deString2
{
public:
	enum string_flag
	{
		search_forwards = 0,
		search_backwards,
		flag_32bit = 0x7fffffff
	};
#ifdef _UNICODE
	typedef wchar_t deString_char;
#else
	typedef char deString_char;
#endif
private:
	class StringData
	{
	protected:
		~StringData()
		{
			delete mString;
		}
	public:
		long mRefs;
		deString_char * mString;
		long mLength;
		long mAlloc;
	public:
		inline StringData() : mRefs(1), mString(0), mLength(0), mAlloc(0) {}
		StringData(const StringData & ref);
		StringData(const deString_char* str);
		StringData(long max_len);
		inline long Claim()
		{
			++mRefs;
			return mRefs;
		}
		inline long Release()
		{
			--mRefs;
			if (mRefs == 0){
				delete this;
				return 0;
			}
			return mRefs;
		}
		StringData* getModString();
	};
	// member data
	StringData* mData;
	deBoolean mCaseSensitive;
	// private methods
	void resizeData(long newlen);
	void OwnBuffer();
	deString_char * c_str(); // temporarily private, until it's needed
	deString2(StringData* ref);
public:
	deString2();
	deString2(const deString_char * ref);
	deString2(const deString2 &ref);
	~deString2();

	void reserve(long max_size);
	void resize(long size);
	/// get the length of the string
	inline long size() const { return mData->mLength; }
	inline void SetCaseSensitivity(deBoolean Sensitive) { mCaseSensitive = Sensitive; }
	/// get a const C-string without creating a copy of the data
	inline const deString_char * const_str() const { return mData->mString; }

	/// get a character at an offset from the start of the string
	deString_char get_at(long index) const;
	/// [write] set a character at an offset from the start of the string
	void set_at(long index, deString_char ch);

	/// create a new string containing this string and another string appended
	deString2 operator+(const deString2 & rhs) const;
	
	/// [write] append another string onto this one
	void operator+=(const deString2 & rhs);
	/// [write] append another string onto this one
	void operator+=(const deString_char * str);
	
	/// [write] assign another string to this one
	void operator=(const deString2 & rhs);
	
	/// test equality, using current case-sensitivity setting.
	bool operator==(const deString2 & rhs) const;
	/// test equality, using current case-sensitivity setting.
	bool operator!=(const deString2 & rhs) const;
	/// test equality, using current case-sensitivity setting.
	bool operator==(const deString_char * rhs) const;
	/// test equality, using current case-sensitivity setting.
	bool operator!=(const deString_char * rhs) const;
	/// test equality, using current case-sensitivity setting.
	bool operator<(const deString2 & rhs) const;
	/// test equality, using current case-sensitivity setting.
	bool operator<(const deString_char * rhs) const;

	// special string operations
	/// [write] replace all instances of 'find' with 'replace'
	void Replace(deString_char find, deString_char replace);
	/// linearly searches for the first instance of 'find', starting at offset 'start_at' 
	/// and searching in direction according to 'search_order'
	long Find(deString_char find, long start_at = 0, string_flag search_order = search_forwards) const;
	/// [write] cut the string down to at most length 'newsize', discarding any characters past that size.
	void Trim(long newsize);
	deString2 DropChars(const deString2 &Chars) const;
	/// retrieve a sub-string over the half-open intervace [start,stop)
	deString2 SubString(int start, int stop) const;
	/// [write] convert this string to lowercase
	void ToLower();
	/// [write] convert this string to uppercase
	void ToUpper();

	u32 Hash() const;
};

#endif
