#pragma once

//! @file Mix/String.h
//! @brief 文字列クラスインクルードファイル

#include <vector>

namespace Mix
{
	//! @class StringW
	//! @brief 文字列クラス( ユニコード )
	class _MIX_DLL_API StringW
	{
	private:
		enum { VA_TEMP_NUM = 2048 };
		static wchar_t empty;

	private:
		wchar_t*	m_pStr;			//文字列
		UIntT		m_Num;			//文字数
		UIntT		m_Size;			//NULLを含む文字列のサイズ(バイト単位)
		UIntT		m_AllocateSize;	//NULLを含まない文字列のサイズ(バイト単位)
		UIntT		m_Capacity;		//キャパシティ(バイト単位)

	private:
		void Allocate( UIntT num );
		void Copy( const wchar_t* pStr );
		void Copy( const wchar_t* pStr, UIntT num );
		void Convert( const char* pStr );
		void Add( const wchar_t* pStr );

	public:
		//! @brief コンストラクタ
		StringW( void );
		//! @brief コピーコンストラクタ
		//! @param[in] pStr マルチバイト文字列へのポインタ
		StringW( const char* pStr );
		//! @brief コピーコンストラクタ
		//! @param[in] pStr ユニコード文字列へのポインタ
		StringW( const wchar_t* pStr );
		//! @brief コピーコンストラクタ
		//! @param[in] pStr ユニコード文字列へのポインタ
		//! @param[in] sizeInWords 文字数
		StringW( const wchar_t* pStr, UIntT sizeInWords );
		//! @brief コピーコンストラクタ
		//! @param[in] str 文字列
		StringW( const StringW& str );
		//! @brief デストラクタ
		~StringW( void );

		//! @brief 要素を取得します
		//! @param[in] index 取得する要素のインデックス
		//! @return 要素
		wchar_t& operator [] ( UIntT index );
		//! @brief 比較( == )をします
		//! @param[in] str 文字列
		//! @return 等しかった場合は True を返します
		Boolean operator == ( const StringW& str ) const;
		//! @brief 比較( == )をします
		//! @param[in] pStr 文字列へのポインタ
		//! @return 等しかった場合は True を返します
		Boolean operator == ( const wchar_t* pStr ) const;
		//! @brief 比較( == )をします
		//! @param[in] code 文字コード
		//! @return 等しかった場合は True を返します
		Boolean operator == ( wchar_t code ) const;
		//! @brief 比較( == )をします
		//! @param[in] nullValue NULLのみ可能
		//! @return 等しかった場合は True を返します
		Boolean operator == ( UIntT nullValue ) const;
		//! @brief 比較( != )をします
		//! @param[in] str 文字列
		//! @return 等しくなかった場合は True を返します
		Boolean operator != ( const StringW& str ) const;
		//! @brief 比較( != )をします
		//! @param[in] pStr 文字列へのポインタ
		//! @return 等しくなかった場合は True を返します
		Boolean operator != ( const wchar_t* pStr ) const;
		//! @brief 比較( != )をします
		//! @param[in] nullValue NULLのみ指定可能
		//! @return 等しくなかった場合は True を返します
		Boolean operator != ( UIntT nullValue ) const;
		//! @brief 代入( = )をします
		//! @param[in] str 文字列
		//! @return 代入後の文字列
		StringW& operator = ( const StringW& str );
		//! @brief 代入( = )をします
		//! @param[in] pStr 文字列へのポインタ
		//! @return 代入後の文字列
		StringW& operator = ( const wchar_t* pStr );
		//! @brief 代入( = )をします
		//! @param[in] pStr マルチバイト文字列へのポインタ
		//! @return 代入後の文字列
		StringW& operator = ( const char* pStr );
		//! @brief 代入( = )をします
		//! @param[in] code コード
		//! @return 代入後の文字列
		StringW& operator = ( const wchar_t code );
		//! @brief クリアします
		//! @param[in] nullValue NULL のみ指定可能
		//! @return 文字列
		StringW& operator = ( UIntT nullValue );
		//! @brief 追加( += )をします
		//! @param[in] pStr 文字列へのポインタ
		//! @return 追加後の文字列
		StringW& operator += ( const wchar_t* pStr );
		//! @brief 追加( += )をします
		//! @param[in] str 文字列
		//! @return 追加後の文字列
		StringW& operator += ( const StringW& str );
		//! @brief 追加( += )をします
		//! @param[in] code 文字コード
		//! @return 追加後の文字列
		StringW& operator += ( const wchar_t code );
		//! @brief 結合( + )します
		//! @param[in] str 文字列
		//! @return 結合後の文字列
		StringW operator + ( const StringW& str );
		//! @brief 結合( + )します
		//! @param[in] ls 結合先文字列
		//! @param[in] rs 結合元文字列
		//! @return 結合後の文字列
		friend const StringW operator + ( const wchar_t* ls, const StringW& rs )
		{
			StringW tmp( ls );
			tmp.Add( rs.m_pStr );

			return tmp;
		}

