#include "Mix/Tool/Win32/Core/Graphics/LineHelper.h"

namespace Mix{ namespace Tool{ namespace Win32{ namespace Graphics{

LineHelper::LineHelper( void ) :
m_TransformStart( 0 ),
m_Color( 1.0f, 1.0f, 1.0f, 1.0f )
{
	const int SPHERE_STEPCOUNT = 30;
	const int SPHERE_STEPCOUNT_H = SPHERE_STEPCOUNT >> 1;
	const float SPHERE_STEP = 360.0f / static_cast<float>( SPHERE_STEPCOUNT );
	const float SPHERE_STEP_H = 180.0f / static_cast<float>( SPHERE_STEPCOUNT_H );

	::D3DXMatrixIdentity( &m_Mat );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 球のラインテーブルを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		D3DXVECTOR3 ppx( 0.0f, 0.0f, 1.0f );
		D3DXVECTOR3 ppy( 0.0f, 0.0f, 1.0f );
		D3DXVECTOR3 ppz( 1.0f, 0.0f, 0.0f );

		for( int i = 0; i < ( SPHERE_STEPCOUNT + 1 ); i++ )
		{
			D3DXVECTOR3 px( 0.0f, 0.0f, 1.0f );
			D3DXVECTOR3 py( 0.0f, 0.0f, 1.0f );
			D3DXVECTOR3 pz( 1.0f, 0.0f, 0.0f );

			D3DXMATRIX mx;
			D3DXMATRIX my;
			D3DXMATRIX mz;
			D3DXVECTOR3 tempVec;

			D3DXMatrixRotationX( &mx, D3DXToRadian( SPHERE_STEP * static_cast<float>( i ) ) );
			D3DXMatrixRotationY( &my, D3DXToRadian( SPHERE_STEP * static_cast<float>( i ) ) );
			D3DXMatrixRotationZ( &mz, D3DXToRadian( SPHERE_STEP * static_cast<float>( i ) ) );

			px = *D3DXVec3TransformCoord( &tempVec, &px, &mx );
			py = *D3DXVec3TransformCoord( &tempVec, &py, &my );
			pz = *D3DXVec3TransformCoord( &tempVec, &pz, &mz );

			m_SphereLineTable.push_back( ppx );
			m_SphereLineTable.push_back( px );

			m_SphereLineTable.push_back( ppy );
			m_SphereLineTable.push_back( py );

			m_SphereLineTable.push_back( ppz );
			m_SphereLineTable.push_back( pz );

			ppx = px;
			ppy = py;
			ppz = pz;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 半球のラインテーブルを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		D3DXVECTOR3 ppx( 0.0f, 0.0f, 1.0f );
		D3DXVECTOR3 ppz( 1.0f, 0.0f, 0.0f );

		for( int i = 0; i < ( SPHERE_STEPCOUNT_H + 1 ); i++ )
		{
			D3DXVECTOR3 px( 0.0f, 0.0f, 1.0f );
			D3DXVECTOR3 pz( 1.0f, 0.0f, 0.0f );

			D3DXMATRIX mx;
			D3DXMATRIX mz;
			D3DXVECTOR3 tempVec;

			D3DXMatrixRotationX( &mx, -D3DXToRadian( SPHERE_STEP_H * static_cast<float>( i ) ) );
			D3DXMatrixRotationZ( &mz, D3DXToRadian( SPHERE_STEP_H * static_cast<float>( i ) ) );

			px = *D3DXVec3TransformCoord( &tempVec, &px, &mx );
			pz = *D3DXVec3TransformCoord( &tempVec, &pz, &mz );

			m_HemisphereLineTable.push_back( ppx );
			m_HemisphereLineTable.push_back( px );

			m_HemisphereLineTable.push_back( ppz );
			m_HemisphereLineTable.push_back( pz );

			ppx = px;
			ppz = pz;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// シリンダーのラインテーブルを作成
	////////////////////////////////////////////////////////////////////////////////////////////////////

	{
		/*
			エッジ
		*/

		D3DXVECTOR3 ppy( 0.0f, 0.0f, 1.0f );

		for( int i = 0; i < ( SPHERE_STEPCOUNT + 1 ); i++ )
		{
			D3DXVECTOR3 py( 0.0f, 0.0f, 1.0f );

			D3DXMATRIX my;
			D3DXVECTOR3 tempVec;

			D3DXMatrixRotationY( &my, D3DXToRadian( SPHERE_STEP * static_cast<float>( i ) ) );

			py = *D3DXVec3TransformCoord( &tempVec, &py, &my );

			m_CylinderLineTable.push_back( D3DXVECTOR3( ppy.x, +1.0f, ppy.z ) );
			m_CylinderLineTable.push_back( D3DXVECTOR3(  py.x, +1.0f,  py.z ) );

			m_CylinderLineTable.push_back( D3DXVECTOR3( ppy.x, -1.0f, ppy.z ) );
			m_CylinderLineTable.push_back( D3DXVECTOR3(  py.x, -1.0f,  py.z ) );

			ppy = py;
		}

		/*
			表面
		*/

		m_CylinderLineTable.push_back( D3DXVECTOR3( +1.0f, +1.0f, +0.0f ) );
		m_CylinderLineTable.push_back( D3DXVECTOR3( +1.0f, -1.0f, +0.0f ) );
		m_CylinderLineTable.push_back( D3DXVECTOR3( -1.0f, +1.0f, +0.0f ) );
		m_CylinderLineTable.push_back( D3DXVECTOR3( -1.0f, -1.0f, +0.0f ) );

		m_CylinderLineTable.push_back( D3DXVECTOR3( +0.0f, +1.0f, +1.0f ) );
		m_CylinderLineTable.push_back( D3DXVECTOR3( +0.0f, -1.0f, +1.0f ) );
		m_CylinderLineTable.push_back( D3DXVECTOR3( +0.0f, +1.0f, -1.0f ) );
		m_CylinderLineTable.push_back( D3DXVECTOR3( +0.0f, -1.0f, -1.0f ) );
	}
}

LineHelper::~LineHelper( void )
{
}

void LineHelper::Clear( void )
{
	m_VertexList.clear();
	m_TransformStart = 0;
}

void LineHelper::IdentityColor( void )
{
	m_Color = D3DXVECTOR4( 1.0f, 1.0f, 1.0f, 1.0f );
}

void LineHelper::SetColor( const D3DXVECTOR4& color )
{
	m_Color = color;
}

const D3DXVECTOR4& LineHelper::GetColor( void ) const
{
	return m_Color;
}

void LineHelper::IdentityMatrix( void )
{
	::D3DXMatrixIdentity( &m_Mat );
}

void LineHelper::SetMatrix( const D3DXMATRIX& mat )
{
	m_Mat = mat;
}

const D3DXMATRIX& LineHelper::GetMatrix( void ) const
{
	return m_Mat;
}

void LineHelper::AddPoints( const D3DXVECTOR3& p0, const D3DXVECTOR3& p1 )
{
	Mix::Tool::Win32::Graphics::LINE_VERTEX lv;

	lv.color = m_Color;

	lv.pos = D3DXVECTOR4( p0.x, p0.y, p0.z, 1.0f );
	m_VertexList.push_back( lv );

	lv.pos = D3DXVECTOR4( p1.x, p1.y, p1.z, 1.0f );
	m_VertexList.push_back( lv );

	Transform();
}

void LineHelper::AddPoints( const D3DXVECTOR3* points, unsigned int count )
{
	if( ( points == NULL ) ||
		( count == 0 ) ||
		( ( count % 2 ) != 0 ) )
	{
		return;
	}

	Mix::Tool::Win32::Graphics::LINE_VERTEX lv;

	lv.color = m_Color;

	for( unsigned int i = 0; i < count; i++ )
	{
		const D3DXVECTOR3& pos = points[i];

		lv.pos = D3DXVECTOR4( pos.x, pos.y, pos.z, 1.0f );
		m_VertexList.push_back( lv );
	}

	Transform();
}

void LineHelper::AddBox( const D3DXVECTOR3& halfExtents )
{
	D3DXVECTOR4 points[8] =
	{
		D3DXVECTOR4( -halfExtents.x, +halfExtents.y, +halfExtents.z, 1.0f ),
		D3DXVECTOR4( +halfExtents.x, +halfExtents.y, +halfExtents.z, 1.0f ),
		D3DXVECTOR4( +halfExtents.x, +halfExtents.y, -halfExtents.z, 1.0f ),
		D3DXVECTOR4( -halfExtents.x, +halfExtents.y, -halfExtents.z, 1.0f ),

		D3DXVECTOR4( -halfExtents.x, -halfExtents.y, +halfExtents.z, 1.0f ),
		D3DXVECTOR4( +halfExtents.x, -halfExtents.y, +halfExtents.z, 1.0f ),
		D3DXVECTOR4( +halfExtents.x, -halfExtents.y, -halfExtents.z, 1.0f ),
		D3DXVECTOR4( -halfExtents.x, -halfExtents.y, -halfExtents.z, 1.0f ),
	};

	unsigned int indices[24] =
	{
		0, 1,
		1, 2,
		2, 3,
		3, 0,

		4, 5,
		5, 6,
		6, 7,
		7, 4,

		0, 4,
		1, 5,
		2, 6,
		3, 7,
	};

	unsigned int indexCount = sizeof( indices ) / sizeof( unsigned int );

	Mix::Tool::Win32::Graphics::LINE_VERTEX lv;

	lv.color = m_Color;

	for( unsigned int i = 0; i < indexCount; i++ )
	{
		lv.pos = points[indices[i]];
		m_VertexList.push_back( lv );
	}

	Transform();
}

void LineHelper::AddCapsule( const Mix::Tool::Win32::Geometry::CAPSULE& capsule )
{
	D3DXMATRIX oldMat = GetMatrix();

	D3DXMATRIX rot0;
	D3DXMATRIX rot1;
	D3DXMATRIX tr0;
	D3DXMATRIX tr1;
	D3DXMATRIX temp;

	////////////////////////////////////////////////////////////////////////////////////////////////////

	switch( capsule.axisType )
	{
	case Mix::Tool::Win32::Geometry::CAPSULE::AXIS_X:
		::D3DXMatrixRotationZ( &rot0, D3DXToRadian( +90.0f ) );
		::D3DXMatrixRotationZ( &rot1, D3DXToRadian( -90.0f ) );
		::D3DXMatrixTranslation( &tr0, -capsule.halfLen + capsule.pos.x, capsule.pos.y, capsule.pos.z );
		::D3DXMatrixTranslation( &tr1, +capsule.halfLen + capsule.pos.x, capsule.pos.y, capsule.pos.z );
		break;

	case Mix::Tool::Win32::Geometry::CAPSULE::AXIS_Y:
		::D3DXMatrixIdentity( &rot0 );
		::D3DXMatrixRotationZ( &rot1, D3DXToRadian( 180.0f ) );
		::D3DXMatrixTranslation( &tr0, capsule.pos.x, +capsule.halfLen + capsule.pos.y, capsule.pos.z );
		::D3DXMatrixTranslation( &tr1, capsule.pos.x, -capsule.halfLen + capsule.pos.y, capsule.pos.z );
		break;

	case Mix::Tool::Win32::Geometry::CAPSULE::AXIS_Z:
		::D3DXMatrixRotationX( &rot0, D3DXToRadian( +90.0f ) );
		::D3DXMatrixRotationX( &rot1, D3DXToRadian( -90.0f ) );
		::D3DXMatrixTranslation( &tr0, capsule.pos.x, capsule.pos.y, +capsule.halfLen + capsule.pos.z );
		::D3DXMatrixTranslation( &tr1, capsule.pos.x, capsule.pos.y, -capsule.halfLen + capsule.pos.z );
		break;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////

	D3DXMatrixIdentity( &temp );
	D3DXMatrixScaling( &temp, capsule.radius, capsule.radius, capsule.radius );
	D3DXMatrixMultiply( &temp, &temp, &rot0 );
	D3DXMatrixMultiply( &temp, &temp, &tr0 );

	SetMatrix( temp * oldMat );
	AddPoints( &( m_HemisphereLineTable[0] ), m_HemisphereLineTable.size() );

	////////////////////////////////////////////////////////////////////////////////////////////////////

	D3DXMatrixIdentity( &temp );
	D3DXMatrixScaling( &temp, capsule.radius, capsule.radius, capsule.radius );
	D3DXMatrixMultiply( &temp, &temp, &rot1 );
	D3DXMatrixMultiply( &temp, &temp, &tr1 );

	SetMatrix( temp * oldMat );
	AddPoints( &( m_HemisphereLineTable[0] ), m_HemisphereLineTable.size() );

	////////////////////////////////////////////////////////////////////////////////////////////////////

	D3DXMATRIX pose;

	switch( capsule.axisType )
	{
	case Mix::Tool::Win32::Geometry::CAPSULE::AXIS_X:
		::D3DXMatrixRotationZ( &rot0, D3DXToRadian( 90.0f ) );
		break;
	case Mix::Tool::Win32::Geometry::CAPSULE::AXIS_Y:
		::D3DXMatrixIdentity( &rot0 );
		break;
	case Mix::Tool::Win32::Geometry::CAPSULE::AXIS_Z:
		::D3DXMatrixRotationX( &rot0, D3DXToRadian( 90.0f ) );
		break;
	}

	D3DXMatrixTranslation( &tr0, capsule.pos.x, capsule.pos.y, capsule.pos.z );

	D3DXMatrixScaling( &temp, capsule.radius, capsule.halfLen, capsule.radius );
	D3DXMatrixMultiply( &temp, &temp, &rot0 );
	D3DXMatrixMultiply( &temp, &temp, &tr0 );

	SetMatrix( temp * oldMat );
	AddPoints( &( m_CylinderLineTable[0] ), m_CylinderLineTable.size() );

	////////////////////////////////////////////////////////////////////////////////////////////////////

	SetMatrix( oldMat );
}

void LineHelper::AddSphere( const Mix::Tool::Win32::Geometry::SPHERE& sphere )
{
	D3DXMATRIX oldMat = GetMatrix();
	D3DXMATRIX temp;

	::D3DXMatrixScaling( &temp, sphere.radius, sphere.radius, sphere.radius );
	temp._41 = sphere.pos.x; temp._42 = sphere.pos.y; temp._43 = sphere.pos.z; temp._44 = 1.0f;

	SetMatrix( temp * oldMat );
	AddPoints( &( m_SphereLineTable[0] ), m_SphereLineTable.size() );

	SetMatrix( oldMat );
}

void LineHelper::AddAABB( const Mix::Tool::Win32::Geometry::AABB& aabb )
{
	static const unsigned int INDEX_TABLE[12][2] =
	{
		{ 0, 1, },
		{ 1, 2, },
		{ 2, 3, },
		{ 3, 0, },

		{ 4, 5, },
		{ 5, 6, },
		{ 6, 7, },
		{ 7, 4, },

		{ 0, 4, },
		{ 1, 5, },
		{ 2, 6, },
		{ 3, 7, },
	};

	D3DXVECTOR4 POINT_TABLE[8] =
	{
		D3DXVECTOR4( aabb.min.x, aabb.max.y, aabb.max.z, 1.0f ),
		D3DXVECTOR4( aabb.max.x, aabb.max.y, aabb.max.z, 1.0f ),
		D3DXVECTOR4( aabb.max.x, aabb.max.y, aabb.min.z, 1.0f ),
		D3DXVECTOR4( aabb.min.x, aabb.max.y, aabb.min.z, 1.0f ),
		D3DXVECTOR4( aabb.min.x, aabb.min.y, aabb.max.z, 1.0f ),
		D3DXVECTOR4( aabb.max.x, aabb.min.y, aabb.max.z, 1.0f ),
		D3DXVECTOR4( aabb.max.x, aabb.min.y, aabb.min.z, 1.0f ),
		D3DXVECTOR4( aabb.min.x, aabb.min.y, aabb.min.z, 1.0f ),
	};

	Mix::Tool::Win32::Graphics::LINE_VERTEX lv;

	lv.color = m_Color;

	for( unsigned int i = 0; i < 12; i++ )
	{
		lv.pos = POINT_TABLE[INDEX_TABLE[i][0]];
		m_VertexList.push_back( lv );

		lv.pos = POINT_TABLE[INDEX_TABLE[i][1]];
		m_VertexList.push_back( lv );
	}

	Transform();
}

void LineHelper::AddOBB( const Mix::Tool::Win32::Geometry::OBB& obb )
{
	static const unsigned int INDEX_TABLE[24] =
	{
		0, 1,
		1, 2,
		2, 3,
		3, 0,

		4, 5,
		5, 6,
		6, 7,
		7, 4,

		0, 4,
		1, 5,
		2, 6,
		3, 7,
	};

	D3DXVECTOR3 vx = obb.axis[0] * obb.len.x;
	D3DXVECTOR3 vy = obb.axis[1] * obb.len.y;
	D3DXVECTOR3 vz = obb.axis[2] * obb.len.z;

	D3DXVECTOR3 points[8] =
	{
		obb.pos - vx + vy + vz,
		obb.pos + vx + vy + vz,
		obb.pos + vx + vy - vz,
		obb.pos - vx + vy - vz,
		obb.pos - vx - vy + vz,
		obb.pos + vx - vy + vz,
		obb.pos + vx - vy - vz,
		obb.pos - vx - vy - vz,
	};

	Mix::Tool::Win32::Graphics::LINE_VERTEX lv;

	lv.color = m_Color;

	for( unsigned int i = 0; i < 24; i++ )
	{
		const D3DXVECTOR3& point = points[INDEX_TABLE[i]];

		lv.pos = D3DXVECTOR4( point.x, point.y, point.z, 1.0f );
		m_VertexList.push_back( lv );
	}

	Transform();
}

void LineHelper::AddAxis( const D3DXVECTOR3& pos, float scale )
{
	AddAxis( pos, D3DXVECTOR3( scale, scale, scale ) );
}

void LineHelper::AddAxis( const D3DXVECTOR3& pos, const D3DXVECTOR3& scale )
{
	Mix::Tool::Win32::Graphics::LINE_VERTEX lv;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// X軸
	////////////////////////////////////////////////////////////////////////////////////////////////////

	lv.color = D3DXVECTOR4( m_Color.x, 0.0f, 0.0f, m_Color.w );

	lv.pos = D3DXVECTOR4( pos.x, pos.y, pos.z, 1.0f );
	m_VertexList.push_back( lv );

	lv.pos = D3DXVECTOR4( pos.x + scale.x, pos.y, pos.z, 1.0f );
	m_VertexList.push_back( lv );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Y軸
	////////////////////////////////////////////////////////////////////////////////////////////////////

	lv.color = D3DXVECTOR4( 0.0f, m_Color.y, 0.0f, m_Color.w );

	lv.pos = D3DXVECTOR4( pos.x, pos.y, pos.z, 1.0f );
	m_VertexList.push_back( lv );

	lv.pos = D3DXVECTOR4( pos.x, pos.y + scale.y, pos.z, 1.0f );
	m_VertexList.push_back( lv );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Z軸
	////////////////////////////////////////////////////////////////////////////////////////////////////

	lv.color = D3DXVECTOR4( 0.0f, 0.0f, m_Color.z, m_Color.w );

	lv.pos = D3DXVECTOR4( pos.x, pos.y, pos.z, 1.0f );
	m_VertexList.push_back( lv );

	lv.pos = D3DXVECTOR4( pos.x, pos.y, pos.z + scale.z, 1.0f );
	m_VertexList.push_back( lv );

	////////////////////////////////////////////////////////////////////////////////////////////////////

	Transform( true );
}

void LineHelper::AddAxis( const D3DXVECTOR3& pos, const D3DXVECTOR3 axis[3], const D3DXVECTOR3& scale )
{
	Mix::Tool::Win32::Graphics::LINE_VERTEX lv;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// X軸
	////////////////////////////////////////////////////////////////////////////////////////////////////

	lv.color = D3DXVECTOR4( m_Color.x, 0.0f, 0.0f, m_Color.w );

	lv.pos = D3DXVECTOR4( pos.x, pos.y, pos.z, 1.0f );
	m_VertexList.push_back( lv );

	lv.pos = D3DXVECTOR4( pos.x + axis[0].x * scale.x, pos.y + axis[0].y * scale.x, pos.z + axis[0].z * scale.x, 1.0f );
	m_VertexList.push_back( lv );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Y軸
	////////////////////////////////////////////////////////////////////////////////////////////////////

	lv.color = D3DXVECTOR4( 0.0f, m_Color.y, 0.0f, m_Color.w );

	lv.pos = D3DXVECTOR4( pos.x, pos.y, pos.z, 1.0f );
	m_VertexList.push_back( lv );

	lv.pos = D3DXVECTOR4( pos.x + axis[1].x * scale.y, pos.y + axis[1].y * scale.y, pos.z + axis[1].z * scale.y, 1.0f );
	m_VertexList.push_back( lv );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Z軸
	////////////////////////////////////////////////////////////////////////////////////////////////////

	lv.color = D3DXVECTOR4( 0.0f, 0.0f, m_Color.z, m_Color.w );

	lv.pos = D3DXVECTOR4( pos.x, pos.y, pos.z, 1.0f );
	m_VertexList.push_back( lv );

	lv.pos = D3DXVECTOR4( pos.x + axis[2].x * scale.z, pos.y + axis[2].y * scale.z, pos.z + axis[2].z * scale.z, 1.0f );
	m_VertexList.push_back( lv );

	////////////////////////////////////////////////////////////////////////////////////////////////////

	Transform( true );
}

const std::vector<Mix::Tool::Win32::Graphics::LINE_VERTEX>& LineHelper::GetVertexList( void ) const
{
	return m_VertexList;
}

Mix::Tool::Win32::Object::TYPE LineHelper::GetType( void ) const
{
	return Mix::Tool::Win32::Object::GRAPHICS__LINE_HELPER;
}

void LineHelper::Transform( bool bExcludeScaing )
{
	if( ( m_VertexList.size() == 0 ) ||
		( m_VertexList.size() <= m_TransformStart ) )
	{
		return;
	}

	Mix::Tool::Win32::Graphics::LINE_VERTEX* pVertex = &( m_VertexList[m_TransformStart] );
	Mix::Tool::Win32::Graphics::LINE_VERTEX* pVertexLast = &( m_VertexList[m_VertexList.size() - 1] );

	if( bExcludeScaing == true )
	{
//		D3DXMATRIX mat = Mix::Tool::Win32::ExcludeScaling( m_Mat );
		D3DXMATRIX mat = Mix::Tool::Win32::Matrix_ExcludeScaling( m_Mat );

		while( pVertex <= pVertexLast )
		{
			D3DXVec4Transform( &( pVertex->pos ), &( pVertex->pos ), &mat );
			pVertex++;
		}
	}
	else
	{
		while( pVertex <= pVertexLast )
		{
			D3DXVec4Transform( &( pVertex->pos ), &( pVertex->pos ), &m_Mat );
			pVertex++;
		}
	}

	m_TransformStart = m_VertexList.size();
}

}}}}
