/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 1, or (at your option)
** any later version.

** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.

** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
 * Author : Alexandre Parenteau <aubonbeurre@hotmail.com> --- May 1999
 */

#ifndef USTR_H
#define USTR_H

#include <stddef.h>
#include <string.h>
#include "uconfig.h"

#ifdef qUnix
#	include <string>
#endif

#ifdef __GNUC__
#	define EGCS_CONST const
#else
#	define EGCS_CONST
#endif

#define MAX_ARGS	40
#define MAX_CMD_LEN	2048

#ifdef WIN32
static const char kPathDelimiter = '\\';
#else
static const char kPathDelimiter = '/';
#endif

#define CPStr UPStr
#define CStr UStr

class UStr;

#if TARGET_RT_MAC_MACHO || qUnix
#	ifndef HAVE_STRICMP
#		define stricmp strcasecmp
#	endif
#	ifndef HAVE_STRNICMP
#		define strnicmp strncasecmp
#	endif
#endif

bool SplitPath(const char* dir, UStr& uppath, UStr& folder, bool dontSkipDelimiter = false);
bool MakeTmpFile(UStr& file, const char* prefix, const char* extension = 0L, bool create = false);
void GetExtension(const char* file, UStr& base, UStr& ext);
bool GetEnvValue(const char* cmd, const char* key, UStr& value);
void FlatenSubFolders(UStr& tmpf);
UStr& NormalizeFileName(UStr& fileName);

char* CleanupLogMsg(const char* msg);
int StrOrdICmp(const char* s1, const char* s2);
int StrPathCmp(const char* s1, const char* s2);
void StrSafeCopy(char* buffer, const char* value, const int bufferSize);
int StringToArgv(const char* cmdLine, char** argv);

std::string& Replace(std::string& strRes, const char* strFind, const char* strReplace);

/// Class to use when fed-up with realloc, malloc...
template <class T>
class CStaticAllocT
{
public:
	// Construction
	CStaticAllocT();
	~CStaticAllocT();
	
protected:
	// Data members
	T* m_buf;			/*!< Buffer */
	size_t m_bufsize;	/*!< Buffer size */
	
public:
	// Interface
	void AdjustSize(size_t size);
	
	inline operator T*(void) const /* egcs const */;
	inline operator const T*(void) const;
	
#ifdef __GNUC__
	inline const T& operator[](int i) const;
	inline T& operator[](int i);
#endif
	
	inline size_t size() const;
};

/// Up to 255 characters pascal and C string class
class UEXPORT UPStr
{
public:
	// Construction
	inline UPStr();
	inline UPStr(const char* newstr);
	inline UPStr(const unsigned char* newstr);
	inline UPStr(const UPStr& newstr);
	virtual ~UPStr();

protected:
	// Data members
	char* m_str;	/*!< The String */
	
	// Methods
	void clear(void);

public:
	// Interface
	inline bool empty(void) const;
	inline unsigned int length(void) const;

	UPStr& operator=(const char* newstr);
	UPStr& operator=(const unsigned char* newstr);
	UPStr& operator=(const UPStr& newstr);
	
	inline operator const char*() const;
//	inline operator char*() EGCS_CONST;
	inline operator const unsigned char*() const;
//	inline operator unsigned char*() EGCS_CONST;

	UPStr& set(const char* buf, unsigned int len);
	inline const char* c_str() const;
	inline const unsigned char* p_str() const;

	UPStr& operator<<(const char* addToStr);
	UPStr& operator<<(char addToStr);
	UPStr& operator<<(int addToStr);

	inline bool endsWith(char c) const;
	UPStr& replace(char which, char bywhich);
};

/// C string class
class UEXPORT UStr
{
public:
	// Construction
	inline UStr();
	inline UStr(const char* newstr);
	inline UStr(const unsigned char* newstr);
	inline UStr(const UStr& newstr);
	virtual ~UStr();

protected:
	// Data members
	char* m_str;	/*!< The String */
	
public:
	// Interface
	void clear(void);
	
	inline bool empty(void) const;
	inline unsigned int length(void) const;

	UStr& operator=(const char* newstr);
	UStr& operator=(const unsigned char* newstr);
	UStr& operator=(const UStr& newstr);
	UStr& set(const char* buf, unsigned int len);

