#include "StdAfx.h"
#include "UTF8StringTool.h"

////////////////////////////////////////////////////////////////////////////////
//	補助
////////////////////////////////////////////////////////////////////////////////
/*!
	空文字列を返す
*/
LPCU8STR CUTF8StringTool::GetEmptyString()
{
	static utf8_char empty[1] = { 0 };

	return(empty);
}

/*!
	空文字列を返す
*/
LPCSTR CUTF8StringTool::GetEmptyStringMB()
{
	return((LPCSTR)GetEmptyString());
}

/*!
	空文字列を返す
*/
LPCWSTR CUTF8StringTool::GetEmptyStringU16()
{
	static wchar_t empty[1] = { 0 };

	return(empty);
}


////////////////////////////////////////////////////////////////////////////////
//	変換処理
////////////////////////////////////////////////////////////////////////////////
/*!
	UTF8安全性チェック

	\return TRUIE:異常が見つかった
*/
int CUTF8StringTool::CheckIllegalUTF8(LPCU8STR utf8, int length)
{
	//	NULL?
	if(utf8 == NULL)
		return(FALSE);

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

	//	バイトオーダーマークがあるか？
	if(length >= 3 && utf8[0] == 0xEF && utf8[1] == 0xBB && utf8[2] == 0xBF)
	{
		TRACE_U8(_U16("CheckIllegalUTF8: バイトオーダーマークを検出しました\n"));
		return(TRUE);
	}

	//	UTF8の書式をチェック
	if(!CheckIsUTF8((LPCSTR)utf8, length))
	{
		TRACE_U8(_U16("CheckIllegalUTF8: UTF8のシーケンスに従っていません\n"));
		return(TRUE);
	}

	const utf8_char *ptr = utf8;
	for(int i=0;i<length;i++)
	{
		//	異常なシーケンスの検出
		if(*ptr == 0xC0 || *ptr == 0xE0 || *ptr == 0xF0 || *ptr == 0xF8 || *ptr == 0xFC)
		{
			TRACE_U8(_U16("CheckIllegalUTF8: 異常なUTF8文字列を検出しました\n"));
			return(TRUE);
		}
		ptr++;
	}
	return(FALSE);
}

/*!
	全てアスキーコードかチェック
*/
int CUTF8StringTool::CheckAllAscii(LPCSTR mbStr, int mbLength)
{
	//	NULL?
	if(mbStr == NULL)
		return(TRUE);

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

	const unsigned char *ptr = (const unsigned char*)mbStr;
	for(int i=0;i<mbLength;i++)
	{
		//	0x80検出
		if(*ptr & 0x80)
		{
			TRACE_U8(_U16("CheckAllAscii: ASCII以外の文字を検出しました\n"));
			return(FALSE);
		}
		ptr++;
	}
	return(TRUE);
}


/*!
	UTF8文字列である可能性が高いかチェック
	UTF8のシーケンスに違反していないか確認する
*/
int CUTF8StringTool::CheckIsUTF8(LPCSTR mbStr, int mbLength)
{
	//	NULL?
	if(mbStr == NULL)
		return(TRUE);

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

	const unsigned char *ptr = (const unsigned char*)mbStr;
	while(*ptr != 0 && ptr - (const unsigned char*)mbStr < mbLength)
	{
		//	0x80検出
		if(*ptr & 0x80)
		{
			//	1100 0000
			if((*ptr & 0xe0) == 0xC0)
			{
				//	長さ不正
				if(ptr[1] == 0)
					return(FALSE);
				
				//	シーケンス内の確認
				if((ptr[1] & 0xC0) != 0x80)
					return(FALSE);
				ptr += 1;
			}
			else if((*ptr & 0xf0) == 0xe0)
			{
				//	長さ不正
				if(ptr[1] == 0 || ptr[2] == 0)
					return(FALSE);
				
				//	シーケンス内の確認
				if((ptr[1] & 0xC0) != 0x80 || (ptr[2] & 0xC0) != 0x80)
					return(FALSE);
				ptr += 2;
			}
			else if((*ptr & 0xf8) == 0xf0)
			{
				//	長さ不正
				if(ptr[1] == 0 || ptr[2] == 0 || ptr[3] == 0)
					return(FALSE);
				
				//	シーケンス内の確認
				if((ptr[1] & 0xC0) != 0x80 || (ptr[2] & 0xC0) != 0x80 || (ptr[3] & 0xC0) != 0x80)
					return(FALSE);
				ptr += 3;
			}
			else if((*ptr & 0xfc) == 0xf8)
			{
				//	長さ不正
				if(ptr[1] == 0 || ptr[2] == 0 || ptr[3] == 0 || ptr[4] == 0)
					return(FALSE);
				
				//	シーケンス内の確認
				if((ptr[1] & 0xC0) != 0x80 || (ptr[2] & 0xC0) != 0x80 || (ptr[3] & 0xC0) != 0x80 || (ptr[4] & 0xC0) != 0x80)
					return(FALSE);
				ptr += 4;
			}
			else if((*ptr & 0xfe) == 0xfc)
			{
				//	長さ不正
				if(ptr[1] == 0 || ptr[2] == 0 || ptr[3] == 0 || ptr[4] == 0 || ptr[5] == 0)
					return(FALSE);
				
				//	シーケンス内の確認
				if((ptr[1] & 0xC0) != 0x80 || (ptr[2] & 0xC0) != 0x80 || (ptr[3] & 0xC0) != 0x80 || (ptr[4] & 0xC0) != 0x80 || (ptr[5] & 0xC0) != 0x80)
					return(FALSE);
				ptr += 5;
			}
			else
				return(FALSE);
		}
		ptr++;
	}
	return(TRUE);
}



