/** @file string.h
 *	Definition of string template for EABaseTypes
 *	@author Jeff Skelton (jskelton@ea.com)
 *
 *	EABaseTypes
 *	(c) 2011 Electronic Arts Inc.
 */

#ifndef __EABASETYPES_STRING_H
#define __EABASETYPES_STRING_H

#include "EASTLICA/string.h"

namespace EA
{
	// RawString
	class RawString : public EASTLICA::String
	{
	public:
		RawString(EA::Allocator::ICoreAllocator *alloc):EASTLICA::String(alloc, "EA::RawString", EA::Allocator::MEM_TEMP) {}
		RawString(EA::Allocator::ICoreAllocator *alloc, const char *str):EASTLICA::String(alloc, "EA::RawString", EA::Allocator::MEM_TEMP) { assign(str); }
		RawString(const EASTLICA::String &rhs):EASTLICA::String(rhs, "EA::RawString", EA::Allocator::MEM_TEMP) {}
	};

	namespace Types
	{
		class Factory;
	}

	// Define EABASETYPES_NO_STRING_INTERN in your build to turn off interning of EA strings
	// This define is here for performance comparisons and unit testing, it is highly recommended that you leave string interning on in your game
#if defined(EABASETYPES_NO_STRING_INTERN)
	class String : public RawString
	{
	public:
		explicit String(Types::Factory* factory);
		String(Types::Factory* factory, const char* str);
		String(const String& str);
		String& operator=(const char* rhs) { assign(rhs); return *this; }
		inline const RawString& raw() const { return *this; }
	};
#else

	/*! @class String string.h	"EABaseTypes/string.h"
	*	@brief An interned string class for storing immutable strings, if you need a mutable string use EA::RawString.
	*	@attention	Once the string table has been created, the String class is threadsafe (although individual Strings are not)
	*/
	class String
	{
	public:

		//typedefs for base container types
		typedef RawString::size_type		size_type;
		typedef RawString::basic_string		basic_string;
		typedef RawString::value_type		value_type;
		typedef RawString::const_reference	const_reference;
		
		/*!
		*	Default C'tor
		*/
		explicit String(Types::Factory* factory);

		/*!
		*	Construct from a const char*
		*	@param[in]	str	the str to copy
		*/
		String(Types::Factory* factory, const char* str);

		/*!
		*	Copy Constructor
		*	@param[in]	str	the str to copy
		*/
		String(const String& str);

		/*!
		*	D'tor
		*/
		~String();

		/*!
		*	Construct a string using formatted data
		*	@param[in] pFormat the string format followed by a variable argument list
		*	@return	String&	A reference to this
		*/
		String&   sprintf(const value_type* pFormat, ...);

		/*!
		*	Assign data to this string, removes and previously assigned data.
		*	@param[in] val the string value to assign
		*/
		void assign(const char* val);

		/*!
		*	Assign data to this string, removes and previously assigned data.
		*	@param[in] val the string value to assign
		*	@param[in] length the string value to assign
		*	@attention the length variable is currently unused, this signature is here to maintain consistancy with the RawString assign api
		*/
		void assign(const char* val, size_type length);

		/*!
		*	Access the const char* data for this string
		*	@return	const char*	The const char* data stored by this 
		*/
		const char* c_str() const;

		/*!
		*	Access the RawString data for this string
		*	@return	const RawString& The RawString& stored by this 
		*/
		const RawString& raw() const;

		/*!
		*	Compare to another string
		*	param[in] b the string to compare to
		*	@return	int equivalent of compare in EASTL
		*/
		int compare(const char* b) const;

		/*!
		*	Compare to another EA::String
		*	param[in] b the string to compare to
		*	@return	int equivalent of compare in EASTL
		*/
		int compare(const EA::String& b) const;

		/*!
		*	Get the strlen
		*	@return	size_type the length of this string
		*/
		size_type length() const;

		/*!
		*	Wrappers for underlying eastl::basic_string::find operations
		*/
		size_type find(const basic_string& x, size_type position = 0) const;
		size_type find(const value_type* p, size_type position = 0) const;
		size_type find(const value_type* p, size_type position, size_type n) const;
		size_type find(value_type c, size_type position = 0) const;

		/*!
		*	Is the string empty
		*	@return	bool true if the string is empty false otherwise
		*/
		bool empty() const;

		/*!
		*	< operator, uses standard string comparision rules
		*	param[in] rhs String to compare
		*	@return	bool true if this is less then rhs, false otherwise
		*/
		bool operator<(const String& rhs) const;

		/*!
		*	> operator, uses standard string comparision rules
		*	param[in] rhs String to compare
		*	@return	bool true if this is less then rhs, false otherwise
		*/
		bool operator>(const String& rhs) const;

		/*!
		*	Index operator
		*	param[in] n index to retrieve 
		*	@return	const_refernace	const reference to the character at index n
		*/
		const_reference	operator[](size_type n) const;

		/*!
		*	Assignment operator
		*	param[in] rhs value to assign
		*	@return	String&	reference to this
		*/
		String& operator=(const char* rhs);

		/*!
		*	Assignment operator
		*	param[in] rhs value to assign
		*	@return	String&	reference to this
		*/
		String& operator=(const String& rhs);

		// Utility functors for EASTL usage
		struct less // Less compare functor for STL
		{
			bool operator()(const String& a, const String& b) const;
		};

