#pragma once

////////////////////////////////////////////////////////////////////////////////////////////////////
// インクルード
////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
	#include <crtdbg.h>
#endif //_DEBUG

#ifndef TEST_WIN32
	#include <vcclr.h>
#endif //TEST_WIN32

#include <Windows.h>
#include <string>
#include <vector>

////////////////////////////////////////////////////////////////////////////////////////////////////
// マクロ
////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG

	#define MIX_ABORT ( *( ( int* )0x0 ) = 0x0 )

	#define MIX_ASSERT( cnd ) { \
			if( !( cnd ) )\
			{\
				wchar_t text[1024]; \
				::swprintf_s( text, L"File : \"%s\"\nLine : %d\nCondition : %s\n\n", __FILEW__, __LINE__, L#cnd ); \
				::MessageBoxW( ::GetActiveWindow(), text, L"Error", MB_OK | MB_ICONSTOP ); \
				MIX_ABORT; \
			}\
		}

	#define MIX_ERROR( mes, ... ) { \
			wchar_t fmt[1024] = L"File : \"%s\"\nLine : %d\n\n"; \
			wchar_t text[1024]; \
			::wcscat_s( fmt, ( sizeof( fmt ) >> 1 ), mes ); \
			::swprintf_s( text, fmt, __FILEW__, __LINE__, __VA_ARGS__ ); \
			::MessageBoxW( ::GetActiveWindow(), text, L"Error", MB_OK | MB_ICONSTOP ); \
			MIX_ABORT; \
		}

	#define MIX_TRACE( fmt, ... ) { \
			wchar_t text[1024] = L""; \
			::swprintf_s( text, fmt, ##__VA_ARGS__ ); \
			::OutputDebugStringW( text ); \
		}

	#define MIX_TRACELINE( fmt, ... ) { \
			wchar_t text[1024] = L""; \
			::swprintf_s( text, fmt, ##__VA_ARGS__ ); \
			::wcscat_s( text, sizeof( text ) >> 1, L"\n" ); \
			::OutputDebugStringW( text ); \
		}

#else //_DEBUG

	#define MIX_ASSERT( cnd )
	#define MIX_ERROR( mes, ... )
	#define MIX_TRACE( fmt, ... )
	#define MIX_TRACELINE( fmt, ... )

#endif// _DEBUG

#ifdef MIX_USE_THREAD
	#define MIX_THREAD_SLEEP Sleep( 0 );
#else //MIX_USE_THREAD
	#define MIX_THREAD_SLEEP
#endif //MIX_USE_THREAD

#define MIX_CLAMP( a, b, c ) ( ( ( b ) > ( a ) )? ( b ) : ( ( c ) < ( a ) )? ( c ) : ( a ) )

#define MIX_FLOAT_IS_ZERO( a ) ( ( -FLT_EPSILON <= ( a ) ) && ( FLT_EPSILON >= ( a ) ) )
#define MIX_FLOAT_DIV( a, b ) ( ( MIX_FLOAT_IS_ZERO( a ) == false )? ( ( a ) / ( b ) ) : ( 0.0f ) )
#define MIX_FLOAT_RCP( a ) ( ( MIX_FLOAT_IS_ZERO( a ) == false )? ( 1.0f / a ) : 0.0f )

#define MIX_FLOAT_EQUAL( a, b ) ( ::fabs( ( a ) - ( b ) ) <= FLT_EPSILON )
#define MIX_FLOAT_NOT_EQUAL( a, b ) ( ::fabs( ( a ) - ( b ) ) > FLT_EPSILON )

#define MIX_XY_TO_VEC2( x, y ) D3DXVECTOR2( static_cast<float>( x ), static_cast<float>( y ) )
#define MIX_POINT_TO_VEC2( a ) D3DXVECTOR2( static_cast<float>( a.x ), static_cast<float>( a.y ) )

#define MIX_DELETE( x ){ if( x != NULL ){ delete x; x = NULL; } }
#define MIX_DELETE_ARRAY( x ){ if( x != NULL ){ delete [] x; x = NULL; } }
#define MIX_RELEASE( x ){ if( x != NULL ){ x->Release(); x = NULL; } }
#define MIX_ADD_REF( x ){ if( x != NULL ){ x->AddRef(); } }

#define MIX_TEST_BIT( flags, bit ) ( flags & bit )
#define MIX_SET_BIT( flags, bit ) { if( ( flags & bit ) != bit ) { flags |= bit; } }
#define MIX_RESET_BIT( flags, bit ) { if( ( flags & bit ) == bit ) { flags ^= bit; } }

