#include "munga.h"
#include "cstr.h"
#include "memstrm.h"

//namespace Munga
//{
	rString StringRepresentation::operator+=(char chr)
	{
		Check(this);
		size_t len = strlen(&firstByte);
		*(&firstByte + len++) = chr;
		*(&firstByte + len) = '\0';
		return *this;
	}

	#if defined(USE_SIGNATURE)
		int Is_Signature_Bad(const StringRepresentation *)
		{
			return false;
		}
	#endif

	MemoryStream& MemoryStream_Read(MemoryStream *stream, StringRepresentation *str)
	{
		Check(stream);
		Check(str);

		pString ptr = *str;
		for (*stream >> ptr[(size_t)0]; ptr[(size_t)0]; ++ptr)
			*stream >> ptr[(size_t)0];
		return *stream;
	}

	MemoryStream& MemoryStream_Write(MemoryStream *stream, const StringRepresentation &str)
	{
		Check(stream);
		Check(&str);

		return stream->WriteBytes((const char*)str, str.GetLength() + 1);
	}

	#define DELETE_FILL_CHAR ('0')

	size_t CStringRepresentation::allocationIncrement = 8;

	inline size_t CStringRepresentation::CalculateSize(size_t needed)
	{
		Verify(!Small_Enough(allocationIncrement));
		size_t x = ((needed + allocationIncrement) / allocationIncrement) * allocationIncrement;
		return x;
	}

	CStringRepresentation::CStringRepresentation()
	{
		stringLength = 0;
		stringSize = 0;
		stringText = NULL;
		referenceCount = 0;
	}

	CStringRepresentation::CStringRepresentation(const CStringRepresentation &str)
	{
		Check(&str);
		stringLength = str.stringLength;
		stringSize = str.stringSize;

		if (str.stringText == NULL)
			stringText = NULL;
		else
		{
			stringText = new char[stringSize];
			Register_Pointer(stringText);
			Mem_Copy(stringText, str.stringText, stringLength + 1, stringSize);
		}
		referenceCount = 0;
	}

	CStringRepresentation::CStringRepresentation(const char *cstr)
	{
		if ((cstr == NULL) || (cstr[0] == '\x00'))
		{
			stringLength = 0;
			stringSize = 0;
			stringText = NULL;
		}
		else
		{
			stringLength = strlen(cstr);
			stringSize = CalculateSize(stringLength);

			stringText = new char[stringSize];
			Register_Pointer(stringText);
			Mem_Copy(stringText, cstr, stringLength + 1, stringSize);
		}
		referenceCount = 0;
	}

	CStringRepresentation::~CStringRepresentation()
	{
		if (stringText != NULL)
		{
	#if DEBUG_LEVEL>0
			memset(stringText, DELETE_FILL_CHAR, stringSize);
	#endif
			Unregister_Pointer(stringText);
			delete[] stringText;
		}
	}

	Logical CStringRepresentation::TestInstance() const
	{
		if (stringText == NULL)
		{
			Verify(stringSize == 0);
			Verify(stringLength == 0);
		}
		else
		{
			Verify(stringSize > 0);
			Verify(stringLength == strlen(stringText));
		}
		return true;
	}

	CStringRepresentation CStringRepresentation::operator = (const CStringRepresentation &str)
	{
		Check(this);

		if (this == &str)
			return *this;

		if (stringText != NULL)
		{
	#if DEBUG_LEVEL>0
			memset(stringText, DELETE_FILL_CHAR, stringSize);
	#endif
			Unregister_Pointer(stringText);
			delete[] stringText;
		}

		Check(&str);
		stringLength = str.stringLength;
		stringSize = str.stringSize;

		if (stringSize == 0)
			stringText = NULL;
		else
		{
			stringText = new char[stringSize];
			Register_Pointer(stringText);
			Mem_Copy(stringText, str.stringText, stringLength + 1, stringSize);
		}

		return *this;
	}

	CStringRepresentation CStringRepresentation::operator = (const char *cstr)
	{
		Check(this);
		
		if (stringText != NULL)
		{
	#if DEBUG_LEVEL>0
			memset(stringText, DELETE_FILL_CHAR, stringSize);
	#endif
			Unregister_Pointer(stringText);
			delete[] stringText;
		}

		if ((cstr == NULL) || (cstr[0] == '\x00'))
		{
			stringLength = 0;
			stringSize = 0;
			stringText = NULL;
		}
		else
		{
			stringLength = strlen(cstr);
			stringSize = CalculateSize(stringLength);

			stringText = new char[stringSize];
			Register_Pointer(stringText);
			Mem_Copy(stringText, cstr, stringLength + 1, stringSize);
		}

		return *this;
	}

	CStringRepresentation operator + (const CStringRepresentation &str1, const CStringRepresentation &str2)
	{
		Check(&str1);
		Check(&str2);

		CStringRepresentation temp;

		unsigned long totalLen = str1.stringLength + str2.stringLength;

		if (totalLen == 0)
			return temp;
	   
		temp.stringLength = 0;
		temp.stringSize = CStringRepresentation::CalculateSize((size_t)totalLen);
		temp.stringText = new char[temp.stringSize];
		Register_Pointer(temp.stringText);

		Verify(temp.stringSize >= 1);
		temp.stringText[0] = '\000';

		if (str1.stringText != NULL)
		{
			Mem_Copy(temp.stringText, str1.stringText, str1.stringLength, temp.stringSize);
			temp.stringLength = str1.stringLength;
		}

		if (str2.stringText != NULL)
		{
			Verify(temp.stringLength < temp.stringSize);
			Mem_Copy(&temp.stringText[temp.stringLength], str2.stringText, str2.stringLength + 1, temp.stringSize - temp.stringLength);
			temp.stringLength += str2.stringLength;
		}

		Check(&temp);
		return temp;
	}

	CStringRepresentation operator + (const CStringRepresentation &str, char ch)
	{
		Check(&str);

		CStringRepresentation temp;

		if (str.stringText == NULL)
		{
			temp.stringLength = 1;
			temp.stringSize = CStringRepresentation::allocationIncrement;
			temp.stringText = new char [temp.stringSize];
			Register_Pointer(temp.stringText);

			Verify(temp.stringSize >= 2);
			temp.stringText[0] = ch;
			temp.stringText[1] = '\000';
		}
		else
		{
			Verify(str.stringLength != UINT_MAX);

			temp.stringLength = str.stringLength + 1;

			if (temp.stringLength == str.stringSize)
				temp.stringSize = str.stringSize + CStringRepresentation::allocationIncrement;
			else
				temp.stringSize = str.stringSize;

			temp.stringText = new char[temp.stringSize];
			Register_Pointer(temp.stringText);
			Mem_Copy(temp.stringText, str.stringText, str.stringLength, temp.stringSize);

			Verify(str.stringLength < temp.stringSize);
			Verify(temp.stringLength < temp.stringSize);
			temp.stringText[str.stringLength] = ch;
			temp.stringText[temp.stringLength] = '\000';
		}

		Check(&temp);
		return temp;
	}

	int CStringRepresentation::Compare(const CStringRepresentation &str) const
	{
		Check(this);
		Check(&str);

		// handle special cases where one string is empty
		if (stringText == NULL)
		{
			if (str.stringText == NULL)
				return 0;
			else
				return -1;
		}
		if (str.stringText == NULL)
   			return 1;
	   
		return strcmp(stringText, str.stringText);
	}

	CStringRepresentation CStringRepresentation::GetNthToken(size_t nth_token, char *delimiters) const
	{
		Check(this);

		//
		// Which delimters to use
		//
		char *delimter_string = " \t,";

		if (delimiters != NULL)
			delimter_string = delimiters;
		Check_Pointer(delimter_string);

		//
		// Make temporary
		//
		CStringRepresentation temp(*this);

		if (temp.stringText == NULL)
			return temp;

		//
		// Parse string with strtok
		//
		size_t i;
		char *ptr;

		Check_Pointer(temp.stringText);
		ptr = strtok(temp.stringText, delimter_string);
		for (i = 0; i < nth_token; i++)
			if ((ptr = strtok(NULL, delimter_string)) == NULL)
				break;

		if (ptr == NULL)
		{
			CStringRepresentation null_return;
			return null_return;
		}
		CStringRepresentation token_return(ptr);
		return token_return;
	}

	#if 0
		void CStringRepresentation::ToUpper()
		{
			Check(this);
			if (stringText != NULL)
				strupr(stringText);
		}

		void CStringRepresentation::ToLower()
		{
			Check(this);
			if (stringText != NULL)
				strlwr(stringText);
		}
	#endif

	MemoryStream& MemoryStream_Read(MemoryStream *stream, CStringRepresentation *str)
	{
		Check(stream);
		Check(str);

		size_t string_length;
		char *ptr;

		MemoryStream_Read(stream, &string_length);
		ptr = new char[string_length + 1];
		Register_Pointer(ptr);
		stream->ReadBytes(ptr, string_length + 1);

		*str = ptr;

		Unregister_Pointer(ptr);
		delete[] ptr;

		Check(str);
		return *stream;
	}

	MemoryStream& MemoryStream_Write(MemoryStream *stream, const CStringRepresentation &str)
	{
		Check(stream);
		Check(&str);

		MemoryStream_Write(stream, &str.stringLength);
		return stream->WriteBytes(str.stringText, str.stringLength + 1);
	}

	void Convert_From_Ascii(const char *str, CStringRepresentation *value)
	{
		#if 0
			Check_Pointer(str);
			Check(value);
			*value = str;
		#else
			if (str == NULL)
				Fail("Convert_From_Ascii - str == NULL");
			if (value == NULL)
				Fail("Convert_From_Ascii - value == NULL");
			*value = str;
		#endif
	}

	CString CString::operator = (const CString &str)
	{
		Check(this);
		Check(&str);

		if (this == &str)
			return *this;

		Check(representation);
		representation->DecrementReferenceCount();
		representation = str.representation;
		representation->IncrementReferenceCount();
		return *this;
	}

	CString CString::operator = (const char *cstr)
	{
		Check(representation);
		representation->DecrementReferenceCount();
		representation = new CStringRepresentation(cstr);
		Register_Object(representation);
		representation->IncrementReferenceCount();
		return *this;
	}

	MemoryStream& MemoryStream_Read(MemoryStream *stream, CString *str)
	{
		Check(str);
		Check(str->representation);
		str->representation->DecrementReferenceCount();
		str->representation = new CStringRepresentation;
		Register_Object(str->representation);
		str->representation->IncrementReferenceCount();
		Verify(str->representation->referenceCount == 1);
		return MemoryStream_Read(stream, str->representation);
	}

	void Convert_From_Ascii(const char *str, CString *value)
	{
		Check(value);
		Check(value->representation);
		value->representation->DecrementReferenceCount();
		value->representation = new CStringRepresentation;
		Register_Object(value->representation);
		value->representation->IncrementReferenceCount();
		Verify(value->representation->referenceCount == 1);
		Convert_From_Ascii(str, value->representation);
	}
//}

#if defined(TEST_CLASS)
	#include "cstr.tcp"
#endif

