//===========================================================================//
// File:     string.hpp                                                      //
// Title:    Definition of MString class.                                    //
// Project:  Munga                                                           //
// Author:   Eric Huffman                                                    //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 03/21/95  ECH  Initial coding                                                                                                                          //
//---------------------------------------------------------------------------//
// Copyright (c) 1994-1995 Virtual World Entertainment, Inc.                 //
//   All rights reserved worldwide.                                          //
// This unpublished source code is PROPRIETARY and CONFIDENTIAL.             //
//===========================================================================//

#pragma once

#include "Stuff.hpp"
#include "Hash.hpp"
#include "Scalar.hpp"

namespace Stuff
{
	class MString;
}

#if !defined(Spew)
	void
		Spew(
			const char* group,
			const Stuff::MString& string
		);
#endif

namespace MemoryStreamIO {

	Stuff::MemoryStream&
		Read(
			Stuff::MemoryStream* stream,
			Stuff::MString *str
		);
	Stuff::MemoryStream&
		Write(
			Stuff::MemoryStream* stream,
			const Stuff::MString* str
		);

}

namespace GetHashFunctions {
	unsigned
		GetHashValue(const Stuff::MString &value);
}

namespace Stuff {

	class MString;

	class MemoryStream;

	//##########################################################################
	//############################    MString    ###############################
	//##########################################################################

