/*
 * Copyright (C) 2005-2006 Alfresco, Inc.
 *
 * Licensed under the Mozilla Public License version 1.1 
 * with a permitted attribution clause. You may obtain a
 * copy of the License at
 *
 *   http://www.alfresco.org/legal/license.txt
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the
 * License.
 */

#include "util\String.h"

using namespace Alfresco;
using namespace std;

/**
 * Default constructor
 */
String::String() {
	m_string = std::wstring();
}

/**
 * Class constructor
 * 
 * @param alloc const unsigned int
 */
String::String(const unsigned int alloc) {
	m_string = std::wstring();
	m_string.reserve( alloc);
}

/**
 * Class constructor
 *
 * @param str const char*
 */
String::String(const char* str) {

	//	Expand the characters to wide characters and append to the string

	wchar_t wch;

	while ( *str != '\0') {
		wch = (wchar_t) *str++;
		m_string += wch;
	}
}

/**
* Class constructor
*
* @param str const unsigned char*
*/
String::String(const unsigned char* str) {

	//	Expand the characters to wide characters and append to the string

	wchar_t wch;

	while ( *str != '\0') {
		wch = (wchar_t) *str++;
		m_string += wch;
	}
}

/**
 * Class constructor
 * 
 * @param buf const char*
 * @param offset const unsigned int
 * @param len const unsigned int
 */
String::String(const char* buf, const unsigned int offset, const unsigned int len) {

	//	Expand the characters to wide characters and append to the string

	wchar_t wch;

	const char* str = buf + offset;
	unsigned int sLen = len;

	while ( sLen--) {
		wch = (wchar_t) *str++;
		m_string += wch;
	}
}

/**
 * Class constructor
 *
 * @param str  const wchar_t*
 */
String::String(const wchar_t* str) {
	m_string = std::wstring( str);
}

/**
 * Class constructor
 * 
 * @param buf const wchar_t*
 * @param offset const unsigned int
 * @param len const unsigned int
 */
String::String(const wchar_t* buf, const unsigned int offset, const unsigned int len) {
	m_string = std::wstring(buf + offset, len);
}

/**
 * Class constructor
 *
 * @param str const std::wstring&
 */
String::String(const std::wstring& str) {
	m_string = str;
}

/**
 * Copy constructor
 *
 * @param str const String&
 */
String::String(const String& str) {
	m_string = std::wstring(str.data());
}

/**
* Class constructor
*
* @param byts ByteArray&
*/
String::String( ByteArray& byts) {

	//	Expand the characters to wide characters and append to the string

	wchar_t wch;

	for ( unsigned int idx = 0; idx < byts.getLength(); idx++) {
		wch = (wchar_t) byts[idx];
		m_string += wch;
	}
}

/**
 * Compare strings for equality
 *
 * @param str const wchar_t*
 * @return bool
 */
bool String::equals(const wchar_t* str) const {

	//	Check that the string is valid

	if ( str == NULL)
		return false;

	//	Compare the strings

	if ( m_string.compare(str) == 0)
		return true;
	return false;
}

/**
 * Compare strings for equality
 *
 * @param str const String&
 * @return bool
 */
bool String::equals(const String& str) const {

	//	Compare the strings

	if ( m_string.compare(str.data()) == 0)
		return true;
	return false;
}

/**
 * Compare strings for equality ignoring case
 *
 * @param str const wchar_t*
 * @return  bool
 */
bool String::equalsIgnoreCase(const wchar_t* str) const {
	return _wcsicmp( str, data()) == 0 ? true : false;
}

/**
 * Compare strings for equality ignoring case
 *
 * @param str const String&
 * @return bool
 */
bool String::equalsIgnoreCase(const String& str) const {
	return _wcsicmp( str.data(), data()) == 0 ? true : false;
}

/**
 * Compare strings
 * 
 * @param str const String&
 * @return int
 */
int String::compareTo( const String& str) const {
	return m_string.compare( str.getString());
}

/**
 * Compare strings
 * 
 * @param pStr const wchar_t*
 * @return int
 */
int String::compareTo( const wchar_t* pStr) const {
	return m_string.compare( pStr);
}

/**
 * Convert the string to lower case returning the resulting String
 *
 * @return String
 */
String String::toLowerCase() const {

	//	Create a copy of the string then convert to lowercase

	std::wstring lstr(m_string);

	for ( unsigned int i = 0; i < lstr.length(); i++)
		lstr[i] = tolower(lstr[i]);

	return String(lstr);
}

/**
 * Convert the string to upper case returning the resulting String
 *
 * @return String
 */
