#include "Mix/Tool/Win32/Core/Common.h"

namespace Mix{ namespace Tool{ namespace Win32{

////////////////////////////////////////////////////////////////////////////////////////////////////
// ログ
////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef TEST_WIN32

class ManagedLogClear
{
public:
	ManagedLogClear( void )
	{
		Mix::Tool::Logger::Clear();
	}
};

class ManagedLogPrint
{
public:
	ManagedLogPrint( Mix::Tool::Win32::LOGTYPE type, const wchar_t* pStr )
	{
		Mix::Tool::Logger::Type netType;

		switch( type )
		{
		case Mix::Tool::Win32::LT_INFO:
			netType = Mix::Tool::Logger::Type::Info;
			break;
		case Mix::Tool::Win32::LT_WARNING:
			netType = Mix::Tool::Logger::Type::Warning;
			break;
		case Mix::Tool::Win32::LT_ERROR:
			netType = Mix::Tool::Logger::Type::Error;
			break;
		};

		Mix::Tool::Logger::Print( netType, gcnew System::String( pStr ) );
	}
};

#endif //TEST_WIN32

#ifndef TEST_WIN32
	#pragma unmanaged
#endif //TEST_WIN32

void LogClear( void )
{
#ifndef TEST_WIN32
	ManagedLogClear clear;
#endif //TEST_WIN32
}

void LogPrint( Mix::Tool::Win32::LOGTYPE type, const wchar_t* pFormat, ... )
{
	wchar_t str[4096] = L"";
	va_list args;

	va_start( args, pFormat );
	_vsnwprintf_s( str, sizeof( str ) >> 1, _TRUNCATE, pFormat, args );
	va_end( args );

//	::wvsprintf( str, pFormat, ( va_list )( &pFormat + 1 ) );

#ifdef TEST_WIN32

	::OutputDebugStringW( str );
	::OutputDebugStringW( L"\n" );

#else //TEST_WIN32

	ManagedLogPrint print( type, str );

#endif //TEST_WIN32
}

#ifndef TEST_WIN32
	#pragma managed
#endif //TEST_WIN32

////////////////////////////////////////////////////////////////////////////////////////////////////
// ワイド文字列→マルチバイト文字列
////////////////////////////////////////////////////////////////////////////////////////////////////

bool WideToAnsi( const wchar_t* pSrc, std::string& dst )
{
	if( pSrc == NULL )
	{
		dst = "";
		return true;
	}

	int tempSize;
	char* temp;

	tempSize = ::WideCharToMultiByte( CP_THREAD_ACP, 0, pSrc, -1, NULL, 0, NULL, NULL );
	if( tempSize > 0 )
	{
		tempSize += 1;
	}
	else
	{
		dst = "";
		return false;
	}

	temp = static_cast<char*>( ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, tempSize ) );
	if( temp == NULL )
	{
		dst = "";
		return false;
	}

	if( ::WideCharToMultiByte( CP_THREAD_ACP, 0, pSrc, -1, temp, tempSize, NULL, NULL ) <= 0 )
	{
		::HeapFree( ::GetProcessHeap(), 0, temp );
		return false;
	}

	dst = temp;
	::HeapFree( ::GetProcessHeap(), 0, temp );