	class MString
		#if defined(_ARMOR)
			: public Stuff::Signature
		#endif
	{
	public:
		static void
			InitializeClass();
		static void
			TerminateClass();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Construction, Destruction
	//
	public:
		MString()
			{m_text = NULL;}
		MString(const MString &str)
			{
				Check_Object(&str); m_text = str.m_text;
				if (m_text)
				{
					char *count = m_text-1; Check_Pointer(count);
					Verify(*count != -1); ++(*count);
				}
			}
		MString(const char *cstr);
		MString(
			const char *cstr1,
			const char *cstr2
		);
		MString(
			const char *cstr,
			char ch
		);

		~MString()
			{DetachReference();}

		static MString
			*s_Empty;

		void
			TestInstance() const
				{if (m_text) Check_Pointer(m_text-1);}
		static bool
			TestClass();

	protected:
		void
			DetachReference()
				{
					Check_Object(this);
					if (m_text)
					{
						char *count = m_text-1; Check_Pointer(count);
						Verify(*count != 0);
						if (!--(*count))
							delete count;
					}
				}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Length, Size
	//
	public:
		//
		// Length returns strlen of string
		//
// Merged in the following minor API change from Black Knight to avoid having
// to add a #define to the Stuff project --CBT 12/12/01
//		unsigned
//			GetLength() const
//				{Check_Pointer(this); Check_Pointer(m_text); return strlen(m_text);}
		unsigned
			GetLength() const
				{
					Check_Pointer(this); 
					if (m_text == NULL)
					{
						return 0;
					}
					else
					{
						Check_Pointer(m_text); 
						return strlen(m_text);
					}
				}

		void
			AllocateLength(unsigned length);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Accesors & Manipulation
	//
	public:
		operator char*()
			{Check_Object(this); return m_text;}
		operator const char*() const
			{Check_Object(this); return m_text;}

		//
		// assignment method
		//
		MString&
			operator=(const MString &str);
		MString&
			operator=(const char *cstr);

		//
		// concatenation methods
		//
		friend MString
			operator + (
				const MString &str1,
				const MString &str2
			)
				{return MString(str1.m_text, str2.m_text);}
		friend MString
			operator + (
				const MString &str1,
				const char* str2
			)
				{return MString(str1.m_text, str2);}

		friend MString
			operator + (
				const MString & str,
				char ch
			)
				{return MString(str.m_text, ch);}

		MString&
			operator +=(const char* str)
				{Check_Object(this); Check_Pointer(str); *this = MString(m_text, str); return *this;}
		MString&
			operator +=(char ch)
				{Check_Object(this); *this = MString(m_text, ch); return *this;}

		//
		// comparison methods
		//
		bool
			operator!()
				{Check_Object(this); return m_text == NULL;}
		friend bool
			Close_Enough(
				const char *str1,
				const char *str2,
				Scalar e = SMALL
			);

		bool
			friend operator<(
				const MString &str1,
				const MString &str2
			)
				{Check_Pointer(str1.m_text); Check_Pointer(str2.m_text); return strcmp(str1.m_text, str2.m_text)<0;}
		bool
			friend operator<(
				const MString &str1,
				const char *str2
			)
				{Check_Pointer(str1.m_text); Check_Pointer(str2); return strcmp(str1.m_text, str2)<0;}

		bool
			friend operator<=(
				const MString &str1,
				const MString &str2
			)
				{Check_Pointer(str1.m_text); Check_Pointer(str2.m_text); return strcmp(str1.m_text, str2.m_text)<=0;}
		bool
			friend operator<=(
				const MString &str1,
				const char *str2
			)
				{Check_Pointer(str1.m_text); Check_Pointer(str2); return strcmp(str1.m_text, str2)<=0;}

		bool
			friend operator>(
				const MString &str1,
				const MString &str2
			)
				{Check_Pointer(str1.m_text); Check_Pointer(str2.m_text); return strcmp(str1.m_text, str2.m_text)>0;}
		bool
			friend operator>(
				const MString &str1,
				const char *str2
			)
				{Check_Pointer(str1.m_text); Check_Pointer(str2); return strcmp(str1.m_text, str2)>0;}

		bool
			friend operator>=(
				const MString &str1,
				const MString &str2
			)
				{Check_Pointer(str1.m_text); Check_Pointer(str2.m_text); return strcmp(str1.m_text, str2.m_text)>=0;}
			friend operator>=(
				const MString &str1,
				const char *str2
			)
				{Check_Pointer(str1.m_text); Check_Pointer(str2); return strcmp(str1.m_text, str2)>=0;}

		bool
			friend operator==(
				const MString &str1,
				const MString &str2
			)
				{Check_Pointer(str1.m_text); Check_Pointer(str2.m_text); return strcmp(str1.m_text, str2.m_text)==0;}
		bool
			friend operator==(
				const MString &str1,
				const char *str2
			)
				{Check_Pointer(str1.m_text); Check_Pointer(str2); return strcmp(str1.m_text, str2)==0;}

		bool
			friend operator!=(
				const MString &str1,
				const MString &str2
			)
				{Check_Pointer(str1.m_text); Check_Pointer(str2.m_text); return strcmp(str1.m_text, str2.m_text)!=0;}
		bool
			friend operator!=(
				const MString &str1,
				const char *str2
			)
				{Check_Pointer(str1.m_text); Check_Pointer(str2); return strcmp(str1.m_text, str2)!=0;}

		//
		// character retrieval method		
		//
		const char&
			operator[](int pos) const
				{Check_Object(this); return m_text[pos];}
		char&
			operator[](int pos)
				{Check_Object(this); return m_text[pos];}
	
		MString
			GetNthToken(
				size_t nth_token,
				const char *delimiters=NULL
			) const;

		//
		// case-modification methods
		//
		void
			ToUpper();
		void
			ToLower();


		//
		// white-space removal (replaces spaces with a '_' character)
		//
		void
			RemoveSpaces();

		MString&
			StripExtension();
		MString&
			IsolateDirectory();
		MString&
			StripDirectory();

		//
		// stream input/output methods
		//
		#if !defined(Spew)
			friend void
				::Spew(
					const char* group,
					const MString &string
				)
					{Check_Object(&string); Spew(group, string.m_text);}
		#endif

		friend MemoryStream&
			MemoryStreamIO::Read(
				MemoryStream* stream,
				MString* str
			);

		friend MemoryStream&
			MemoryStreamIO::Write(
				MemoryStream* stream,
				const MString* str
			);

		static unsigned
			ComputeHash(const char* ptr);

		unsigned
			GetHashValue()
				{Check_Pointer(m_text); return ComputeHash(m_text);}

		friend unsigned
			GetHashFunctions::GetHashValue(const MString &value)
				{return Stuff::MString::ComputeHash(value.m_text);}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Private Data
	//
	protected:
		char
			*m_text;
	};

}
