/////////////////////////////////////////////////////////////
// CINEMA 4D SDK                                           //
/////////////////////////////////////////////////////////////
// (c) MAXON Computer GmbH, all rights reserved            //
/////////////////////////////////////////////////////////////

#ifndef __C4DSTRING_H
#define __C4DSTRING_H

#ifdef __API_INTERN__
	#include "ge_string.h"
#else

#ifdef __MAC
	#if defined(__cplusplus)
		#include <new>
		using namespace std;
	#endif
#elif defined __LINUX
	#include <new>
#elif	defined	__PC
	#include <new>
#endif

#include "operatingsystem.h"

#define USE_EX_STRING 1 ///OK

//=================================================================================================
class String
{
#if USE_EX_STRING
	#define StCall(fnc, ...) C4DOS.St->fnc(this,##__VA_ARGS__)
#else
	//#define StCall(fnc) (this->*C4DOS.St->fnc) //default
#endif

	private:
#ifndef USE_EX_STRING
		C4D_RESERVE_PRIVATE_TYPE(LONG,dummy1);
		C4D_RESERVE_PRIVATE_TYPE(LONG,dummy2);
		C4D_RESERVE_PRIVATE_TYPE(void*,dummy3);
		C4D_RESERVE_PRIVATE_TYPE(void*,dummy4);
#else
		LONG	m_size; 	// actual allocated size / capacity() //min 16
		LONG	m_len; 		// number of characters / size()
		UWORD*	m_txt; 		// data
		void*	m_future_enhancements;
#endif

public:

		#ifdef	__C4D_NO_NEW_DELETE__																

		void* operator new(size_t, const std::nothrow_t&, int line, const CHAR *file) throw()
		{
			return C4DOS.St->Alloc();
		}

		void operator delete(void* del,int line, const CHAR *file)
		{
			C4DOS.St->Free((String*)del);
		}

		void operator delete(void* del)
		{
			C4DOS.St->Free((String*)del);
		}

		void* operator new(size_t,void *place) { return place; }
		void  operator delete(void*, void*) {}
		#endif

		void CopyTo(String *str) const
		{
			C4DOS.St->CopyTo(this,str);
		}

		const String& operator = (const String &cs)
		{
			C4DOS.St->CopyTo(&cs,this);
			return *this;
		}

		String(void) 
			: m_size(0),m_len(0),m_txt(0),m_future_enhancements(0)
		{
			C4DOS.St->Init(this);
		}

		String(const String &cs)
		{
			C4DOS.St->Init(this);
			C4DOS.St->CopyTo(&cs,this);
		}

		String(const UWORD* s)
		{
			C4DOS.St->Init(this);
			StCall(SetUcBlock, s, -1);
		}

		String(const CHAR *cstr,STRINGENCODING type=STRINGENCODING_XBIT)
			 : m_size(0),m_len(0),m_txt(0),m_future_enhancements(0)
		{
			C4DOS.St->Init(this);
			C4DOS.St->InitCString(this,cstr,-1,type);
		}

		String(LONG count,UWORD fillch)
		{
			C4DOS.St->Init(this);
			C4DOS.St->InitArray(this,count,fillch);
		}

		~String(void)
		{
			C4DOS.St->Flush(this);
		}
#if USE_EX_STRING
		LONG GetLength() const { return m_len; }
#else
		LONG GetLength() const
		{
			return StCall(GetLength);
		}
#endif
		Bool Content() const { return GetLength()!=0; }

		friend const String operator + (const String &Str1,const String &Str2)
		{
			String s(Str1);
			s += Str2;
			return s;
		}

		const String& operator += (const String &Str)
		{
			Insert(GetLength(),Str);
			return *this;
		}

		Bool operator < (const String &Str) const
		{
			return (Compare(Str) < 0);
		}

		Bool operator > (const String &Str) const
		{
			return (Compare(Str) > 0);
		}

		Bool operator <= (const String &Str) const
		{
			return (Compare(Str) <= 0);
		}

		Bool operator >= (const String &Str) const
		{
			return (Compare(Str) >= 0);
		}

		Bool operator == (const String &Str) const
		{
			return (Compare(Str) == 0);
		}

		Bool operator != (const String &Str) const
		{
			return (Compare(Str) != 0);
		}

		// 0 == , <0 < , >0 >
		LONG Compare(const String &cs) const
		{
			return StCall(Compare, cs);
		}

		LONG LexCompare(const String &cs) const
		{
			return StCall(LexCompare, cs);
		}

		LONG RelCompare(const String &cs) const
		{
			return StCall(RelCompare, cs);
		}

		LONG ComparePart(const String &Str, LONG cnt, LONG pos) const
		{
			return StCall(ComparePart, Str,cnt,pos);
		}

		LONG LexComparePart(const String &Str, LONG cnt, LONG pos) const
		{
			return StCall(LexComparePart, Str,cnt,pos);
		}

		Bool FindFirst(const String &cs,LONG *pos,LONG start=0) const
		{
			return StCall(FindFirst, cs,pos,start);
		}