	return true;
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// マルチバイト文字列→ワイド文字列
////////////////////////////////////////////////////////////////////////////////////////////////////

bool AnsiToWide( const char* pSrc, std::wstring& dst )
{
	if( pSrc == NULL )
	{
		dst = L"";
		return true;
	}

	int tempSize;
	wchar_t* temp;

	tempSize = ::MultiByteToWideChar( CP_THREAD_ACP, 0, pSrc, -1, NULL, 0 );
	if( tempSize > 0  )
	{
		tempSize += 1;
	}
	else
	{
		dst = L"";
		return false;
	}

	temp = static_cast<wchar_t*>( ::HeapAlloc( ::GetProcessHeap(), HEAP_ZERO_MEMORY, ( tempSize << 1 ) ) );
	if( temp == NULL )
	{
		dst = L"";
		return false;
	}

	if( ::MultiByteToWideChar( CP_THREAD_ACP, 0, pSrc, -1, temp, tempSize ) <= 0 )
	{
		::HeapFree( ::GetProcessHeap(), 0, temp );
		dst = L"";
		return false;
	}

	dst = temp;
	::HeapFree( ::GetProcessHeap(), 0, temp );

	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// ファイル名の変換に必要なデータを作成
////////////////////////////////////////////////////////////////////////////////////////////////////

bool CreateConvertFileNameData( const wchar_t* pFileName, CONVERT_FILENAME_DATA& cfd )
{
	if( pFileName == NULL )
	{
		return false;
	}

	int i;
	int j;
	wchar_t lastChar;
	std::wstring source;
	std::wstring temp;

	source = pFileName;

	for( i = ( ::wcslen( pFileName ) - 1 ); i >= 0; i-- )
	{
		if( ( pFileName[i] == L'\\' ) ||
			( pFileName[i] == L'/' ) )
		{
			cfd.path = source.substr( 0, ( i + 1 ) );

			temp = source.substr( ( i + 1 ) );
			for( j = ( temp.size() - 1 ); j >= 0; j-- )
			{
				if( temp[j] == L'.' )
				{
					cfd.name = temp.substr( 0, j );
					cfd.ext = temp.substr( j );
					break;
				}
			}

			break;
		}
	}

	if( ( cfd.path.size() == 0 ) ||
		( cfd.name.size() == 0 ) ||
		( cfd.ext.size() == 0 ) )
	{
		return false;
	}

	lastChar = cfd.path[cfd.path.size() - 1];
	if( ( lastChar != L'\\' ) &&
		( lastChar != L'/' ) )
	{
		cfd.path += L'\\';
	}

	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// ファイル名を指定されたフォーマットに従って変換
////////////////////////////////////////////////////////////////////////////////////////////////////

bool ConvertFileName( const Mix::Tool::Win32::CONVERT_FILENAME_DATA& cfd, const wchar_t* pFormat, std::wstring& fileName )
{
	if( ( pFormat == NULL ) ||
		( ::wcslen( pFormat ) == 0 ) )
	{
		fileName = L"";
		return false;
	}

	int i;
	int j;
	wchar_t temp[12];

	fileName = L"";

	for( i = 0; pFormat[i] != L'\0'; i++ )
	{
		if( pFormat[i] == L'$' )
		{
			::memset( temp, 0, sizeof( temp ) );

			for( j = i; ( pFormat[j] != L'\0' ) && ( ( j - i ) < 12 ); j++ )
			{
				temp[j - i] = pFormat[j];

				if( pFormat[j] == L')' )
				{
					break;
				}
			}

			if( ::wcscmp( temp, L"$(DIR)" ) == 0 )
			{
				fileName += cfd.path;
			}
			else if( ::wcscmp( temp, L"$(FILENAME)" ) == 0 )
			{
				fileName += cfd.name;
			}
			else if( ::wcscmp( temp, L"$(EXT)" ) == 0 )
			{
				fileName += cfd.ext;
			}
			else
			{
				return false;
			}

			i += ( ::wcslen( temp ) - 1 );
		}
		else
		{
			fileName += pFormat[i];
		}
	}

	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// ベクトル
////////////////////////////////////////////////////////////////////////////////////////////////////

//最小値を取得
D3DXVECTOR3 Min( const D3DXVECTOR3& a, const D3DXVECTOR3& b )
{
	D3DXVECTOR3 ret;

	ret.x = ( a.x > b.x )? b.x : a.x;
	ret.y = ( a.y > b.y )? b.y : a.y;
	ret.z = ( a.z > b.z )? b.z : a.z;

	return ret;
}

//最大値を取得
D3DXVECTOR3 Max( const D3DXVECTOR3& a, const D3DXVECTOR3& b )
{
	D3DXVECTOR3 ret;

	ret.x = ( a.x < b.x )? b.x : a.x;
	ret.y = ( a.y < b.y )? b.y : a.y;
	ret.z = ( a.z < b.z )? b.z : a.z;

	return ret;
}

//b.wを使って補完( 結果の w は a.w )
D3DXVECTOR4 Lerp_B( const D3DXVECTOR4& a, const D3DXVECTOR4& b )
{
	float tb = MIX_CLAMP( b.w, 0.0f, 1.0f );
	float ta = 1.0f - tb;
	D3DXVECTOR4 ret;

	ret.x = a.x * ta + b.x * tb;
	ret.y = a.y * ta + b.y * tb;
	ret.z = a.z * ta + b.z * tb;
	ret.w = a.w;

	return ret;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// XYZ軸からクォータニオンを作成
////////////////////////////////////////////////////////////////////////////////////////////////////

D3DXQUATERNION ToQuaternion( const D3DXVECTOR3& x, const D3DXVECTOR3& y, const D3DXVECTOR3& z )
{
	D3DXQUATERNION quat;
	D3DXMATRIX mat;

	mat._11 = x.x;
	mat._12 = x.y;
	mat._13 = x.z;
	mat._14 = 0.0f;

	mat._21 = y.x;
	mat._22 = y.y;
	mat._23 = y.z;
	mat._24 = 0.0f;

	mat._31 = z.x;
	mat._32 = z.y;
	mat._33 = z.z;
	mat._34 = 0.0f;

	mat._41 = 0.0f;
	mat._42 = 0.0f;
	mat._43 = 0.0f;
	mat._44 = 1.0f;

	D3DXQuaternionRotationMatrix( &quat, &mat );

	return quat;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// 角度(DEG)を範囲内(-180〜+180)に収める
////////////////////////////////////////////////////////////////////////////////////////////////////

static float ModifyDegree( float angle )
{
	static const float MAX_ANGLE = +180.0f - FLT_EPSILON;
	static const float MIN_ANGLE = -180.0f + FLT_EPSILON;

	if( angle >= MAX_ANGLE )
	{
		while( angle >= MAX_ANGLE )
		{
			angle -= 180.0f;
		}
	}
	else if( angle <= MIN_ANGLE )
	{
		while( angle <= MIN_ANGLE )
		{
			angle += 180.0f;
		}
	}

	return angle;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// クォータニオンからオイラー角を求める
////////////////////////////////////////////////////////////////////////////////////////////////////

//XYZ(ラジアン単位)
D3DXVECTOR3 ToEulerRadianXYZ( const D3DXQUATERNION& quat )
{
	D3DXMATRIX mat;
	D3DXVECTOR4 temp;
	D3DXVECTOR3 rot;
	float cosY;

	/*
		一度行列に変換
	*/

	D3DXMatrixRotationQuaternion( &mat, &quat );

	/*
		ノーマライズ
	*/

	D3DXVec4Normalize( &temp, &D3DXVECTOR4( mat._11, mat._12, mat._13, mat._14 ) );
	mat._11 = temp.x; mat._12 = temp.y; mat._13 = temp.z; mat._14 = temp.w;

	D3DXVec4Normalize( &temp, &D3DXVECTOR4( mat._21, mat._22, mat._23, mat._24 ) );
	mat._21 = temp.x; mat._22 = temp.y; mat._23 = temp.z; mat._24 = temp.w;

	D3DXVec4Normalize( &temp, &D3DXVECTOR4( mat._31, mat._32, mat._33, mat._34 ) );
	mat._31 = temp.x; mat._32 = temp.y; mat._33 = temp.z; mat._34 = temp.w;

	/*
		オイラー角を求める
	*/

	rot.y = asin( MIX_CLAMP( -mat._13, -1.0f, +1.0f ) );
	cosY = cosf( rot.y );

	if( MIX_FLOAT_IS_ZERO( cosY ) == false )
	{
		//X軸の角度
		rot.x = ::asin( MIX_CLAMP( mat._23 / cosY, -1.0f, +1.0f ) );
		if( mat._33 < FLT_EPSILON )
		{
			rot.x = D3DX_PI - rot.x;
		}

		//Z軸の角度
		rot.z = atan2( mat._12, mat._11 );
	}
	else
	{
		//X軸の角度
		rot.x = 0.0f;

		//Z軸の角度
		rot.z = atan2( -mat._21, mat._22 );
	}

	rot.x = ModifyDegree( D3DXToDegree( rot.x ) );
	rot.y = ModifyDegree( D3DXToDegree( rot.y ) );
	rot.z = ModifyDegree( D3DXToDegree( rot.z ) );

	return rot;
}

//XYZ(デグリー単位)
D3DXVECTOR3 ToEulerDegreeXYZ( const D3DXQUATERNION& quat )
{
	D3DXVECTOR3 rot = ToEulerRadianXYZ( quat );

	rot.x = ModifyDegree( D3DXToDegree( rot.x ) );
	rot.y = ModifyDegree( D3DXToDegree( rot.y ) );
	rot.z = ModifyDegree( D3DXToDegree( rot.z ) );

	return rot;
}

#if 0

D3DXVECTOR3 ToEulerDegreeYZX( const D3DXQUATERNION& quat )
{
	D3DXMATRIX mat;
	D3DXVECTOR4 temp;
	D3DXVECTOR3 rot;
	float cosZ;

	/*
		一度行列に変換
	*/

	D3DXMatrixRotationQuaternion( &mat, &quat );

	/*
		ノーマライズ
	*/

	D3DXVec4Normalize( &temp, &D3DXVECTOR4( mat._11, mat._12, mat._13, mat._14 ) );
	mat._11 = temp.x; mat._12 = temp.y; mat._13 = temp.z; mat._14 = temp.w;

	D3DXVec4Normalize( &temp, &D3DXVECTOR4( mat._21, mat._22, mat._23, mat._24 ) );
	mat._21 = temp.x; mat._22 = temp.y; mat._23 = temp.z; mat._24 = temp.w;

	D3DXVec4Normalize( &temp, &D3DXVECTOR4( mat._31, mat._32, mat._33, mat._34 ) );
	mat._31 = temp.x; mat._32 = temp.y; mat._33 = temp.z; mat._34 = temp.w;

	/*
		オイラー角を求める
	*/

	rot.z = asin( MIX_CLAMP( -mat._21, -1.0f, +1.0f ) );
	cosZ = cosf( rot.z );

	if( MIX_FLOAT_IS_ZERO( cosZ ) == false )
	{
		rot.y = atan2( mat._23, mat._22 );

		rot.x = asin( MIX_CLAMP( mat._31 / cosZ, -1.0f, +1.0f ) );
		if( mat._11 < 0.0f )
		{
			rot.x = D3DX_PI - rot.x;
		}
	}
	else
	{
		rot.y = atan2( -mat._32, mat._33 );
		rot.x = 0.0f;
	}

	rot.x = D3DXToDegree( rot.x );
	rot.y = D3DXToDegree( rot.y );
	rot.z = D3DXToDegree( rot.z );

	return rot;
}

#endif

//ZYX(ラジアン単位)
D3DXVECTOR3 ToEulerRadianZYX( const D3DXQUATERNION& quat )
{
	D3DXMATRIX mat;
	D3DXVECTOR4 temp;
	D3DXVECTOR3 rot;
	float cosY;

	/*
		一度行列に変換
	*/

	D3DXMatrixRotationQuaternion( &mat, &quat );

	/*
		ノーマライズ
	*/

	D3DXVec4Normalize( &temp, &D3DXVECTOR4( mat._11, mat._12, mat._13, mat._14 ) );
	mat._11 = temp.x; mat._12 = temp.y; mat._13 = temp.z; mat._14 = temp.w;

	D3DXVec4Normalize( &temp, &D3DXVECTOR4( mat._21, mat._22, mat._23, mat._24 ) );
	mat._21 = temp.x; mat._22 = temp.y; mat._23 = temp.z; mat._24 = temp.w;

	D3DXVec4Normalize( &temp, &D3DXVECTOR4( mat._31, mat._32, mat._33, mat._34 ) );
	mat._31 = temp.x; mat._32 = temp.y; mat._33 = temp.z; mat._34 = temp.w;

	/*
		オイラー角を求める
	*/

	rot.y = asin( MIX_CLAMP( mat._31, -1.0f, +1.0f ) );
	cosY = cosf( rot.y );

	if( MIX_FLOAT_IS_ZERO( cosY ) == false )
	{
		//X軸の角度( ラジアン )
		rot.x = atan2( -mat._32, mat._33 );

		//Z軸の角度( ラジアン )
		rot.z = asin( MIX_CLAMP( -mat._21 / cosY, -1.0f, +1.0f ) );
		if( mat._11 < FLT_EPSILON )
		{
			rot.z = D3DX_PI - rot.z;
		}
	}
	else
	{
		//X軸の角度( ラジアン )
		rot.x = atan2( mat._23, mat._22 );

		//Z軸の角度( ラジアン )
		rot.z = 0.0f;
	}

	return rot;
}

//ZYX(デグリー単位)
D3DXVECTOR3 ToEulerDegreeZYX( const D3DXQUATERNION& quat )
{
	D3DXVECTOR3 rot = ToEulerRadianZYX( quat );

	rot.x = D3DXToDegree( rot.x );
	rot.y = D3DXToDegree( rot.y );
	rot.z = D3DXToDegree( rot.z );

	return rot;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// 行列
////////////////////////////////////////////////////////////////////////////////////////////////////

//スケーリングを外す( スケールのサイン( +- )は維持する )
D3DXMATRIX Matrix_ExcludeScaling( const D3DXMATRIX& mat )
{
	float sx = D3DXVec3Length( &D3DXVECTOR3( mat._11, mat._12, mat._13 ) );
	float sy = D3DXVec3Length( &D3DXVECTOR3( mat._21, mat._22, mat._23 ) );
	float sz = D3DXVec3Length( &D3DXVECTOR3( mat._31, mat._32, mat._33 ) );

	D3DXMATRIX ret;

	D3DXMatrixIdentity( &ret );

	if(	( MIX_FLOAT_IS_ZERO( sx ) == false ) &&
		( MIX_FLOAT_IS_ZERO( sy ) == false ) &&
		( MIX_FLOAT_IS_ZERO( sz ) == false ) )
	{
		sx = 1.0f / sx;
		sy = 1.0f / sy;
		sz = 1.0f / sz;

		ret._11 = mat._11 * sx;
		ret._12 = mat._12 * sx;
		ret._13 = mat._13 * sx;
		ret._21 = mat._21 * sy;
		ret._22 = mat._22 * sy;
		ret._23 = mat._23 * sy;
		ret._31 = mat._31 * sz;
		ret._32 = mat._32 * sz;
		ret._33 = mat._33 * sz;
	}

	ret._41 = mat._41;
	ret._42 = mat._42;
	ret._43 = mat._43;
	ret._44 = 1.0f;

	return ret;
}

//スケーリング ローテーション トランスレーション の成分に分解
void Matrix_Decompose( const D3DXMATRIX& mat, D3DXVECTOR3* pS, D3DXQUATERNION* pR, D3DXVECTOR3* pT )
{
	float sx = 0.0f;
	float sy = 0.0f;
	float sz = 0.0f;

	if( ( pS != NULL ) ||
		( pR != NULL ) )
	{
		sx = D3DXVec3Length( &D3DXVECTOR3( mat._11, mat._12, mat._13 ) );
		sy = D3DXVec3Length( &D3DXVECTOR3( mat._21, mat._22, mat._23 ) );
		sz = D3DXVec3Length( &D3DXVECTOR3( mat._31, mat._32, mat._33 ) );
	}

	if( pS != NULL )
	{
		pS->x = sx; 
		pS->y = sy; 
		pS->z = sz; 
	}

	if( pR != NULL )
	{
		if(	( MIX_FLOAT_IS_ZERO( sx ) == true ) ||
			( MIX_FLOAT_IS_ZERO( sy ) == true ) ||
			( MIX_FLOAT_IS_ZERO( sz ) == true ) )
		{
			D3DXQuaternionIdentity( pR );
		}
		else
		{
			D3DXMATRIX norm;

			sx = 1.0f / sx;
			sy = 1.0f / sy;
			sz = 1.0f / sz;

			D3DXMatrixIdentity( &norm );

			norm._11 = mat._11 * sx;
			norm._12 = mat._12 * sx;
			norm._13 = mat._13 * sx;
			norm._21 = mat._21 * sy;
			norm._22 = mat._22 * sy;
			norm._23 = mat._23 * sy;
			norm._31 = mat._31 * sz;
			norm._32 = mat._32 * sz;
			norm._33 = mat._33 * sz;

			D3DXQuaternionRotationMatrix( pR, &norm );
		}
	}

	if( pT != NULL )
	{
		pT->x = mat._41;
		pT->y = mat._42;
		pT->z = mat._43;
	}
}

}}}