	inline bool operator<(const UStr& str) const;
	inline bool operator>(const UStr& str) const;
	inline bool operator==(const UStr& str) const;
	inline bool operator==(const char* str) const;
	inline bool operator!=(const UStr& str) const;
	inline bool operator!=(const char* str) const;

#if (defined(__MWERKS__) && __MWERKS__ < 0x2400) || defined(__GNUC__)
	inline char operator[](int index) const;
	inline char & operator[](int index);
#endif /* TARGET_OS_MAC */

	inline operator const char*() const;
//	inline operator char*() EGCS_CONST;
	inline const char* c_str() const;

	UStr& operator<<(const char* addToStr);
	UStr& operator<<(char addToStr);
	UStr& operator<<(int addToStr);
	inline UStr& operator+=(int addToStr);
//	inline UStr& operator+=(char* addToStr);
	inline UStr& operator+=(const char* addToStr);
	inline UStr& operator+=(const UStr& addToStr);

	inline int compare(const char* thestr) const;
	//inline bool compareNoCase(const UStr & str) const;
	//inline bool compareNoCase(const char *str) const;
	inline bool endsWith(char c) const;
	
	//! replace a character
	UStr& replace(char which, char bywhich);
	//! replace a substring
	const UStr & Replace(const char* which, char *bywhich);
	
	int find(const char* thestr) const;
	int rfind(const char* thestr) const;
	
	int find(char thechar) const;
	int rfind(char thechar) const;
		
//	char* substr(int idx, int len) const;
//	UStr& substr(int idx, int len, UStr& subStr) const;
	UStr substr(int idx, int len) const;

	UStr& trim (char c = ' ');
	UStr& rtrim (char c);
	UStr& rtrim (const char *MatchList);
};

//////////////////////////////////////////////////////////////////////////
// UPStr inline implementation

inline UPStr::UPStr()
	: m_str(0L)
{
}

inline UPStr::UPStr(const char* newstr)
{
	m_str = 0L;
	*this = newstr;
}


inline UPStr::UPStr(const unsigned char* newstr)
{
	m_str = 0L;
	*this = newstr;
}


inline UPStr::UPStr(const UPStr& newstr)
{
	m_str = 0L;
	*this = newstr;
}

/*!
	Check whether the string is empty
	\return true if the string is empty, false otherwise
*/
inline bool UPStr::empty(void) const
{
	return m_str == 0L || m_str[0] == '\0';
}

/*!
	Get the string length
	\return The string length
*/
inline unsigned int UPStr::length(void) const
{
	return m_str == 0L ? 0 : (unsigned char)m_str[0];
}

/// Access the string as a C string
inline UPStr::operator const char*() const
{
	return m_str == 0L ? "" : m_str + 1;
}

#if 0
/// Access the string as a C string
inline UPStr::operator char*() EGCS_CONST
{
	return m_str == 0L ? 0L : m_str + 1;
}
#endif

/// Access the string as a C string
inline const char* UPStr::c_str() const
{
	return m_str == 0L ? "" : m_str + 1;
}


/// Access the string as a P string
inline UPStr::operator const unsigned char*() const
{
	return m_str == 0L ? (const unsigned char*)"" : (const unsigned char*)m_str;
}

#if 0
/// Access the string as a P string
inline UPStr::operator unsigned char*() EGCS_CONST
{
	return m_str == 0L ? (unsigned char*)"" : (unsigned char*)m_str;
}
#endif

/// Access the string as a P string
inline const unsigned char* UPStr::p_str() const
{
	return m_str == 0L ? (unsigned char*)"" : (unsigned char*)m_str;
}


/*!
	Check whether the string is ending with a given character
	\param c The character to check
	\return true if the string ends with a character, false otherwise
*/
inline bool UPStr::endsWith(char c) const
{
	return m_str == 0L ? false : m_str[m_str[0]] == c;
}

//////////////////////////////////////////////////////////////////////////
// UStr inline implementation

inline UStr::UStr()
	: m_str(0L)
{
}

inline UStr::UStr(const char* newstr)
{
	m_str = 0L;
	*this = newstr;
}


inline UStr::UStr(const unsigned char* newstr)
{
	m_str = 0L;
	set((const char*)newstr + 1, newstr[0]);
}


inline UStr::UStr(const UStr& newstr)
{
	m_str = 0L;
	*this = newstr;
}

/*!
	Check whether the string is empty
	\return true if the string is empty, false otherwise
*/
inline bool UStr::empty(void) const
{
	return m_str == 0L || m_str[0] == '\0';
}

