#include "StdAfx.h"
#include "UTF8String.h"
#include "UTF8StringTool.h"
#include "UTF8StringConvert.h"
#include "UTF8StringCore.h"
#include "UTF8StringMemoryPool.h"


////////////////////////////////////////////////////////////////////////////////
//	コンストラクタ
////////////////////////////////////////////////////////////////////////////////
/*!
	コンストラクタ
*/
CUTF8String::CUTF8String(void)
{
	InitInternal();
}

/*!
	コンストラクタ(CUTF8String)
*/
CUTF8String::CUTF8String(const CUTF8String &other)
{
	InitInternal();

	//	コピー
	*this = other;
}

/*!
	コンストラクタ(LPCU8STR)
*/
CUTF8String::CUTF8String(LPCU8STR other)
{
	InitInternal();

	//	コピー
	*this = other;
}

/*!
	コンストラクタ(LPCWSTR)
*/
CUTF8String::CUTF8String(LPCWSTR other)
{
	InitInternal();

	//	コピー
	*this = other;
}

/*!
	デストラクタ
*/
CUTF8String::~CUTF8String(void)
{
	FreeString();
}



////////////////////////////////////////////////////////////////////////////////
//	その他の操作
////////////////////////////////////////////////////////////////////////////////
/*!
	からっぽにする
*/
void CUTF8String::Empty()
{
	if(GetLength() > 0)
		GetWritableString(0)[0] = 0;
}

////////////////////////////////////////////////////////////////////////////////
//	複製
////////////////////////////////////////////////////////////////////////////////
/*!
	コピーする(UFT8)
*/
void CUTF8String::Copy(LPCU8STR copyFrom, int u8Length, int safetyCheck)
{
	//	NULL?
	if(copyFrom == NULL)
		u8Length = 0;

	//	サイズ調査
	if(u8Length < 0)
		u8Length = strlen((char *)copyFrom);

	//	からっぽ？
	if(u8Length <= 0)
	{
		Empty();
	}
	else
	{
		//	安全性チェック
		if(safetyCheck && CUTF8StringTool::CheckIllegalUTF8(copyFrom, u8Length))
		{
			//	引っかかったので、一旦UTF16に変換して格納
			CopyWithUTF16((LPCSTR)copyFrom, u8Length, CP_UTF8);
		}
		else
		{
			//	バッファ確保
			char *ptr = (char *)GetWritableString(u8Length);

			//	コピー
			strncpy_s(ptr, u8Length + 1, (char *)copyFrom, u8Length);

			//	念のため長さを再設定
			GetWritableString(strlen(ptr));
		}
	}
}


/*!
	コピーする(UFT16)
*/
void CUTF8String::CopyUTF16(LPCWSTR copyFrom, int u16Length)
{
	//	NULL?
	if(copyFrom == NULL)
		u16Length = 0;

	//	サイズ調査
	if(u16Length < 0)
		u16Length = wcslen(copyFrom);

	//	からっぽ？
	if(u16Length <= 0)
	{
		Empty();
	}
	else
	{
		//	変換サイズを取得
		int u8Length = ::WideCharToMultiByte(CP_UTF8, 0, copyFrom, u16Length, NULL, 0, NULL, NULL);
		if(u8Length == 0)
		{
			//	エラーっぽいのであきらめる
			Empty();
			return;
		}

		//	バッファ確保
		char *ptr = (char *)GetWritableString(u8Length);

		//	変換
		if(::WideCharToMultiByte(CP_UTF8, 0, copyFrom, u16Length, ptr, u8Length, NULL, NULL) == 0)
		{
			//	エラーっぽいのであきらめる
			Empty();
			return;
		}

		//	C文字列へ
		ptr[u8Length] = 0;
	}
}

/*!
	コピーする(MBCS)
*/
void CUTF8String::CopyMBCS(LPCSTR copyFrom, int mbLength)
{
	//	NULL?
	if(copyFrom == NULL)
		mbLength = 0;

	//	サイズ調査
	if(mbLength < 0)
		mbLength = _mbstrlen(copyFrom);

	//	からっぽ？
	if(mbLength <= 0)
	{
		Empty();
	}
	else
	{
		//	全部ASCIIなら無視
		if(CUTF8StringTool::CheckAllAscii(copyFrom, mbLength))
			Copy((LPCU8STR)copyFrom, mbLength, FALSE);
		else
			CopyWithUTF16(copyFrom, mbLength, CP_ACP);
	}
}