String String::toUpperCase() const {

	//	Create a copy of the string then convert to uppercase

	std::wstring ustr(m_string);

	for ( unsigned int i = 0; i < ustr.length(); i++)
		ustr[i] = toupper(ustr[i]);

	return String(ustr);
}

/**
 * Return the index of the specified character, or -1 if not found
 *
 * @param ch const wchar_t
 * @param startIndex int
 * @return int
 */
int String::indexOf(const wchar_t ch, int startIndex) const {
	return (int) m_string.find_first_of( ch, startIndex);
}

/**
 * Return the index of the specified string, or -1 if not found
 *
 * @param str const wchar_t*
 * @param startIndex int
 * @return int
 */
int String::indexOf(const wchar_t* str, int startIndex) const {
	return (int) m_string.find_first_of( str, startIndex);
}

/**
 * Return the index of the specified string, or -1 if not found
 *
 * @param str const String&
 * @param startIndex int
 * @return int
 */
int String::indexOf(const String& str, int startIndex) const {
	return (int) m_string.find_first_of( str, startIndex);
}

/**
 * Return the last index of the specified character, or -1 if not found
 *
 * @param ch const wchar_t
 * @param startIndex int
 * @return int
 */
int String::lastIndexOf(const wchar_t ch, int startIndex) const {
	return (int) m_string.find_last_of( ch, startIndex);
}

/**
 * Return the last index of the specified string, or -1 if not found
 *
 * @param str const wchar_t*
 * @param startIndex int
 * @return int
 */
int String::lastIndexOf(const wchar_t* str, int startIndex) const {
	return (int) m_string.find_last_of( str, startIndex);
}

/**
 * Return the last index of the specified string, or -1 if not found
 *
 * @param str const String&
 * @param startIndex int
 * @return int
 */
int String::lastIndexOf(const String& str, int startIndex) const {
	return (int) m_string.find_last_of( str, startIndex);
}

/**
 * Check if this string starts with the specified string.
 *
 * @param str const wchar_t*
 * @return bool
 */
bool String::startsWith(const wchar_t* str) const {

	//	Check if the string to check is valid

	if ( str == NULL)
		return false;

	//	Get the string length, if the comparison string is longer than this string
	//	then there is no match.

	size_t len = wcslen(str);
	if ( str == NULL || wcslen(str) > m_string.length())
		return false;

	//	Check if this string starts with the specified string

	if ( m_string.compare(0, len, str) == 0)
		return true;
	return false;
}

/**
 * Check if this string starts with the specified string.
 *
 * @param str const String&
 * @return bool
 */
bool String::startsWith(const String& str) const {

	//	Get the string length, if the comparison string is longer than this string
	//	then there is no match.

	if ( str.length() > m_string.length())
		return false;

	//	Check if this string starts with the specified string

	if ( m_string.compare(0, str.length(), str.data()) == 0)
		return true;
	return false;
}

/**
 * Check if this string starts with the specified string, ignoring case.
 *
 * @param str const wchar_t*
 * @return bool
 */
bool String::startsWithIgnoreCase(const wchar_t* str) const {

	//	Check if the string to check is valid

	if ( str == NULL)
		return false;

	//	Get the string length, if the comparison string is longer than this string
	//	then there is no match.

	size_t len = wcslen(str);
	if ( str == NULL || wcslen(str) > m_string.length())
		return false;

	//	Check if this string starts with the specified string

	if ( _wcsnicmp(str, data(), len) == 0)
		return true;
	return false;
}

/**
 * Check if this string starts with the specified string, ignoring case.
 *
 * @param str const String&
 * @return bool
 */
bool String::startsWithIgnoreCase(const String& str) const {

	//	Get the string length, if the comparison string is longer than this string
	//	then there is no match.

	if ( str.length() > m_string.length())
		return false;

	//	Check if this string starts with the specified string

	if ( _wcsnicmp( str.data(), data(), str.length()) == 0)
		return true;
	return false;
}

/**
 * Check if this string ends with the specified string.
 *
 * @param str const wchar_t*
 * @return bool
 */
bool String::endsWith(const wchar_t* str) const {

	//	Check if the string to check is valid

	if ( str == NULL)
		return false;

	//	Get the string length, if the comparison string is longer than this string
	//	then there is no match.

	size_t len = wcslen(str);
	if ( str == NULL || wcslen(str) > m_string.length())
		return false;

	//	Check if this string ends with the specified string

	if ( m_string.compare(m_string.length() - len, len, str) == 0)
		return true;
	return false;
}

