/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__NSTRING__
#define	__NSTRING__


		#include <cstring>
		#include "container/narray.h"

namespace GS {
	template <class T> class List;
	template <class T> class Array;

/*!
	@short	String class.
	Strings are internally stored as UTF-8.
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
class	String
	{
		Array <char> _p;

		mutable uint hash;
		int			len;

		void		Touch();

public:

enum	CaseSensitivity
{
	CaseSensitive	=	0,
	CaseInsensitive
};

enum	EOLConvention
{
	EOLUnix			=	0,
	EOLWindows
};

		/*!
			@short	Compare string object with a C string
			Optionally accept a hash value for the input string.
			@return	True if strings match.
		*/
		bool		equals(const char *s, const uint hash = 0) const;

		bool		operator == (const String &) const;
		bool		operator != (const String &) const;
		bool		operator == (const char *) const;
		bool		operator != (const char *) const;

		/// Format a string.
static	String		Format(const char *format, ...);

		String		operator + (const char *) const;
		String		operator + (const String &) const;

		void		operator += (const char *);
		void		operator += (const String &);
		void		operator += (float);
		void		operator += (double);
		void		operator += (int);
		void		operator += (uint);
		void		operator += (bool);

		String		&operator = (const String &);
		String		&operator = (const char *);

		void		Set(const char *s, const char *e = 0);

		String		&operator << (const String &);
		String		&operator << (const char *);
		String		&operator << (float);
		String		&operator << (double);
		String		&operator << (int);
		String		&operator << (uint);
		String		&operator << (bool);
		
		/// String hash value.
		uint		Hash() const;
		/// String length.
		uint		Len() const;
		/// String size in bytes.
		size_t		Size() const;

		/// Is empty.
inline	bool		IsEmpty() const { return (!_p || !len) ? true : false; }

		/// Normalize the string end of line.
		void		NormalizeEOL(EOLConvention = EOLUnix);
		/// Return a normalized version of this string.
		String		NormalizedEOL(EOLConvention = EOLUnix) const;

/*!
	@name	File system tool set.
	@{
*/
		/// Is absolute path.
		bool		IsAbsolutePath() const;

		String		BeautifyFileName(bool allow_spaces = true) const;

		String		CleanFilePath() const;

		String		CutFilePath() const;
		String		CutFileName() const;
		String		CutFileExtension() const;

		String		GetFilePath() const;
		String		GetFileName() const;
		String		GetFileNameAndExtension() const;
		String		GetFileExtension() const;

		/// Return this filename with another file extension.
		String		GetSwappedExtension(const char *) const;

		void		FileCutPath();
		void		FileSwapExtension(const char *);
		void		FileCutPathAndExtension();
		void		FileCutExtension();
		void		FileCutName();

		/// Get extension.
static	String		FileGetExtension(const char *path);
		/// Convert backslash to slash, merge redundant slash.
		bool		FileCleanName();
/// @}

		/// Return as utf-8 ASCII C string.
inline	operator	const char *() const { return _p; }
		/// Return as utf-8 ASCII C string.
inline	const char	*c_str() const { return _p; }
inline	char	*str() const { return _p; }
		/// Return a pointer to the end of the string.
inline	const char	*eos() const { return _p + Len(); }
/*!
	@name	Unicode tool set.
	@{
*/
		/// Convert an utf-8 character to utf-32, returns the number of consumed bytes.
static	size_t		Utf8toUtf32(const uchar *utf8, uint *utf32);
		/// Get utf-8 character size in byte.
static	size_t		GetUtf8CharSize(const uchar *utf8);

		/// Create a string from a UCS-2 buffer.
static	String		FromUcs2(const ushort *);

		/// Return as utf-8
inline	const char	*toUtf8() const { return _p; }
		/// Return string as UCS-2 (utf-16 compatible up to code point 0xffff).
		Array <ushort> toUcs2() const;
		/// Return string as utf-32.
		Array <uint> toUtf32() const;
/// @}

		/// Remove all occurrences of a given character from the string.
		String		TrimChar(char);

		/// ASCII to float.
inline	float		Float(bool support_comma = true) const { return atof(_p, _p + Len(), support_comma); }
		/// ASCII to integer.
inline	int			Integer()	const { return atoi(_p); }
		/// ASCII header less, 0x or h prefixed hexadecimal to integer.
inline	int			Hex()	const { return atoh(_p); }