		//! @brief 比較( l < r )をします
		//! @param[in] l 比較する文字列1
		//! @param[in] r 比較する文字列2
		//! @return l < r だった場合は true
		friend bool operator < ( const StringW& l, const StringW& r )
		{
			return ( ::wcscmp( l.m_pStr, r.m_pStr ) < 0 );
		}
		//! @brief 比較( l > r )をします
		//! @param[in] l 比較する文字列1
		//! @param[in] r 比較する文字列2
		//! @return l > r だった場合は true
		friend bool operator > ( const StringW& l, const StringW& r )
		{
			return ( ::wcscmp( l.m_pStr, r.m_pStr ) > 0 );
		}
		//! @brief 比較( l <= r )をします
		//! @param[in] l 比較する文字列1
		//! @param[in] r 比較する文字列2
		//! @return l <= r だった場合は true
		friend bool operator <= ( const StringW& l, const StringW& r )
		{
			return ( ::wcscmp( l.m_pStr, r.m_pStr ) <= 0 );
		}
		//! @brief 比較( l >= r )をします
		//! @param[in] l 比較する文字列1
		//! @param[in] r 比較する文字列2
		//! @return l >= r だった場合は true
		friend bool operator >= ( const StringW& l, const StringW& r )
		{
			return ( ::wcscmp( l.m_pStr, r.m_pStr ) >= 0 );
		}

		//! @brief Float64型へ変換します
		//! @param[in] pbRet 返り値\n
		//! True 成功\n
		//! False 失敗
		//! @return 値
		Float64 ToDouble( Boolean* pbRet = NULL ) const;
		//! @brief Float32型へ変換します
		//! @param[in] pbRet 返り値\n
		//! True 成功\n
		//! False 失敗
		//! @return 値
		Float32 ToFloat( Boolean* pbRet = NULL ) const;
		//! @brief Int32型へ変換します
		//! @param[in] pbRet 返り値\n
		//! True 成功\n
		//! False 失敗
		//! @return 値
		Int32 ToInt( Boolean* pbRet = NULL ) const;
		//! @brief unsigned Int32型へ変換します
		//! @param[in] pbRet 返り値\n
		//! True 成功\n
		//! False 失敗
		//! @return 値
		UInt32 ToUInt( Boolean* pbRet = NULL ) const;

		//! @brief 自身のコピーを大文字に変換して返します
		//! @return 大文字に変換した文字列
		StringW ToUpper( void );
		//! @brief 自身のコピーを小文字に変換して返します
		//! @return 小文字に変換した文字列
		StringW ToLower( void );

		//! @brief 自身を大文字に変換します
		void Upper( void );
		//! @brief 自身を小文字に変換します
		void Lower( void );

		//! @brief 指定フォーマットで文字列を作成します
		//! @param[in] format フォーマット
		//! @param[in] ... 値
		//! @return 文字列
		wchar_t* Sprintf( const wchar_t* format, ... );

		//! @brief 左端から指定した文字数だけ取得します
		//! @param[in] sizeInWords 文字数
		//! @return 文字列
		StringW Left( UIntT sizeInWords );
		//! @brief 右端から指定した文字数だけ取得します
		//! @param[in] sizeInWords 文字数
		//! @return 文字列
		StringW Right( UIntT sizeInWords );
		//! @brief 指定した位置と文字数だけ取得します
		//! @param[in] startPos 取り出しを開始する位置
		//! @param[in] sizeInWords 取り出す文字数
		//! @return 文字列
		StringW Mid( UIntT startPos, UIntT sizeInWords );