/**
 * Check if this string ends with the specified string.
 *
 * @param str const String&
 * @return bool
 */
bool String::endsWith(const String& str) const {

	//	Get the string length, if the comparison string is longer than this string
	//	then there is no match.

	if ( str.length() > m_string.length())
		return false;

	//	Check if this string ends with the specified string

	if ( m_string.compare(m_string.length() - str.length(), str.length(), str.data()) == 0)
		return true;
	return false;
}

/**
 * Trim leading and trailing whitespace from the string returning the resulting String
 *
 * @return String
 */
String String::trim( void) const {
	std::wstring str = m_string;
	str.erase( str.find_last_not_of( L" ") + 1);

	return String( str);
}

/**
 * Return a substring of this string
 * 
 * @param beginIndex unsigned int
 * @return String
 */
String String::substring( unsigned int beginIndex) const {
	std::wstring str = m_string.substr( beginIndex);
	return String(str);
}

/**
 * Return a substring of this string
 * 
 * @param beginIndex unsigned int
 * @param endIndex unsigned int
 * @return String
 */
String String::substring( unsigned int beginIndex, unsigned int endIndex) const {
	std::wstring str = m_string.substr( beginIndex, (endIndex - beginIndex));
	return String(str);
}

/**
 * Assignment operator
 *
 * @param str const wchar_t*
 * @return String&
 */
String& String::operator=(const wchar_t* str) {
	m_string = str;
	return *this;
}

/**
 * Assignment operator
 *
 * @param str const String&
 * @return String&
 */
String& String::operator=(const String& str) {
	m_string = str.data();
	return *this;
}

/**
 * Return the string as an array of 8 bit bytes.
 *
 * @param byts ByteArray&
 * @return ByteArray
 */
ByteArray String::getBytes( ByteArray& byts) const {

	//	Create a byte array to hold the byte data

	byts.setLength( length());

	//	Convert the wide characters to ASCII characters

	for ( unsigned int i = 0; i < length(); i++)
		byts[ i] = (char) (charAt(i) & 0xFF);
	return byts;
}

/**
* Return the string as an array of 8 bit bytes.
*
* @return ByteArray
*/
ByteArray String::getBytes( void) const {

	//	Create a byte array to hold the byte data

	ByteArray byts;
	byts.setLength( length());

	//	Convert the wide characters to ASCII characters

	for ( unsigned int i = 0; i < length(); i++)
		byts[ i] = (char) (charAt(i) & 0xFF);
	return byts;
}

/**
 * Equality operator
 * 
 * @param str const String&
 * @return bool
 */
bool String::operator== ( const String& str) const {
	return equals( str);
}

/**
 * Equality operator
 * 
 * @param str const wchar_t*
 * @return bool
 */
bool String::operator== ( const wchar_t* str) const {
	return equals( str);
}

/**
 * Equality operator
 * 
 * @param str const char*
 * @return bool
 */
bool String::operator== ( const char* str) const {
	return equals( String( str));
}

/**
 * Wide character output stream operator
 * 
 * @param out wostream&
 * @param str const String&
 * @return wostream&
 */
std::wostream& Alfresco::operator<< ( std::wostream& out, const Alfresco::String& str) {
	return out << str.data();
}

/**
 * Less than operator
 *
 * @param str const String&
 * @return bool
 */
bool String::operator<( const String& str) const {
	return getString().compare( str.getString()) < 0 ? true : false;
}

/**
 * ASCII character output stream operator
 * 
 * @param out ostream&
 * @param str const String&
 * @return ostream&
 */
std::ostream& Alfresco::operator<< ( std::ostream& out, const Alfresco::String& str) {
	std::string ascStr;
	ascStr.reserve( str.length());

	for ( unsigned int i = 0; i < str.length(); i++)
		ascStr += (char) ( str.charAt( i) & 0xFF);
	return out << ascStr.c_str();
}

/**
* Replace occurrences of the character oldCh with newCh
* 
* @param oldCh wchar_t
* @param newCh wchar_t
*/
void String::replace( wchar_t oldCh, wchar_t newCh) {
	if ( m_string.size() == 0)
		return;
	for ( unsigned int i = 0; i < m_string.size(); i++) {
		if ( m_string.at( i) == oldCh)
			m_string[i] = newCh;
	}
}

/**
 * Append a character to this string
 * 
 * @param ch wchar_t
 */
void String::append( wchar_t ch) {
	m_string += ch;
}

/**
 * Append a string to this string
 * 
 * @param str const char*
 */
