//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * 文字列実装
 * @author Junpee
 */

#include "LampBasic.h"
#include "Core/Primitive/String.h"
#include "Core/System/StringMethod.h"

namespace Lamp{

//------------------------------------------------------------------------------
// ユーティリティ関数
//------------------------------------------------------------------------------
/**
 * 新しい文字列の作成
 * @param length 文字列長
 * @return 文字列
 */
inline static char* newString(int length){
	char* returnString = new char[sizeof(int) + length + 1];
	// リファレンスカウンタに1を設定
	*(int*)returnString = 1;
	returnString += sizeof(int);
	return returnString;
}
//------------------------------------------------------------------------------
/**
 * 文字列の解放
 * @param string 解放する文字列
 * @return リファレンスカウンタ
 */
inline static int releaseString(char** string){
	if((*string) != NULL){
		// リファレンスカウンタデクリメント
		int* refCount = (int*)((*string) - sizeof(int));
		(*refCount) -= 1;
		int returnCount = *refCount;
		if(returnCount == 0){ delete[] refCount; }
		(*string) = NULL;
		return returnCount;
	}
	return 0;
}
//------------------------------------------------------------------------------
/**
 * リファレンスカウンタの追加
 * @param string リファレンスを追加する文字列
 * @return リファレンスカウンタ
 */
inline static int addReferenceCounter(char* string){
	// リファレンスカウンタをインクリメント
	int* counterAddress = (int*)(string - sizeof(int));
	*counterAddress += 1;
	return *counterAddress;
}
//------------------------------------------------------------------------------
/**
 * リファレンスカウンタの取得
 * @param string リファレンスカウンタを取得する文字列
 * @return リファレンスカウンタ
 */
inline static int getReferenceCounter(char* string){
	return *(int*)(string - sizeof(int));
}
//------------------------------------------------------------------------------
// 文字列の構築、破棄
//------------------------------------------------------------------------------
// コンストラクタ
String::String() : string_(NULL), size_(0){
}
//------------------------------------------------------------------------------
// コンストラクタ
String::String(const char* initString){
	if(initString == NULL){
		string_ = NULL;
		size_ = 0;
		return;
	}
	size_ = (int)StdStrlen(initString);
	Assert(size_ >= 0);
	if(size_ == 0){
		string_ = NULL;
		return;
	}
	string_ = newString(size_);
	StdStrcpy(string_, initString);
}
//------------------------------------------------------------------------------
// コピーコンストラクタ
String::String(const String& copy){
	size_ = copy.getSize();
	Assert(size_ >= 0);
	if(size_ == 0){
		string_ = NULL;
		return;
	}
	string_ = copy.string_;
	addReferenceCounter(string_);
}
//------------------------------------------------------------------------------
// 内部使用コンストラクタ
String::String(char* initString, int initLength){
	Assert(initString != NULL);
	Assert(initLength >= 0);
	string_ = initString;
	size_ = initLength;
}
//------------------------------------------------------------------------------
// 代入演算子
String& String::operator =(const String& copy){
	// 自分自身ならリターン
	if(this == &copy){ return *this; }
	releaseString(&string_);
	size_ = copy.getSize();
	Assert(size_ >= 0);
	if(size_ == 0){
		string_ = NULL;
		return *this;
	}
	string_ = copy.string_;
	addReferenceCounter(string_);
	return *this;
}
//------------------------------------------------------------------------------
// 代入演算子
String& String::operator =(const char* copy){
	releaseString(&string_);
	if(copy == NULL){
		string_ = NULL;
		size_ = 0;
		return *this;
	}
	size_ = (int)StdStrlen(copy);
	Assert(size_ >= 0);
	if(size_ == 0){
		string_ = NULL;
		return *this;
	}
	string_ = newString(size_);
	StdStrcpy(string_, copy);
	return *this;
}
//------------------------------------------------------------------------------
// デストラクタ
String::~String(){
	releaseString(&string_);
}
//------------------------------------------------------------------------------
// 文字列の情報を取得する
//------------------------------------------------------------------------------
// 文字数を返す
int String::getCharacterCount() const{
	return (int)StdMbslen(getBytes());
}
//------------------------------------------------------------------------------
// 部分文字列の取得
String String::getSubstring(int beginIndex) const{
	if(beginIndex == 0){ return *this; }
	Assert(beginIndex >= 0);
	Assert((beginIndex <= size_));
	// 空文字列になる
	if(beginIndex == size_){ return String(); }
	int newLength = size_ - beginIndex;
	char* newArray = newString(newLength);
	StdStrcpy(newArray, string_ + beginIndex);
	return String(newArray, newLength);
}
//------------------------------------------------------------------------------
// 部分文字列の取得
String String::getSubstring(int beginIndex, int endIndex) const{
	Assert(beginIndex >= 0);
	Assert(endIndex >= 0);
	Assert((endIndex <= size_));
	Assert((beginIndex <= endIndex));
	// 空文字列になる
	if(beginIndex == endIndex){ return String(); }
	int newLength = endIndex - beginIndex;
	char* newArray = newString(newLength);
	for(int i = 0; i < newLength; i++){
		newArray[i] = string_[i + beginIndex];
	}
	newArray[newLength] = '\0';
	return String(newArray, newLength);
}
//------------------------------------------------------------------------------
// 大文字化した文字列の取得
String String::getUpperCase() const{
	// コピーを作成する必要がある
	String returnString(this->getBytes());
	if(returnString.getSize() == 0){ return returnString; }
	StdStrupr(returnString.string_);
	return returnString;
}
//------------------------------------------------------------------------------
// 小文字化した文字列の取得
String String::getLowerCase() const{
	// コピーを作成する必要がある
	String returnString(this->getBytes());
	if(returnString.getSize() == 0){ return returnString; }
	StdStrlwr(returnString.string_);
	return returnString;
}
//------------------------------------------------------------------------------
// ハッシュコードの取得
u_int String::getHashCode() const{
	u_int hashCode = 0;
	if(size_ == 0){ return hashCode; }
	const char* position = getBytes();
	for(int i = 0;i < size_;i++){
		hashCode = hashCode * 31 + position[i];
	}
	return hashCode;
}
//------------------------------------------------------------------------------
// 文字列をパースする
//------------------------------------------------------------------------------
// charへのパース
bool String::parseChar(char* value) const{
	Assert(value != NULL);
	int scanValue;
	int scanCount = StdSscanf(string_, "%d", &scanValue);
	if((scanCount == 1) &&
		(scanValue <= Limit::charMax) && (scanValue >= Limit::charMin)){
		*value = (char)scanValue;
		return true;
	}
	return false;
}
//------------------------------------------------------------------------------
// u_charへのパース
bool String::parseUChar(u_char* value) const{
	Assert(value != NULL);
	int scanValue;
	int scanCount = StdSscanf(string_, "%d", &scanValue);
	if((scanCount == 1) &&
		(scanValue <= Limit::uCharMax) && (scanValue >= Limit::uCharMin)){
		*value = (u_char)scanValue;
		return true;
	}
	return false;
}
//------------------------------------------------------------------------------
// shortへのパース
bool String::parseShort(short* value) const{
	Assert(value != NULL);
	int scanValue;
	int scanCount = StdSscanf(string_, "%d", &scanValue);
	if((scanCount == 1) &&
		(scanValue <= Limit::shortMax) && (scanValue >= Limit::shortMin)){
		*value = (short)scanValue;
		return true;
	}
	return false;
}
//------------------------------------------------------------------------------
// u_shortへのパース
bool String::parseUShort(u_short* value) const{
	Assert(value != NULL);
	int scanValue;
	int scanCount = StdSscanf(string_, "%d", &scanValue);
	if((scanCount == 1) &&
		(scanValue <= Limit::uShortMax) && (scanValue >= Limit::uShortMin)){
		*value = (u_short)scanValue;
		return true;
	}
	return false;
}
//------------------------------------------------------------------------------
// intへのパース
bool String::parseInt(int* value) const{
	Assert(value != NULL);
	int scanCount = StdSscanf(string_, "%d", value);
	if(scanCount == 1){ return true; }
	return false;
}
//------------------------------------------------------------------------------
// u_intへのパース
bool String::parseUInt(u_int* value) const{
	Assert(value != NULL);
	int scanCount = StdSscanf(string_, "%u", value);
	if(scanCount == 1){ return true; }
	return false;
}
//------------------------------------------------------------------------------
// floatへのパース
bool String::parseFloat(float* value) const{
	Assert(value != NULL);
	int scanCount = StdSscanf(string_, "%f", value);
	if(scanCount == 1){ return true; }
	return false;
}
//------------------------------------------------------------------------------
// doubleへのパース
bool String::parseDouble(double* value) const{
	Assert(value != NULL);
	int scanCount = StdSscanf(string_, "%lf", value);
	if(scanCount == 1){ return true; }
	return false;
}
//------------------------------------------------------------------------------
// 文字列を変更する
//------------------------------------------------------------------------------
// 文字列の追加
String& String::append(const String& appendString){
	int appendLength = appendString.getSize();
	if(appendLength == 0){ return *this; }
	int newLength = size_ + appendLength;
	char* newArray = newString(newLength);
	if(size_ != 0){ std::memcpy(newArray, string_, size_); }
	std::memcpy(newArray + size_, appendString.getBytes(), appendLength);
	newArray[newLength] = '\0';
	releaseString(&string_);
	string_ = newArray;
	size_ = newLength;
	return *this;
}
//------------------------------------------------------------------------------
// 文字列の追加
String& String::append(const char* appendString){
	Assert(appendString != NULL);
	int appendLength = (int)StdStrlen(appendString);
	if(appendLength == 0){ return *this; }
	int newLength = size_ + appendLength;
	char* newArray = newString(newLength);
	if(size_ != 0){ std::memcpy(newArray, string_, size_); }
	std::memcpy(newArray + size_, appendString, appendLength);
	newArray[newLength] = '\0';
	releaseString(&string_);
	string_ = newArray;
	size_ = newLength;
	return *this;
}
//------------------------------------------------------------------------------
// フォーマット
String& String::format(const char* formatString, ...){
	Assert(formatString != NULL);
	releaseString(&string_);
	// フォーマット文字列が空文字列
	if(formatString[0] == '\0'){
		string_ = NULL;
		size_ = 0;
		return *this;
	}
	int allocateSize = formatDefaultLength;
	va_list args;
	va_start(args, formatString);
	while(true){
		int maxLength = allocateSize - sizeof(int) - 1;
		string_ = newString(maxLength);
		int result = StdVsnprintf(string_, maxLength, formatString, args);
		if(result != -1){
			size_ = result;
			if(size_ == 0){
				releaseString(&string_);
			}else{
				// _vsntprintfの指定長丁度だとNULL終端されないので
				string_[size_] = '\0';
			}
			break;
		}
		allocateSize *= 2;
		releaseString(&string_);
	}
	va_end(args);
	return *this;
}
//------------------------------------------------------------------------------
// 文字列を比較する
//------------------------------------------------------------------------------
// 文字列の辞書式比較
int String::compareTo(const String& compareString) const{
	return StdStrcmp(getBytes(), compareString.getBytes());
}
//------------------------------------------------------------------------------
// 文字列の辞書式比較
int String::compareTo(const char* compareString) const{
	Assert(compareString != NULL);
	return StdStrcmp(getBytes(), compareString);
}
//------------------------------------------------------------------------------
// 大文字、小文字を無視した文字列の辞書式比較
int String::compareToIgnoreCase(const String& compareString) const{
	String source = this->getLowerCase();
	String destination = compareString.getLowerCase();
	return StdStrcmp(source.getBytes(), destination.getBytes());
}
//------------------------------------------------------------------------------
// 文字列の比較
bool String::equals(const String& compareString) const{
	return (StdStrcmp(getBytes(), compareString.getBytes()) == 0);
}
//------------------------------------------------------------------------------
// 文字列の比較
bool String::equals(const char* compareString) const{
	Assert(compareString != NULL);
	return (StdStrcmp(getBytes(), compareString) == 0);
}
//------------------------------------------------------------------------------
// 大文字、小文字を無視した文字列の比較
bool String::equalsIsIgnoreCase(const String& compareString) const{
	String source = this->getLowerCase();
	String destination = compareString.getLowerCase();
	return (StdStrcmp(source.getBytes(), destination.getBytes()) == 0);
}
//------------------------------------------------------------------------------
// 指定した文字列で始まるかどうか
bool String::startsWith(const String& prefix) const{
	return (StdStrncmp(getBytes(), prefix.getBytes(), prefix.getSize()) == 0);
}
//------------------------------------------------------------------------------
// 指定した文字列で始まるかどうか
bool String::startsWith(const char* prefix) const{
	Assert(prefix != NULL);
	return (StdStrncmp(getBytes(), prefix, StdStrlen(prefix)) == 0);
}
//------------------------------------------------------------------------------
// 指定した文字列で終わるかどうか
bool String::endsWith(const String& suffix) const{
	int index = size_ - suffix.getSize();
	if(index < 0){ return false; }
	return (StdStrncmp(getBytes() + index,
		suffix.getBytes(), suffix.getSize()) == 0);
}
//------------------------------------------------------------------------------
// 指定した文字列で終わるかどうか
bool String::endsWith(const char* suffix) const{
	Assert(suffix != NULL);
	int suffixLength = (int)StdStrlen(suffix);
	int index = size_ - suffixLength;
	if(index < 0){ return false; }
	return (StdStrncmp(getBytes() + index, suffix, suffixLength) == 0);
}
//------------------------------------------------------------------------------
// 指定された文字が最初に出現する位置のインデックスを取得
int String::getIndexOf(const char searchChar) const{
	char* indexPointer = StdStrchr(getBytes(), searchChar);
	if(indexPointer == NULL){ return -1; }
	return (int)(indexPointer - getBytes());
}
//------------------------------------------------------------------------------
// 指定された文字列が最初に出現する位置のインデックスを取得
int String::getIndexOf(const char* searchString) const{
	Assert(searchString != NULL);
	char* indexPointer = StdStrstr(getBytes(), searchString);
	if(indexPointer == NULL){ return -1; }
	return (int)(indexPointer - getBytes());
}
//------------------------------------------------------------------------------
// 指定された文字列が最初に出現する位置のインデックスを取得
int String::getIndexOf(const String& searchString) const{
	char* indexPointer = StdStrstr(getBytes(), searchString.getBytes());
	if(indexPointer == NULL){ return -1; }
	return (int)(indexPointer - getBytes());
}
//------------------------------------------------------------------------------
// 指定された文字が最後に出現する位置のインデックスを取得
int String::getLastIndexOf(const char searchChar) const{
	char* indexPointer = StdStrrchr(getBytes(), searchChar);
	if(indexPointer == NULL){ return -1; }
	return (int)(indexPointer - getBytes());
}
//------------------------------------------------------------------------------
// 指定された文字列が最後に出現する位置のインデックスを取得
int String::getLastIndexOf(const char* searchString) const{
	Assert(searchString != NULL);
	if(StdStrlen(searchString) == 0){ return size_; }
	const char* indexPointer = NULL;
	const char* target = getBytes();
	while(true){
		target = StdStrstr(target, searchString);
		if(target == NULL){ break; }
		indexPointer = target;
		target++;
	}
	if(indexPointer == NULL){ return -1; }
	return (int)(indexPointer - getBytes());
}
//------------------------------------------------------------------------------
// 指定された文字列が最後に出現する位置のインデックスを取得
int String::getLastIndexOf(const String& searchString) const{
	if(searchString.getSize() == 0){ return size_; }
	const char* search = searchString.getBytes();
	const char* indexPointer = NULL;
	const char* target = getBytes();
	while(true){
		target = StdStrstr(target, search);
		if(target == NULL){ break; }
		indexPointer = target;
		target++;
	}
	if(indexPointer == NULL){ return -1; }
	return (int)(indexPointer - getBytes());
}
//------------------------------------------------------------------------------
#ifdef _DEBUG
// デバッグ用文字列情報出力
void String::debugPrint(){
	DebugOut("[%2d] %s\n", getReferenceCounter(string_), string_);
}
#endif// End of _DEBUG
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
// 文字列の連結
const Lamp::String operator+(
	const Lamp::String& leftString, const Lamp::String& rightString){
	Lamp::String returnString(leftString);
	returnString.append(rightString);
	return returnString;
}
//------------------------------------------------------------------------------