		//! @brief 指定した文字が文字列内で最初に見つかった位置のインデックスを取得します
		//! @param[in] value 見つけたい文字
		//! @return 見つかった場合は文字のインデックス、見つからなかった場合は SIZE_MAX を返します
		UIntT IndexOf( wchar_t value );
		//! @brief 指定した文字が文字列内で最後に見つかった位置のインデックスを取得します
		//! @param[in] value 見つけたい文字
		//! @return 見つかった場合は文字のインデックス、見つからなかった場合は SIZE_MAX を返します
		UIntT LastIndexOf( wchar_t value );

		//! @brief 指定したセパレータで文字列を分割します
		//! @param[in] pSeparator 文字列を分割するための区切り文字として使用する文字配列
		//! @param[out] list 分割した文字列を格納するベクタークラス
		template<Mix::Memory::SECTION_TYPE ST>
		void Split( const wchar_t* pSeparator, std::vector<Mix::StringW, std::vector<Mix::StringW, Mix::Memory::STLAllocator<ST, Mix::StringW>>>& list )
		{
			MIX_ASSERT( pSeparator != NULL );

			Mix::StringW tmpStr( m_pStr );

			wchar_t* pCtx = NULL;
			wchar_t* pPart = wcstok_s( tmpStr.m_pStr, pSeparator, &pCtx );

			while( pt != NULL )
			{
				list.push_back( Mix::StringW( pPart ) );
				pPart = wcstok_s( NULL, pSeparator, &pCtx );
			}
		}

		//! @brief 文字列のポインタを取得します
		//! @return 文字列のポインタ
		wchar_t* GetPtr( void ) const;
		//! @brief 文字列のポインタを取得します( CONST )
		//! @return 文字列のポインタ
		const wchar_t* GetConstPtr( void ) const;
		//! @brief 文字数を取得します
		//! @return 文字数を返します
		UIntT GetNum( void ) const;
		//! @brief 文字列サイズ(バイト単位)を取得します
		//! @return サイズを返します
		UIntT GetSize( void ) const;
		//! @brief 確保したメモリのサイズ(バイト単位)を取得します
		//! @return サイズを返します
		UIntT GetAllocateSize( void ) const;
		//! @brief キャパシティ(バイト単位)を取得します
		//! @return キャパシティを返します
		UIntT GetCapacity( void ) const;
	};


	//! @class StringA
	//! @brief 文字列クラス( マルチバイト )
	class _MIX_DLL_API StringA
	{
	private:
		enum { VA_TEMP_NUM = 2048 };
		static char empty;

	private:
		char* m_pStr;
		UIntT m_Num;
		UIntT m_Size;
		UIntT m_AllocateSize;
		UIntT m_Capacity;

	private:
		void Allocate( UIntT num );
		void Copy( const char* pStr );
		void Copy( const char* pStr, UIntT num );
		void Convert( const wchar_t* pStr );
		void Add( const char* pStr );
		UIntT CalcStrNum( const char* pStr );

	public:
		//! @brief コンストラクタ
		StringA( void );
		//! @brief コピーコンストラクタ
		//! @param[in] pStr ユニコード文字列へのポインタ
		StringA( const wchar_t* pStr );
		//! @brief コピーコンストラクタ
		//! @param[in] pStr マルチバイト文字列へのポインタ
		StringA( const char* pStr );
		//! @brief コピーコンストラクタ
		//! @param[in] pStr 文字列へのポインタ
		//! @param[in] size 文字列のサイズ
		StringA( const char* pStr, UIntT size );
		//! @brief コピーコンストラクタ
		//! @param[in] str 文字列
		StringA( const StringA& str );
		//! @brief デストラクタ
		~StringA( void );