		Bool FindLast(const String &cs,LONG *pos,LONG start=-1) const
		{
			return StCall(FindLast, cs,pos,start);
		}

		Bool FindFirst(UWORD ch,LONG *pos,LONG start=0) const
		{
			return StCall(FindFirstCh, ch,pos,start);
		}

		Bool FindLast(UWORD ch,LONG *pos,LONG start=-1) const
		{
			return StCall(FindLastCh, ch,pos,start);
		}

		// searches a string case insensitive, "findupper" has to be uppercase!!!
		Bool FindFirstUpper(const String &findupper, LONG *pos, LONG start) const
		{
			return StCall(FindFirstUpper, findupper,pos,start);
		}

		Bool FindLastUpper(const String &findupper, LONG *pos, LONG start) const
		{
			return StCall(FindLastUpper, findupper,pos,start);
		}

		void Delete(LONG pos,LONG count)
		{
			StCall(Delete, pos,count);
		}

		void Insert(LONG pos,UWORD ch)
		{
			String tmp = String(1,ch);
			StCall(Insert, pos,tmp,-1,-1);
		}

		void Insert(LONG pos,const String &cs,LONG start=-1, LONG end=-1)
		{
			StCall(Insert, pos,cs,start,end);
		}

		String SubStr(LONG start,LONG count) const
		{
			return StCall(SubStr, start,count);
		}

		String Left(LONG count) const
		{
			return SubStr(0, count);
		}

		String Right(LONG count) const
		{
			return SubStr(GetLength() - count, count);
		}

		Real ToReal(LONG *error=NULL, LONG unit=0, LONG angletype=0, LONG base=10) const
		{
			return StCall(ToReal, error,unit,angletype,base);
		}

		LONG ToLong(LONG *error=NULL) const
		{
			return StCall(ToLong, error);
		}

		friend String RealToString(Real v, LONG vk, LONG nk, Bool e, UWORD xchar);
		friend String LongToString(LONG l);
		friend String LLongToString(LLONG l);
			
		String ToUpper() const
		{
			return StCall(ToUpper);
		}

		String ToLower() const
		{
			return StCall(ToLower);
		}
		
		LONG GetCStringLen(STRINGENCODING type=STRINGENCODING_XBIT) const
		{
			return StCall(GetCStringLen, type);
		}

		LONG GetCString(CHAR *scstr, LONG max, STRINGENCODING type=STRINGENCODING_XBIT) const
		{
			return StCall(GetCString, scstr,max,type);
		}

		CHAR *GetCStringCopy(STRINGENCODING Type=STRINGENCODING_XBIT) const;
		
		void SetCString(const CHAR *cstr, LONG count=-1, STRINGENCODING type=STRINGENCODING_XBIT)
		{
			C4DOS.St->InitCString(this, cstr, count, type);
		}

		LONG SetCStringR(const CHAR *cstr, LONG count=-1, STRINGENCODING type=STRINGENCODING_XBIT)
		{
			C4DOS.St->InitCString(this, cstr, count, type);
			return GetLength();
		}

		void GetUcBlock(UWORD *Ubc, LONG Max) const
		{
			StCall(GetUcBlock, Ubc,Max);
		}

		void GetUcBlockNull(UWORD *Ubc, LONG Max) const
		{
			StCall(GetUcBlockNull, Ubc,Max);
		}

		void SetUcBlock(const UWORD *Ubc, LONG Count)
		{
			StCall(SetUcBlock, Ubc,Count);
		}

		class PChar
		{
			friend class String;
			
			private:
				String *Str;
				LONG Pos;

				PChar(String *str,LONG pos)
				{
					Str = str;
					Pos = pos;
				}
				
			public:
				PChar& operator = (const PChar& rhs)
				{
					C4DOS.St->SetChr(Str,Pos,C4DOS.St->GetChr(rhs.Str,rhs.Pos));
					return *this;
				}

				PChar& operator = (UWORD c)
				{
					C4DOS.St->SetChr(Str,Pos,c);
					return *this;
				}

				operator UWORD(void) const
				{
					return C4DOS.St->GetChr(Str,Pos);
				}
		};
		
		const PChar operator[] (LONG Pos) const
		{
			return PChar((String*)this,Pos);
		}

		PChar operator[] (LONG Pos)
		{
			return PChar((String*)this,Pos);
		}

#ifdef USE_EX_STRING //------------------------------------------------------

		String(String&& other);		// Move constructor
		String& operator=(String&& other);	// Move assignment operator.
		void MoveTo(String *str);

		//const UWORD operator() (LONG Pos) const	{	return C4DOS.St->GetChr(this,Pos);	}
		const UWORD operator() (LONG Pos) const	{  if(Pos >=0 && Pos<m_len) return m_txt[Pos]; else return 0;	}

		UWORD GetChr(LONG Pos) { return C4DOS.St->GetChr(this,Pos); }
		void  SetChr(LONG Pos, UWORD c) { C4DOS.St->SetChr(this,Pos,c); }


		void Append(const String &str){	Insert(GetLength(),str); }