/*
	CUTF8Stringを複製
*/
CUTF8String& CUTF8String::operator=(const CUTF8String &other)
{
	//	削除
	FreeString();

	//	複製
	if(other.GetLength() > 0)
	{
		//	ポインタをコピーし、インクリメント
		m_u8string = other.m_u8string;
		GetStringClass()->IncementRef();
	}

	return(*this);
}

/*
	LPCU8STRをコピー
*/
CUTF8String& CUTF8String::operator=(LPCU8STR other)
{
	Copy(other, -1, TRUE);
	return(*this);
}


/*
	LPCWSTRをコピー
*/
CUTF8String& CUTF8String::operator=(LPCWSTR other)
{
	CopyUTF16(other, -1);
	return(*this);
}


/*!
	文字列の設定
*/
void CUTF8String::SetString(LPCU8STR toSet, int length)
{
	Copy(toSet, length, TRUE);
}


//////////////////////////////////////////////////////////////////////////////////////////
//	取得
//////////////////////////////////////////////////////////////////////////////////////////
/*!
	文字列の取得
*/
LPCU8STR CUTF8String::GetString() const
{
	if(GetStringClass() == NULL)
		return(CUTF8StringTool::GetEmptyString());
	return(GetStringClass()->GetString());
}

/*!
	長さ取得
*/
int CUTF8String::GetLength() const
{
	if(GetStringClass() == NULL)
		return(0);
	return(GetStringClass()->GetLength());
}

/*!
	LPCU8STR
*/
CUTF8String::operator LPCU8STR() const
{
	return GetString();
}

/*!
	[]
*/
utf8_char CUTF8String::operator[](int index) const
{
	ASSERT(index >=0 && index < GetLength());

	return(GetString()[index]);
}

//////////////////////////////////////////////////////////////////////////////////////////
//	比較
//////////////////////////////////////////////////////////////////////////////////////////
/*!
	LPCU8STR
*/
int CUTF8String::operator==(LPCU8STR other)
{
	return(Compare(other) == 0);
}


/*!
	LPCWSTR
*/
int CUTF8String::operator==(LPCWSTR other)
{
	return(*this == _cnvU8(other));
}

/*!
	普通に比較
*/
int CUTF8String::Compare(LPCU8STR other)
{
	return(strcmp((char *)GetString(), (char *)other));
}


/*!
	普通に比較
*/
int CUTF8String::Compare(LPCWSTR other)
{
	return Compare(_cnvU8(other));
}


/*!
	大文字小文字無視で比較
*/
int CUTF8String::CompareNoCase(LPCU8STR other)
{
	return(_strcmpi((char *)GetString(), (char *)other));
}


/*!
	大文字小文字無視で比較
*/
int CUTF8String::CompareNoCase(LPCWSTR other)
{
	return CompareNoCase(_cnvU8(other));
}


//////////////////////////////////////////////////////////////////////////////////////////
//	追加
//////////////////////////////////////////////////////////////////////////////////////////
/*!
	LPCU8STR
*/
void CUTF8String::operator+=(LPCU8STR other)
{
	Append(other);
}


/*!
	LPCWSTR
*/
void CUTF8String::operator+=(LPCWSTR other)
{
	Append(other);
}

/*!
	utf8_char
*/
void CUTF8String::operator+=(const utf8_char other)
{
	utf8_char	str[2] = { other, 0 };
	Append(str);
}


/*!
	追加
*/
void CUTF8String::Append(LPCU8STR other)
{
	//	複製する（自己参照回避、増大で元が削除されるのを防ぐ）
	CUTF8String dest(*this);

	//	情報取得
	int appendLength = strlen((char*)other);
	int	curLength = dest.GetLength();

	//	メモリ再確保
	utf8_char *str = dest.GetWritableString(curLength + appendLength);

	//	コピー
	memmove(str + curLength, other, appendLength);

	//	C文字列にしておく
	str[curLength + appendLength] = 0;

	//	戻す
	*this = dest;
}


/*!
	追加
*/
void CUTF8String::Append(LPCSTR other)
{
	Append(_cnvU8(other));
}

/*!
	追加
*/
void CUTF8String::Append(LPCWSTR other)
{
	Append(_cnvU8(other));
}