		//! @brief 要素を取得します
		//! @param[in] index 取得する要素のインデックス
		//! @return 要素
		char& operator [] ( UIntT index );
		//! @brief 比較( == )をします
		//! @param[in] str 文字列
		//! @return 等しかった場合は True を返します
		Boolean operator == ( const StringA& str ) const;
		//! @brief 比較( == )をします
		//! @param[in] pStr 文字列へのポインタ
		//! @return 等しかった場合は True を返します
		Boolean operator == ( const char* pStr ) const;
		//! @brief 比較( == )をします
		//! @param[in] code 文字コード
		//! @return 等しかった場合は True を返します
		Boolean operator == ( char code ) const;
		//! @brief 比較( == )をします
		//! @param[in] nullValue NULLのみ可能
		//! @return 等しかった場合は True を返します
		Boolean operator == ( UIntT nullValue ) const;
		//! @brief 比較( != )をします
		//! @param[in] str 文字列
		//! @return 等しくなかった場合は True を返します
		Boolean operator != ( const StringA& str ) const;
		//! @brief 比較( != )をします
		//! @param[in] pStr 文字列へのポインタ
		//! @return 等しくなかった場合は True を返します
		Boolean operator != ( const char* pStr ) const;
		//! @brief 比較( != )をします
		//! @param[in] nullValue NULLのみ指定可能
		//! @return 等しくなかった場合は True を返します
		Boolean operator != ( UIntT nullValue ) const;
		//! @brief 代入( = )をします
		//! @param[in] str 文字列
		//! @return 代入後の文字列
		StringA& operator = ( const StringA& str );
		//! @brief 代入( = )をします
		//! @param[in] pStr 文字列へのポインタ
		//! @return 代入後の文字列
		StringA& operator = ( const char* pStr );
		//! @brief 代入( = )をします
		//! @param[in] pStr マルチバイト文字列へのポインタ
		//! @return 代入後の文字列
		StringA& operator = ( const wchar_t* pStr );
		//! @brief 代入( = )をします
		//! @param[in] code コード
		//! @return 代入後の文字列
		StringA& operator = ( char code );
		//! @brief 追加( += )をします
		//! @param[in] pStr 文字列へのポインタ
		//! @return 追加後の文字列
		StringA& operator += ( const char* pStr );
		//! @brief 追加( += )をします
		//! @param[in] str 文字列
		//! @return 追加後の文字列
		StringA& operator += ( const StringA& str );
		//! @brief 追加( += )をします
		//! @param[in] code 文字コード
		//! @return 追加後の文字列
		StringA& operator += ( char code );
		//! @brief 結合( + )します
		//! @param[in] str 文字列
		//! @return 結合後の文字列
		StringA operator + ( const StringA& str );
		//! @brief 結合( + )します
		//! @param[in] ls 結合先文字列
		//! @param[in] rs 結合元文字列
		//! @return 結合後の文字列
		friend const StringA operator + ( const char* ls, const StringA& rs )
		{
			StringA tmp( ls );
			tmp.Add( rs.m_pStr );

			return tmp;
		}

		//! @brief 比較( l < r )をします
		//! @param[in] l 比較する文字列1
		//! @param[in] r 比較する文字列2
		//! @return l < r だった場合は true
		friend bool operator < ( const StringA& l, const StringA& r )
		{
			return ( ::strcmp( l.m_pStr, r.m_pStr ) < 0 );
		}
		//! @brief 比較( l > r )をします
		//! @param[in] l 比較する文字列1
		//! @param[in] r 比較する文字列2
		//! @return l > r だった場合は true
		friend bool operator > ( const StringA& l, const StringA& r )
		{
			return ( ::strcmp( l.m_pStr, r.m_pStr ) > 0 );
		}
		//! @brief 比較( l <= r )をします
		//! @param[in] l 比較する文字列1
		//! @param[in] r 比較する文字列2
		//! @return l <= r だった場合は true
		friend bool operator <= ( const StringA& l, const StringA& r )
		{
			return ( ::strcmp( l.m_pStr, r.m_pStr ) <= 0 );
		}
		//! @brief 比較( l >= r )をします
		//! @param[in] l 比較する文字列1
		//! @param[in] r 比較する文字列2
		//! @return l >= r だった場合は true
		friend bool operator >= ( const StringA& l, const StringA& r )
		{
			return ( ::strcmp( l.m_pStr, r.m_pStr ) >= 0 );
		}

		//! @brief Float64型へ変換します
		//! @param[in] pbRet 返り値\n
		//! True 成功\n
		//! False 失敗
		//! @return 値
		Float64 ToDouble( Boolean* pbRet = NULL ) const;
		//! @brief Float32型へ変換します
		//! @param[in] pbRet 返り値\n
		//! True 成功\n
		//! False 失敗
		//! @return 値
		Float32 ToFloat( Boolean* pbRet = NULL ) const;
		//! @brief Int32型へ変換します
		//! @param[in] pbRet 返り値\n
		//! True 成功\n
		//! False 失敗
		//! @return 値
		Int32 ToInt( Boolean* pbRet = NULL ) const;
		//! @brief unsigned Int32型へ変換します
		//! @param[in] pbRet 返り値\n
		//! True 成功\n
		//! False 失敗
		//! @return 値
		UInt32 ToUInt( Boolean* pbRet = NULL ) const;