		// Move + operator.
		friend String operator+ (String &&str1, String &&str2) {
			if (str2.size() <= str1.capacity() - str1.size()
				|| str2.capacity() - str2.size() <  str1.size()){
					str1.Insert(str1.GetLength(),str2); //Append();
					return (String&&)(str1);
			}else{
				str2.Insert(0,str1);
				return (String&&)(str2);
			}
		}
		friend String operator+ (const String &str1, String &&str2) {
			str2.Insert(0,str1);
			return (String&&)(str2); //std::forward<String>(str2);
		}
		friend String operator+ (String &&str1, const String &str2) {
			str1.Insert(str1.GetLength(),str2); //Append();
			return (String&&)(str1); //std::forward<String>(str1);
		}


		const String& operator += ( UWORD ch )	{ //UWORD support
			Insert(GetLength(),ch);
			return *this;
		}
		const String& operator += ( CHAR ch )	{ //CHAR support
			Insert(GetLength(),(UWORD)ch);
			return *this;
		}

		///std mimicry 
		LONG	capacity() const { return m_size; } 		// actual allocated size
		LONG	GetSize() const { return m_size; } 		// actual allocated size //remove this 
		LONG	GetCapacity() const { return m_size; } // actual allocated size

		LONG	size() const { return m_len; }	 	// number of characters
		LONG	GetLen() const { return m_len; }	 	// number of characters

		bool empty() const { return m_len==0; } //checks whether the container is empty 

		void push_back( UWORD ch ) { Insert(GetLength(),ch); }
		void push_back( const String& cs ) { Insert(GetLength(),cs); }

		LONG insert( ULONG pos, UWORD ch ) { Insert(pos,ch); return pos; }
		LONG insert( ULONG pos, const String& cs ) { Insert(pos,cs); return pos; }

		void clear() {	C4DOS.St->Flush(this);	} // Free the existing resource.
		LONG erase( ULONG pos ) { //Removes the element at pos. 
			ULONG len(GetLength()); 
			if(pos<len) Delete(pos,1);
			return pos; 
		} 
		LONG erase( ULONG first,ULONG last ) { //Removes the elements in the range [first; last). 
			ULONG len(GetLength()); 
			if(last<len && first<last) Delete(first,last-first); 
			return first; 
		}

		//const UWORD* GetRawData() const { return m_txt; } 		// data
		//use like this  // const LONG  len = str.GetDataPtr(&raw);
		inline LONG GetDataPtr(const UWORD **out_ptr) const {  *out_ptr = m_txt; return m_len;	}


		// initialization of the string
		void Init(LONG Count = 0, UWORD FillCh = 0)	{ C4DOS.St->InitArray(this,Count,FillCh); }
		void Construct(void)	{  C4DOS.St->Init(this); 	}

		//void ShrinkToLen();// optimizes the memory usage after delete, init etc...

		void Free() {	C4DOS.St->Flush(this);	} // Free the existing resource.
#else 
		void Free() {	C4DOS.St->Flush(this);	} // Free the existing resource.
#endif	//---------------------------------------------------------------------
};

#ifdef USE_EX_STRING // ------------------------------------------------------

// Move constructor
inline String::String(String&& other)	{
	///C4DOS.St->Init(this); //do we need this ?
	// Copy the data pointer and its length from the source object.
	m_len = other.m_len;	other.m_len = 0;
	m_size = other.m_size;	other.m_size = 0;
	m_txt = other.m_txt;	other.m_txt = NULL;
	m_future_enhancements = other.m_future_enhancements;
	other.m_future_enhancements = NULL;
}

// Move assignment operator.
inline String& String::operator=(String&& other) {
	if (this != &other) {
		C4DOS.St->Flush(this);// Free the existing resource.
		// Copy the data pointer and its length from the source object.
		m_len = other.m_len;	other.m_len = 0;
		m_size = other.m_size;	other.m_size = 0;
		m_txt = other.m_txt;	other.m_txt = NULL;
		m_future_enhancements = other.m_future_enhancements;		
		other.m_future_enhancements = NULL;
	}
	return *this;
}

inline void String::MoveTo(String *str) {
	if(str!=NULL && this != str) {
		C4DOS.St->Flush(str);

		str->m_len = m_len;		m_len = 0;
		str->m_size = m_size;	m_size = 0;
		str->m_txt = m_txt;		m_txt = NULL;
		str->m_future_enhancements = m_future_enhancements;
		m_future_enhancements = NULL;
	}
}
#endif //---------------------------------------------------------------------


inline String LongToString(LONG l) { return C4DOS.St->LongToString(l); }
inline String LLongToString(LLONG l) { return C4DOS.St->LLongToString(l); }
inline String RealToString(Real v, LONG vvk=-1, LONG nnk=-1, Bool e=FALSE, UWORD xchar='0') { return C4DOS.St->RealToString(v,vvk,nnk,e,xchar); }

String PtrToString(const void *hex);
String MemoryToString(LLONG mem);

#define TString(x) String(x) // use TString to mark strings to translate

#endif

#endif