//////////////////////////////////////////////////////////////////////////////////////////
//	削除
//////////////////////////////////////////////////////////////////////////////////////////
/*!
	削除
*/
CUTF8String &CUTF8String::Delete(int index, int count)
{
	int curLength = GetLength();

	//	位置、サイズ確認
	if(index < 0)
		index = 0;
	if(index >= curLength)
		return *this;
	if(index + count > curLength)
		count = curLength - index;

	//	サイズ計算
	int newLength = curLength - count;
	int	moveLength = curLength - (index + count);

	//	ポインタ取得
	utf8_char *str = GetWritableString(newLength);

	//	データ移動
	if(moveLength > 0)
		memmove(str + index, str + index + count, moveLength);

	//	C文字列へ
	str[newLength] = 0;
	
	return *this;
}

/*!
	右を削除
*/
CUTF8String &CUTF8String::TrimRight()
{
	//	長さ、アクセス用ポインタ
	int curLength = GetLength();
	LPCU8STR str = GetString();

	//	空白位置検索
	for(int i=curLength-1; i>=0; i--)
	{
		if(CUTF8StringTool::IsASCII(str[i]) && !isspace(str[i]))
		{
			//	削除実行
			if(i != curLength - 1)
				Delete(i, curLength - i);
			break;
		}
	}

	return *this;
}

/*!
	左を削除
*/
CUTF8String &CUTF8String::TrimLeft()
{
	//	長さ、アクセス用ポインタ
	int curLength = GetLength();
	LPCU8STR str = GetString();

	//	空白位置検索
	for(int i=0; i<curLength; i++)
	{
		if(CUTF8StringTool::IsASCII(str[i]) && !isspace(str[i]))
		{
			//	削除実行
			if(i != 0)
				Delete(0, i);
			break;
		}
	}

	return *this;
}

/*!
	空白を削除
*/
CUTF8String &CUTF8String::Trim()
{
	return(TrimLeft().TrimRight());
}


//////////////////////////////////////////////////////////////////////////////////////////
//	Left, Right
//////////////////////////////////////////////////////////////////////////////////////////
//!	右を削除
CUTF8String CUTF8String::Right(int count)
{
	if(count > GetLength())
		count = GetLength();
	if(count < 0)
		return(CUTF8StringTool::GetEmptyString());

	return Mid(GetLength() - count, count);
}

//!	左を削除
CUTF8String CUTF8String::Left(int count)
{
	if(count > GetLength())
		count = GetLength();
	if(count < 0)
		return(CUTF8StringTool::GetEmptyString());

	return Mid(0, count);
}

/*!
	指定位置を取得
*/
CUTF8String CUTF8String::Mid(int index, int count)
{
	//	長さ取得
	int	curLength = GetLength();

	//	確認
	if(index < 0)
		index = 0;

	//	結果は空
	if(count == 0 || index >= curLength)
		return(CUTF8StringTool::GetEmptyString());

	//	カウント取得
	if(count < 0 || index + count > curLength)
		count = curLength - index;

	//	文字列ポインタ取得
	CUTF8String	ret;
	LPSTR dest = (LPSTR)ret.GetWritableString(count);
	LPSTR src = (LPSTR)GetString();

	//	コピー
	memmove(dest, src + index, count);
	dest[count] = 0;

	return(ret);
}


//////////////////////////////////////////////////////////////////////////////////////////
//	検索・置き換え
//////////////////////////////////////////////////////////////////////////////////////////
/*!
	検索
*/
int CUTF8String::Find(LPCU8STR find, int start)
{
	//	位置チェック
	if(start < 0 || start >= GetLength())
		return(-1);

	//	アクセス用ポインタ
	LPCSTR str = (LPCSTR)GetString();

	//	検索
	LPCSTR ptr = strstr(str + start, (LPCSTR)find);
	if(ptr == NULL)
		return(-1);

	return(ptr - str);
}


/*!
	検索
*/
int CUTF8String::Find(LPCWSTR find, int start)
{
	return Find(_cnvU8(find), start);
}


/*!
	反対から探す
*/
int CUTF8String::ReverseFind(utf8_char ch)
{
	//	アクセス用ポインタ
	LPCSTR str = (LPCSTR)GetString();

	//	検索
	LPCSTR ptr = strrchr(str, ch);
	if(ptr == NULL)
		return(-1);

	return(ptr - str);
}