/*!
	Get the string length
	\return The string length
*/
inline unsigned int UStr::length(void) const
{
	return m_str == 0L ? 0 : strlen(m_str);
}

#if (defined(__MWERKS__) && __MWERKS__ < 0x2400) || defined(__GNUC__)
inline char UStr::operator[](int index) const
{
	return m_str[index];
}

inline char& UStr::operator[](int index)
{
	return m_str[index];
}
#endif /* TARGET_OS_MAC */

//! as a C string
inline UStr::operator const char*() const
{
	return m_str == 0L ? "" : m_str;
}

#if 0
/// Access the string as a C string
inline UStr::operator char*() EGCS_CONST
{
	return m_str == 0L ? (char*)"" : m_str;
}
#endif

/// Access the string as a C string
inline const char* UStr::c_str() const
{
	return m_str == 0L ? "" : m_str;
}

#if 0
//! Concatenate
inline UStr& UStr::operator+=(char* addToStr)
{
	return *this << addToStr;
}
#endif

/// Concatenate
inline UStr& UStr::operator+=(const char* addToStr)
{
	return *this << addToStr;
}

/// Concatenate
inline UStr& UStr::operator+=(const UStr& addToStr)
{
	return *this << (const char*)addToStr;
}

/// Concatenate
inline UStr& UStr::operator+=(int addToStr)
{
	return *this << addToStr;
}


//! compare

/// Compare, provided to compile with STL
inline bool UStr::operator<(const UStr& str) const
{
	return strcmp(*this, str) < 0;
}

/// Compare, provided to compile with STL
inline bool UStr::operator>(const UStr& str) const
{
	return strcmp(*this, str) > 0;
}

/// Compare, provided to compile with STL
inline bool UStr::operator==(const UStr& str) const
{
	return strcmp(*this, str) == 0;
}

/// Compare, provided to compile with STL
inline bool UStr::operator==(const char* str) const
{
	return strcmp(*this, str) == 0;
}

/// Compare, provided to compile with STL
inline bool UStr::operator!=(const UStr& str) const
{
	return strcmp(*this, str) != 0;
}

/// Compare, provided to compile with STL
inline bool UStr::operator!=(const char* str) const
{
	return strcmp(*this, str) != 0;
}

/*!
	Compare the string with another one
	\param thestr String to compare with
	\return -1, 0 and 1 as appropriate
*/
inline int UStr::compare(const char* thestr) const
{	
	return strcmp(*this, thestr);
}

/*!
	Check whether the string is ending with a given character
	\param c The character to check
	\return true if the string ends with a character, false otherwise
*/
inline bool UStr::endsWith(char c) const
{
	return !length() ? false : m_str[length()-1] == c;
}

//////////////////////////////////////////////////////////////////////////
// CStaticAllocT inline implementation

template <class T>
CStaticAllocT<T>::CStaticAllocT()
{
	m_buf = 0L;
	m_bufsize = 0;
}

template <class T>
CStaticAllocT<T>::~CStaticAllocT()
{
	if( m_buf != 0L )
		free(m_buf);
}

/*!
	Adjust the buffer size
	\param size New buffer size
*/
template <class T>
void CStaticAllocT<T>::AdjustSize(size_t size)
{
	if( m_bufsize < size )
	{
		if( m_buf == 0L )
			m_buf = (T*)malloc(size * sizeof(T));
		else
			m_buf = (T*)realloc(m_buf, size * sizeof(T));
		
		if( m_buf == 0L )
			throw std::bad_alloc();
		
		m_bufsize = size;
	}
}

/// Buffer access
template <class T>
inline CStaticAllocT<T>::operator T*(void) const /* egcs const */
{
	return m_buf;
}

/// Buffer access
template <class T>
inline CStaticAllocT<T>::operator const T*(void) const
{
	return m_buf;
}

#ifdef __GNUC__
template <class T>
inline const T& CStaticAllocT<T>::operator[](int i) const
{
	return m_buf[i];
}

template <class T>
inline T& CStaticAllocT<T>::operator[](int i)
{
	return m_buf[i];
}
#endif

/*!
	Get the buffer size
	\return The buffer size
*/
template <class T>
inline size_t CStaticAllocT<T>::size() const
{
	return m_bufsize;
}

#endif /* USTR_H */