		struct equals // Equality functor for STL
		{
			bool operator()(const String& a, const String& b) const;
		};

		struct hash // Hash functor for STL
		{
			size_t operator()(const String& a) const;
		};

		struct is_empty // Predicate for being empty
		{
			bool operator()(const String& a) const;
		};

		struct is_null // Predicate for being null
		{
			bool operator()(const String& a) const;
		};

		struct is_null_or_empty // Predicate for being null or empty
		{
			bool operator()(const String& a) const;
		};

#if defined(EA_RUNTIME_TESTING)
		int GetRefCount() const;
#endif

	private:
		void Clear();

		Types::Factory*		mFactory;

		// const pointer to the string instance this is representing
		const RawString*	mStr;
	};

	//inlined functions
	inline String::~String()
	{
		Clear();
	}

	inline const char* String::c_str() const
	{ 
		return mStr->c_str();
	}

	inline const RawString& String::raw() const
	{
		return (*mStr);
	};

	inline int String::compare(const char* b) const
	{
		return mStr->compare(b);
	}

	inline int String::compare(const String& b) const
	{
		return mStr->compare(b.raw());
	}

	inline String::size_type String::length() const
	{ 
		return mStr->length();
	}

	inline String::size_type String::find(const String::basic_string& x, String::size_type position) const
	{ 
		return mStr->find(x, position);
	}

	inline String::size_type String::find(const String::value_type* p, String::size_type position) const 
	{
		return mStr->find(p, position);
	}

	inline String::size_type String::find(const String::value_type* p, String::size_type position, String::size_type n) const
	{
		return mStr->find(p, position, n);
	}

	inline String::size_type String::find(String::value_type c, String::size_type position) const
	{ 
		return mStr->find(c, position);
	}

	inline bool String::operator<( const String& rhs ) const
	{
		return (mStr->compare(rhs.c_str()) < 0);
	}

	inline bool String::operator>( const String& rhs ) const
	{
		return (mStr->compare(rhs.c_str()) > 0);
	}

	inline bool operator==(const String& a, const String& b)
	{
		return (a.raw() == b.raw());
	}

	inline bool operator==(const char* p, const String& b)
	{
		return (p == b.raw());
	}

	inline bool operator==(const String& a, const char* p)
	{
		return (a.raw() == p);
	}

	inline bool operator!=(const String& a, const String& b)
	{
		return (a.raw() != b.raw());
	}

	inline bool operator!=(const char* p, const String& b)
	{
		return (p != b.raw());
	}

	inline bool operator!=(const String& a, const char* p)
	{
		return (a.raw() != p);
	}

	inline String::const_reference String::operator[]( size_type n ) const
	{
		return (*mStr)[n];
	}


	inline bool String::less::operator()( const String& a, const String& b ) const
	{
		return a < b;
	}

	inline bool String::equals::operator()( const String& a, const String& b ) const
	{
		return a == b;
	}

	inline bool String::is_empty::operator()( const String& a ) const
	{
		return a.empty();
	}


	inline bool String::is_null::operator()( const String& a ) const
	{
		return false;
	}

	inline bool String::is_null_or_empty::operator()( const String& a ) const
	{
		return a.empty();
	}
#endif //EABASETYPES_NO_STRING_INTERN
}

#define EABASETYPES_EASTL_STRING_HASH(TYPE_NAME)\
	namespace eastl\
	{\
		template <>\
		struct hash<TYPE_NAME>\
		{\
			size_t operator()(const TYPE_NAME& str) const\
			{\
				hash<const char8_t*> h;\
				size_t h1 = h(str.c_str());\
				return (h1);\
			}\
		};\
		\
		template <>\
		struct equal_to_2<TYPE_NAME, char*> : public binary_function<TYPE_NAME, char*, bool>\
		{\
			bool operator()(const TYPE_NAME& a, const char*& b) const\
			{ return (a.compare(b) == 0); }\
		};\
	}

EABASETYPES_EASTL_STRING_HASH(EA::RawString)
EABASETYPES_EASTL_STRING_HASH(EA::String)

#define EABASETYPES_RAWSTRING(EABASETYPES_GET_ALLOCATOR)\
	class RawString : public EA::RawString\
	{\
	public:\
		RawString():EA::RawString(EABASETYPES_GET_ALLOCATOR) {}\
		RawString(const EASTLICA::String &rhs):EA::RawString(rhs) {}\
		RawString(const eastl::string &rhs):EA::RawString(EABASETYPES_GET_ALLOCATOR, rhs.c_str()) {}\
		RawString(const char *str):EA::RawString(EABASETYPES_GET_ALLOCATOR, str) {}\
	};

#define EABASETYPES_STRING(EABASETYPES_GET_FACTORY)\
	class String : public EA::String\
	{\
	public:\
		String():EA::String(EABASETYPES_GET_FACTORY) {}\
		String(const EA::String& str):EA::String(EABASETYPES_GET_FACTORY, str.c_str()) {}\
		String(const EASTLICA::String &rhs):EA::String(EABASETYPES_GET_FACTORY, rhs.c_str()) {}\
		String(const eastl::string &rhs):EA::String(EABASETYPES_GET_FACTORY, rhs.c_str()) {}\
		String(const char* str):EA::String(EABASETYPES_GET_FACTORY, str) {}\
	};

#endif // __EABASETYPES_STRING_H