void String::append ( const char* str) {

	//	Expand the characters to wide characters and append to the string

	wchar_t wch;

	while ( *str != '\0') {
		wch = (wchar_t) *str++;
		m_string += wch;
	}
}

/**
 * Append a string to this string
 * 
 * @param str const wchar_t*
 */
void String::append (const wchar_t* str) {
	m_string += str;
}

/**
 * Append a string to this string
 * 
 * @param str const String&
 */
void String::append (const String& str) {
	m_string += str.getString();
}

/**
 * Append an integer value to this string
 * 
 * @param ival const unsigned int
 */
void String::append (const unsigned int ival) {
	wchar_t buf[32];
	swprintf( buf, L"%u", ival);

	m_string += buf;
}

/**
* Append a long value to this string
* 
* @param lval const unsigned long
*/
void String::append (const unsigned long lval) {
	wchar_t buf[32];
	swprintf( buf, L"%lu", lval);

	m_string += buf;
}

/**
* Append a long/64 bit value to this string
* 
* @param l64val const unsigned long
*/
void String::append (const LONG64 l64val) {
	wchar_t buf[32];
	swprintf( buf, L"%I64u", l64val);

	m_string += buf;
}

/**
 * Split a string into tokens
 * 
 * @param delims const String&
 * @return StringList
 */
StringList String::tokenize( const String& delims) const {

	//	Skip leading delimiters

	StringList tokens;
	string::size_type lastPos = m_string.find_first_not_of( delims, 0);

	//	Find a non-delimiter character

	string::size_type pos = m_string.find_first_of( delims, lastPos);

	while ( pos != string::npos || lastPos != string::npos) {

		//	Add the current token to the list

		tokens.addString( m_string.substr( lastPos, pos - lastPos));

		//	Skip delimiter(s)

		lastPos = m_string.find_first_not_of( delims, pos);

		//	Find next token

		pos = m_string.find_first_of( delims, lastPos);
	}

	//	Return the token list

	return tokens;
}

/**
 * Default constructor
 */
StringList::StringList( void) {
}

/**
 * Class constructor
 * 
 * @param reserve unsigned int
 */
StringList::StringList( unsigned int reserve) {
	m_list.reserve( reserve);
}

/**
 * Copy constructor
 * 
 * @param strList const StringList&
 */
StringList::StringList( const StringList& strList) {
	copyFrom( strList);
}

/**
 * Copy strings from the specified list
 * 
 * @param strList const StringList&
 */
void StringList::copyFrom( const StringList& strList) {
	for ( unsigned int idx = 0; idx < strList.numberOfStrings(); idx++)
		addString( strList.getStringAt( idx));
}

/**
 * Check if the list contains the string
 * 
 * @param str const String&
 * @return bool
 */
bool StringList::containsString ( const String& str) {
	for ( std::vector<String>::iterator pos = m_list.begin(); pos < m_list.end(); pos++) {
		if ( str.equals( *pos))
			return true;
	}
	return false;
}

/**
 * Check if the list contains the string, ignoring case
 * 
 * @param str const String&
 * @return bool
 */
bool StringList::containsStringCaseless ( const String& str) {
	for ( std::vector<String>::iterator pos = m_list.begin(); pos < m_list.end(); pos++) {
		if ( str.equalsIgnoreCase( *pos))
			return true;
	}
	return false;
}

/**
 * Find the specified string and return the position within the list, or -1 if not found
 * 
 * @param str const String&
 * @return int
 */
int StringList::indexOf( const String& str) const {
	for ( unsigned int i = 0; i < m_list.size(); i++) {
		if ( m_list[i].equals( str))
			return (int) i;
	}
	return -1;
}

/**
 * Remove the specified string from the list
 * 
 * @param str const String&
 */
void StringList::removeString ( const String& str) {
	for ( std::vector<String>::iterator pos = m_list.begin(); pos < m_list.end(); pos++) {
		if ( str.equals( *pos)) {
			m_list.erase( pos);
			return;
		}
	}
}

/**
 * Remove the specified string from the list, ignoring case
 * 
 * @param str const String&
 */
void StringList::removeStringCaseless ( const String& str) {
	for ( std::vector<String>::iterator pos = m_list.begin(); pos < m_list.end(); pos++) {
		if ( str.equalsIgnoreCase( *pos)) {
			m_list.erase( pos);
			return;
		}
	}
}

/**
 * Return the string list as a comma separated string
 * 
 * @return String
 */
String StringList::toString( void) const {
	String ret;

	for ( unsigned int i = 0; i < numberOfStrings(); i++) {
		ret += getStringAt( i);
		ret += ",";
	}

	return ret;
}