/*!
	置き換え
*/
int CUTF8String::Replace(LPCU8STR oldTok, LPCU8STR newTok)
{
	//	サイズ
	int	curLen = GetLength();
	int oldTokLen = strlen((char *)oldTok);
	int newTokLen = strlen((char *)newTok);

	//	まず一致数取得
	int	index = 0, count = 0;
	while(1)
	{
		//	検索
		index = Find(oldTok, index);
		if(index == -1)
			break;

		//	次へ
		index += oldTokLen;
		count++;
	}


	//	新サイズ
	int newLength = curLen + (newTokLen - oldTokLen) * count;
	ASSERT(newLength >= 0);

	//	複製する
	CUTF8String	src(*this);

	//	ポインタ取得
	LPSTR destPtr = (LPSTR)GetWritableString(newLength);
	LPCSTR srcPtr = (LPCSTR)src.GetString();

	//	書き換える
	int	destBufLet = newLength;
	while(1)
	{
		//	検索
		LPCSTR find = strstr(srcPtr, (LPCSTR)oldTok);
		if(find == NULL)
		{
			//	長さ異常
			ASSERT(strlen(srcPtr) == destBufLet);

			//	残りをまとめてコピー
			strcpy_s(destPtr, destBufLet + 1, srcPtr);
			break;
		}
		
		//	移動量
		int moved = find - srcPtr;

		//	非一致をコピー
		memmove(destPtr, srcPtr, moved);
		destPtr += moved;

		//	トークンコピー
		memmove(destPtr, newTok, newTokLen);
		destPtr += newTokLen;

		//	ポインタ移動
		srcPtr = find + oldTokLen;

		//	バッファ管理
		destBufLet -= (moved + newTokLen);
		ASSERT(destBufLet >= 0);
	}

	return(count);
}


/*!
	置き換え
*/
int CUTF8String::Replace(LPCWSTR oldTok, LPCWSTR newTok)
{
	return Replace(_cnvU8(oldTok), _cnvU8(newTok));
}

//////////////////////////////////////////////////////////////////////////////////////////
//	文字種変換
//////////////////////////////////////////////////////////////////////////////////////////
/*!
	中身を小文字へ変換
*/
CUTF8String &CUTF8String::MakeLower()
{
	//	長さ、アクセス用ポインタ
	int curLength = GetLength();
	LPU8STR str = GetWritableString(curLength);

	//	大文字検索
	for(int i=0; i<curLength; i++)
	{
		if(CUTF8StringTool::IsASCII(str[i]) && isupper(str[i]))
			str[i] = str[i] - (unsigned)'A' + (unsigned)'a';
	}

	return(*this);
}

/*!
	中身を大文字へ変換
*/
CUTF8String &CUTF8String::MakeUpper()
{
	//	長さ、アクセス用ポインタ
	int curLength = GetLength();
	LPU8STR str = GetWritableString(curLength);

	//	大文字検索
	for(int i=0; i<curLength; i++)
	{
		if(CUTF8StringTool::IsASCII(str[i]) && islower(str[i]))
			str[i] = str[i] - (unsigned)'a' + (unsigned)'A';
	}

	return(*this);
}


//////////////////////////////////////////////////////////////////////////////////////////
//	Format
//////////////////////////////////////////////////////////////////////////////////////////
/*!
	Format
*/
CUTF8String &CUTF8String::Format(LPCU8STR format, ...)
{
	va_list argList;
	va_start(argList, format);
	FormatV(format, argList);
	va_end(argList);

	return *this;
}


/*!
	Format

	※引数の文字列はUTF8へ自動変換されない
*/
CUTF8String &CUTF8String::Format(LPCWSTR format, ...)
{
	va_list argList;
	va_start(argList, format);
	FormatV(_cnvU8(format), argList);
	va_end(argList);

	return *this;
}

/*!
	Format
*/
CUTF8String &CUTF8String::FormatV(LPCU8STR format, va_list argptr)
{
	//	長さ取得
	int len = _vscprintf((LPSTR)format, argptr );

	//	書き込みバッファ取得
	LPSTR str = (LPSTR)GetWritableString(len);

	//	書き込み
	vsprintf_s(str, len + 1, (LPSTR)format, argptr);

	return *this;
}


/*!
	Format

	※引数の文字列はUTF8へ自動変換されない
*/
CUTF8String &CUTF8String::FormatV(LPCWSTR format, va_list argptr)
{
	return FormatV(_cnvU8(format), argptr);
}


/*!
	Format(直接取得)
*/
CUTF8String CUTF8String::GetFormat(LPCU8STR format, ...)
{
	CUTF8String	ret;

	va_list argList;
	va_start(argList, format);
	ret.FormatV(format, argList);
	va_end(argList);

	return ret;
}

/*!
	Format(直接取得)
*/
CUTF8String CUTF8String::GetFormat(LPCWSTR format, ...)
{
	CUTF8String	ret;

	va_list argList;
	va_start(argList, format);
	ret.FormatV(format, argList);
	va_end(argList);

	return ret;
}