/*!
	UTF8文字列を取得
*/
LPCU8STR CUTF8StringTool::MBCSToUTF8(LPCSTR mbStr, int mbLength, CUTF8StringTempBuf &tempBuf)
{
	//	作業バッファ確保
	CUTF8StringTempBuf	utf16Buf;

	//	MBCSからUTF16へ変換
	LPCWSTR				utf16 = AnyToUTF16(mbStr, mbLength, utf16Buf, CP_ACP);

	//	UTF16からUTF8へ
	return(UTF16ToUTF8(utf16, -1, tempBuf));
}


/*!
	UTF8文字列を取得
*/
LPCU8STR CUTF8StringTool::UTF16ToUTF8(LPCWSTR u16Str, int u16Length, CUTF8StringTempBuf &tempBuf)
{
	//!	変換
	return((LPCU8STR)UTF16ToAny(u16Str, u16Length, tempBuf, CP_UTF8));
}


/*!
	MBCS文字列を取得
*/
LPCSTR CUTF8StringTool::UTF8ToMBCS(LPCU8STR u8Str, int u8Length, CUTF8StringTempBuf &tempBuf)
{
	//	作業バッファ確保
	CUTF8StringTempBuf	utf16Buf;

	//	UTF8からUTF16へ変換
	LPCWSTR				utf16 = AnyToUTF16((LPCSTR)u8Str, u8Length, utf16Buf, CP_UTF8);

	//	UTF16からMBCSへ
	return(UTF16ToAny(utf16, -1, tempBuf, CP_ACP));
}

/*!
	UTF16文字列を取得
*/
LPCWSTR CUTF8StringTool::UTF8ToUTF16(LPCU8STR u8Str, int u8Length, CUTF8StringTempBuf &tempBuf)
{
	//!	変換
	return(AnyToUTF16((LPCSTR)u8Str, u8Length, tempBuf, CP_UTF8));
}


////////////////////////////////////////////////////////////////////////////////
//	変換補助
////////////////////////////////////////////////////////////////////////////////
/*!
	UTF16文字列を取得
*/
LPCWSTR CUTF8StringTool::AnyToUTF16(LPCSTR mbStr, int mbLength, CUTF8StringTempBuf &tempBuf, int charSet)
{
	//	サイズ調査
	if(mbLength < 0)
		mbLength = strlen((char *)mbStr);

	//	変換サイズを取得
	int u16Length = ::MultiByteToWideChar(charSet, 0, mbStr, mbLength, NULL, 0);
	if(u16Length == 0)
		return(GetEmptyStringU16());

	//	作業バッファ確保
	wchar_t				*utf16 = tempBuf.AllocUTF16(u16Length + 1);

	//	変換
	if(::MultiByteToWideChar(charSet, 0, mbStr, mbLength, utf16, u16Length) == 0)
		return(GetEmptyStringU16());

	//	C文字列へ
	utf16[u16Length] = 0;
	return(utf16);
}


/*!
	UTF8/MBCS文字列を取得
*/
LPCSTR CUTF8StringTool::UTF16ToAny(LPCWSTR u16Str, int u16Length, CUTF8StringTempBuf &tempBuf, int charSet)
{
	//	サイズ調査
	if(u16Length < 0)
		u16Length = wcslen(u16Str);

	//	変換サイズを取得
	int mbLength = ::WideCharToMultiByte(charSet, 0, u16Str, u16Length, NULL, 0, NULL, NULL);
	if(mbLength == 0)
		return(GetEmptyStringMB());

	//	バッファ確保
	LPSTR mbcs = tempBuf.AllocChar(mbLength + 1);

	//	変換
	if(::WideCharToMultiByte(charSet, 0, u16Str, u16Length, mbcs, mbLength, NULL, NULL) == 0)
		return(GetEmptyStringMB());

	//	C文字列へ
	mbcs[mbLength] = 0;
	return(mbcs);
}

////////////////////////////////////////////////////////////////////////////////
//	デバッグ補助
////////////////////////////////////////////////////////////////////////////////
/*!
	全てアスキーコードかチェックし、マルチバイトがある場合はASSSERT

	リテラル文字列のチェックに使う
*/
LPU8STR CUTF8StringTool::CheckDirectConvertUTF8(LPCSTR mbStr)
{
	//	マルチバイトを検出した
	ASSERT(CheckAllAscii(mbStr, -1));

	return((LPU8STR)mbStr);
}