		//! @brief 自身のコピーを大文字に変換して返します
		//! @return 大文字に変換した文字列
		StringA ToUpper( void );
		//! @brief 自身のコピーを小文字に変換して返します
		//! @return 小文字に変換した文字列
		StringA ToLower( void );

		//! @brief 自身を大文字に変換します
		void Upper( void );
		//! @brief 自身を小文字に変換します
		void Lower( void );

		//! @brief 指定フォーマットで文字列を作成します
		//! @param[in] format フォーマット
		//! @param[in] ... 値
		//! @return 文字列
		char* Sprintf( const char* format, ... );
		//! @brief 左端から指定した文字数だけ取得します
		//! @param[in] size 取り出すサイズ
		//! @return 文字列
		StringA Left( UIntT size );
		//! @brief 右端から指定した文字数だけ取得します
		//! @param[in] size 取り出すサイズ
		//! @return 文字列
		StringA Right( UIntT size );
		//! @brief 指定した位置と文字数だけ取得します
		//! @param[in] startPos 取り出しを開始する位置
		//! @param[in] size 取り出すサイズ
		//! @return 文字列
		StringA Mid( UIntT startPos, UIntT size );

		//! @brief 指定した文字が文字列内で最初に見つかった位置のインデックスを取得します
		//! @param[in] value 見つけたい文字
		//! @return 見つかった場合は文字のインデックス、見つからなかった場合は -1 を返します
		UIntT IndexOf( char value );
		//! @brief 指定した文字が文字列内で最後に見つかった位置のインデックスを取得します
		//! @param[in] value 見つけたい文字
		//! @return 見つかった場合は文字のインデックス、見つからなかった場合は -1 を返します
		UIntT LastIndexOf( char value );

		//! @brief 指定したセパレータで文字列を分割します
		//! @param[in] pSeparator 文字列を分割するための区切り文字として使用する文字配列
		//! @param[out] list 分割した文字列を格納するベクタークラス
		template<Mix::Memory::SECTION_TYPE ST>
		void Split( const char* pSeparator, std::vector<Mix::StringA, std::vector<Mix::StringA, Mix::Memory::STLAllocator<ST, Mix::StringA>>>& list )
		{
			MIX_ASSERT( pSeparator != NULL );

			Mix::StringA tmpStr( m_pStr );

			char* pCtx = NULL;
			char* pPart = strtok_s( tmpStr.m_pStr, pSeparator, &pCtx );

			while( pPart != NULL )
			{
				list.push_back( Mix::StringA( pPart ) );
				pPart = strtok_s( NULL, pSeparator, &pCtx );
			}
		}

		//! @brief 文字列のポインタを取得します
		//! @return 文字列のポインタ
		char* GetPtr( void ) const;
		//! @brief 文字列のポインタを取得します( CONST )
		//! @return 文字列のポインタ
		const char* GetConstPtr( void ) const;
		//! @brief 文字数を取得します
		//! @return 文字数
		UIntT GetNum( void ) const;
		//! @brief 文字列サイズを取得します
		//! @return 文字列のサイズ
		UIntT GetSize( void ) const;
		//! @brief 確保したメモリのサイズを取得します
		//! @return 確保したメモリのサイズ
		UIntT GetAllocateSize( void ) const;
		//! @brief キャパシティ(バイト単位)を取得します
		//! @return キャパシティを返します
		UIntT GetCapacity( void ) const;
	};

	//! @typedef String
	//! @brief 文字列
	typedef StringW String;

	//! @typedef StringListA
	//! @brief マルチバイト文字列リスト
	typedef std::vector<Mix::StringA, Mix::Memory::STLAllocator<Mix::Memory::SECTION_GENERAL, Mix::StringA>> StringListA;
	//! @typedef StringListW
	//! @brief ワイド文字列リスト
	typedef std::vector<Mix::StringW, Mix::Memory::STLAllocator<Mix::Memory::SECTION_GENERAL, Mix::StringW>> StringListW;

	//! @typedef StringList
	//! @brief 文字列リスト
	typedef StringListW StringList;
};