namespace Mix{ namespace Tool{ namespace Win32{

#ifndef TEST_WIN32
	#pragma unmanaged
#endif //TEST_WIN32

	enum LOGTYPE
	{
		LT_INFO,
		LT_WARNING,
		LT_ERROR,
	};

	void LogClear( void );
	void LogPrint( Mix::Tool::Win32::LOGTYPE type, const wchar_t* pFormat, ... );

#ifndef TEST_WIN32
	#pragma managed
#endif //TEST_WIN32

	//ファイル名変換データ構造体
	struct CONVERT_FILENAME_DATA
	{
		std::wstring path;	//ファイル名を含まないパス
		std::wstring name;	//ファイル名( 拡張子無し )
		std::wstring ext;	//ファイルの拡張子( .含む )
	};

	//ファイル名の変換に必要なデータを作成
	bool CreateConvertFileNameData( const wchar_t* pFileName, CONVERT_FILENAME_DATA& cfd );
	//ファイル名を指定されたフォーマットに従って変換
	bool ConvertFileName( const Mix::Tool::Win32::CONVERT_FILENAME_DATA& cfd, const wchar_t* pFormat, std::wstring& fileName );

	//ワイド文字列→マルチバイト文字列
	bool WideToAnsi( const wchar_t* pSrc, std::string& dst );
	//マルチバイト文字列→ワイド文字列
	bool AnsiToWide( const char* pSrc, std::wstring& dst );

	//ユニークな名前を作成
	template <typename TYPE>
	bool CreateUniqueName(	TYPE* pSelf,
							typename std::vector<TYPE*>::iterator it_begin,
							typename std::vector<TYPE*>::iterator it_end, 
							const wchar_t* pRequestName, std::wstring& name,
							unsigned int maxNumber = 999 )
	{
		MIX_ASSERT( pRequestName != NULL );

		if( ::wcslen( pRequestName ) == 0 )
		{
			return false;
		}

		unsigned int number = 0;
		wchar_t temp[256];

		::wcscpy_s( temp, sizeof( temp ) >> 1, pRequestName );

		for( ;; )
		{
			std::vector<TYPE*>::iterator it;

			for( it = it_begin; it != it_end; ++it )
			{
				if( ( *it ) == pSelf )
				{
					continue;
				}

				if( ::wcscmp( ( *it )->GetName(), temp ) == 0 )
				{
					break;
				}
			}

			if( it != it_end )
			{
				if( number <= maxNumber )
				{
					::wsprintf( temp, L"%s_%.3d", pRequestName, number );
					number++;
				}
				else
				{
					break;
				}
			}
			else
			{
				break;
			}
		}

		if( number > ( maxNumber + 1 ) )
		{
			//そんなばかな、、、
			return false;
		}

		name = temp;

		return true;
	}

	//ベクトルの最小値
	D3DXVECTOR3 Min( const D3DXVECTOR3& a, const D3DXVECTOR3& b );
	//ベクトルの最大値
	D3DXVECTOR3 Max( const D3DXVECTOR3& a, const D3DXVECTOR3& b );
	//b.wを使って補完( 結果の w は a.w )
	D3DXVECTOR4 Lerp_B( const D3DXVECTOR4& a, const D3DXVECTOR4& b );

	//XYZ軸からクォータニオンを作成
	D3DXQUATERNION ToQuaternion( const D3DXVECTOR3& x, const D3DXVECTOR3& y, const D3DXVECTOR3& z );
	//クォータニオンからXYZオイラー角( ラジアン単位 )
	D3DXVECTOR3 ToEulerRadianXYZ( const D3DXQUATERNION& quat );
	//クォータニオンからXYZオイラー角( デグリー単位 )
	D3DXVECTOR3 ToEulerDegreeXYZ( const D3DXQUATERNION& quat );
	//クォータニオンからZYXオイラー角( ラジアン単位 )
	D3DXVECTOR3 ToEulerRadianZYX( const D3DXQUATERNION& quat );
	//クォータニオンからZYXオイラー角( デグリー単位 )
	D3DXVECTOR3 ToEulerDegreeZYX( const D3DXQUATERNION& quat );

	//行列からスケーリングを外す( スケールのサイン( +- )は維持する )
	D3DXMATRIX Matrix_ExcludeScaling( const D3DXMATRIX& mat );
	//行列を スケーリング ローテーション トランスレーション の成分に分解
	void Matrix_Decompose( const D3DXMATRIX& mat, D3DXVECTOR3* pS, D3DXQUATERNION* pR, D3DXVECTOR3* pT );

}}}