////////////////////////////////////////////////////////////////////////////////
//	バッファ操作
////////////////////////////////////////////////////////////////////////////////
//!	クラスを取得
__inline CUTF8StringData *CUTF8String::GetStringClass() const
{
	if(m_u8string == NULL)
		return(NULL);

	LPU8STR	strPtr = (LPU8STR)m_u8string;
	return (CUTF8StringData*)(strPtr - sizeof(CUTF8StringData));
}

/*!
	書き換え
*/
void CUTF8String::SetStringClass(CUTF8StringData *strClass)
{
	FreeString();

	LPU8STR	strPtr = (LPU8STR)strClass;
	m_u8string = strPtr + sizeof(CUTF8StringData);
}


////////////////////////////////////////////////////////////////////////////////
//	補助
////////////////////////////////////////////////////////////////////////////////
/*!
	内部初期化
*/
void CUTF8String::InitInternal()
{
	//	このコンパイラでは、使用不可！！
	ASSERT(sizeof(LPU8STR) == sizeof(CUTF8String));

	//	初期化
	m_u8string = NULL;
}

/*!
	必要なら解放
*/
void CUTF8String::FreeString()
{
	//	参照カウンタを減らす
	if(GetStringClass() != NULL)
		GetStringClass()->DecrementRef();
	m_u8string = NULL;
}

/*!
	書き込み用文字列を取得する
*/
LPU8STR CUTF8String::GetWritableString(int length)
{
	//	存在しない
	if(GetStringClass() == NULL)
	{
		//	新規
		SetStringClass(CUTF8StringData::GetNewString(length));
	}
	else if(!GetStringClass()->CanWrite() || GetStringClass()->SetLength(length))	//	書き込み不能 or 長さが足りない
	{
		//	複製
		CUTF8StringData *newString = CUTF8StringData::GetNewString(length, GetStringClass(), (length > GetLength()));

		//	設定する
		SetStringClass(newString);
	}

	//	書き込み用バッファ取得
	return(GetStringClass()->GetWritableString());
}


/*!
	一旦UTF16にして再設定
*/
void CUTF8String::CopyWithUTF16(LPCSTR mbStr, int mbLength, int charSet)
{
	//	作業バッファ確保
	CUTF8StringTempBuf	utf16Buf;

	//	変換
	LPCWSTR				utf16 = CUTF8StringTool::AnyToUTF16(mbStr, mbLength, utf16Buf, charSet);

	//	UTF16として設定
	CopyUTF16(utf16, -1);
}


//////////////////////////////////////////////////////////////////////////////////////////
//	メモリマネージャ制御
//////////////////////////////////////////////////////////////////////////////////////////
/*!
	グローバルメモリプールを有効にする(いつでも可能、無効にはできない)
*/
void CUTF8String::EnableMemoryPool()
{
	g_utf8StringMemoryPool.EnableMemoryPool();
}

/*!
	現在の使用文字列数取得(プール以外も含む)
*/
int CUTF8String::GetGlobalStringCount()
{
	return g_utf8StringMemoryPool.GetAllocedCount();
}

/*!
	現在のメモリプールサイズを取得
*/
int CUTF8String::GetMemoryPoolSize()
{
	return g_utf8StringMemoryPool.GetPoolSize();
}


//////////////////////////////////////////////////////////////////////////////////////////
//	足し算（グローバル演算子とする）
//////////////////////////////////////////////////////////////////////////////////////////
/*!
	CUTF8String
*/
CUTF8String operator+(const CUTF8String &arg1, const CUTF8String &arg2)
{
	CUTF8String	ret(arg1);
	ret.Append(arg2);

	return(ret);
}

/*!
	LPCU8STR
*/
CUTF8String operator+(const CUTF8String arg1, const LPCU8STR arg2)
{
	CUTF8String	ret(arg1);
	ret.Append(arg2);

	return(ret);
}


/*!
	LPCWSTR
*/
CUTF8String operator+(const CUTF8String arg1, const LPCWSTR arg2)
{
	CUTF8String	ret(arg1);
	ret.Append(arg2);

	return(ret);
}


/*!
	LPCU8STR
*/
CUTF8String operator+(const LPCU8STR arg1, const CUTF8String arg2)
{
	CUTF8String	ret(arg1);
	ret.Append(arg2);

	return(ret);
}


/*!
	LPCWSTR
*/
CUTF8String operator+(const LPCWSTR arg1, const CUTF8String arg2)
{
	CUTF8String	ret(arg1);
	ret.Append(arg2);

	return(ret);
}