		/// Split string against a separator string.
		template <class C> uint Split(const char *separator, C &container, const char skip = 0) const
		{
			const char *s = c_str();
			if	(!s)
				return 0;
			size_t ls = strlen(separator);

			for (const char *n = s; s[0]; s = n)
			{
				// Check the skip char.
				if	(skip && (n[0] == skip))
					for (++n; n[0] && (n[0] != skip); ++n)
						;

				// Seek the next separator occurrence.
				while (n[0] && (strncmp(n, separator, ls) != 0))
					++n;

				// Append sub-string.
				if	(skip)
					container.Add(String(s, n).TrimChar(skip));
				else
					container.Add(String(s, n));

				if	(!n[0])
					break;

				n += ls;
			}
			return container.GetCount();
		}
		/// Get a slice of the string starting at s ending at e.
		String		Slice(uint s, uint e) const;

		bool		Compare(const char *, CaseSensitivity = CaseSensitive) const;

		/// Test if the string starts with another string.
		bool		StartsWith(const char *, CaseSensitivity = CaseSensitive) const;
		/// Test if the string ends with another string.
		bool		EndsWith(const char *, CaseSensitivity = CaseSensitive) const;
		/// Test if the string contains another string.
		bool		Contains(const char *, CaseSensitivity = CaseSensitive) const;

		String		Upper() const;
		String		Lower() const;

		/// Get the n leftmost characters from the string.
		String		Left(uint n) const;
		/// Get the n rightmost characters from the string.
		String		Right(uint n) const;
		/// Get 'n' characters starting at p from the string.
		String		Mid(uint p, int n = -1) const;

		/// Return the index of a specific character in string, starting at p.
		size_t		IndexOf(char c, uint p = 0) const;

		/// Return the address of a substring in string, starting at p.
		char		*FindString(const char *c, uint p = 0, CaseSensitivity = CaseSensitive) const;

		bool		Replace(const char *what, const char *by, CaseSensitivity cs = CaseSensitive);
		bool		ReplaceAll(const char *what, const char *by, bool match_whole_word = false);
		bool		ReplaceAll(const char **what, const char **by, bool match_whole_word = false);

static	const char	*LocatePattern(const char *s, const char *format, const char **e = 0);
		bool		Extract(const char *format, List <String> &arg);

typedef	String		RewriteFunc(const char *pattern, List <String> &arg);
static	String		RewritePatternAll(const char *p, const char *pattern, RewriteFunc &rewrite_func);

/*!
	@short	C string library.
	@{
*/
		/// Compare two strings, returns -1 if b < a, 0 if a == b, 1 if b > a.
static	int			Compare(const String &a, const String &b);

		/*!
			@short	Returns the address where a given character can be found.

			Returns the address where a given character can be found inside the
			memory block starting at 'from' and ending at 'to'.
		*/
static	char		*strfindchar(const char *from, const char c, const char *to = 0);

		/*!
			@short	Compare the shortest C string with the other C string.
			Returns false if matching. (eg: 'fore' and 'foremost' do match).
		*/
static	bool		strccmp(const char *, const char *);
		/// Compute string hash value.
static	uint		strhash(const char *);
		/// Compute string length.
static	size_t		strlen(const char *);

		/// Remove path.
static	void		rmpath(char *);
		/// Remove extension.
static	void		rmext(char *);
		/// Remove path and extension.
static	void		rmpathext(char *);
		/// Remove filename.
static	void		rmname(char *);

		/// Integer to ASCII.
static	char		*itoa(int);

		/// ASCII to float.
static	float		atof(const char *s, const char *e = 0, bool support_comma = true);
		/// ASCII to integer.
static	int			atoi(const char *s);
		/// ASCII header-less, 0x or h prefixed hexadecimal to integer.
static	int			atoh(const char *s);


/// @}

		//-------------------------------------------------------------------
		/*!
			@short Reserve space on the string for a number of characters.
			@note An extra byte will be allocated to store the string terminator.
		*/
		bool		Allocate(uint size);
		void		Clear();

		String(const char *s) : _p(Alloc::StringBuffer)					{	Clear(); *this = s;	}
		String(const String &b) : _p(Alloc::StringBuffer)				{	Clear(); *this = b.c_str();	}
		String(const char *s, const char *e) : _p(Alloc::StringBuffer)	{	Clear(); Set(s, e);	}
		String(const char *s, size_t l) : _p(Alloc::StringBuffer)		{	Clear(); Set(s, s + l);	}
		String() : _p(Alloc::StringBuffer)								{	Clear();	}
		~String()														{	Clear();	}
};

typedef	List <String> StringList;

} // GS


#endif	// __NSTRING__
