#include "Mix/Private/Scene/Common/Planter.h"

#include "Mix/Scene/IMaterial.h"
#include "Mix/Scene/IDefaultMaterial.h"

namespace Mix{ namespace Scene{ namespace Common{

////////////////////////////////////////////////////////////////////////////////////////////////////
// Planter : 定数
////////////////////////////////////////////////////////////////////////////////////////////////////

const wchar_t* Planter::FAILED_CREATE = L"プランターの作成に失敗";
const wchar_t* Planter::FAILED_SET_MATERIAL = L"プランターのマテリアルの設定に失敗";

const UInt32 Planter::DEF_STATUS = FlowerPackage::DAT_MAX;

////////////////////////////////////////////////////////////////////////////////////////////////////
// Planter
////////////////////////////////////////////////////////////////////////////////////////////////////

Planter* Planter::CreateInstance(	Mix::Scene::IFlowerPackage* pFlowerPackage,
									UInt32 numUnit, const Mix::Scene::IPlanter::UNIT* units,
									const Mix::Matrix4x4& worldMat,
									const wchar_t* pDebugName )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_SCENE, Planter, pFlowerPackage, numUnit, units, worldMat, pDebugName );
}

Planter::Planter(	Mix::Scene::IFlowerPackage* pFlowerPackage,
					UInt32 numUnit, const Mix::Scene::IPlanter::UNIT* units,
					const Mix::Matrix4x4& worldMat,
					const wchar_t* pDebugName ) :
m_pFlowerPackage( NULL ),
m_WorldMat( worldMat ),
m_Status( Planter::DEF_STATUS ),
m_pOctObj( NULL ),
m_bLocalLighting( False ),
m_bDraw( True ),
m_TRPriority( 0 ),
m_WCResult( Mix::Scene::WCR_FRONT )
{
	MIX_ASSERT( pFlowerPackage != NULL );

	const FlowerPackage::INTERNAL_MODEL* internalModels = NULL;

#ifdef _DEBUG
	m_DebugName = pDebugName;
#endif //_DEBUG

	// フラワーパッケージ

	MIX_ADD_REF( pFlowerPackage );
	m_pFlowerPackage = static_cast<Mix::Scene::Common::FlowerPackage*>( pFlowerPackage );
	m_pFlowerPackage->AttachPlanter( this );

	internalModels = m_pFlowerPackage->GetInternalModels();

	// ユニット、サブセットリストの作成

	m_UnitList.reserve( numUnit );
	m_SubsetList.reserve( numUnit );

	for( UInt32 i = 0; i < numUnit; i++ )
	{
		const IPlanter::UNIT& srcUnit = units[i];

		if( m_pFlowerPackage->GetModelCount() <= srcUnit.modelIndex )
		{
			continue;
		}

		Planter::INTERNAL_UNIT dstUnit;
		Mix::Scene::Common::WIDGET_SPHERE_SUBSET subset;

		// ユニット //

		dstUnit.pModel = &( internalModels[srcUnit.modelIndex] );
		dstUnit.src = srcUnit;
		dstUnit.worldMat = srcUnit.worldMat;

		m_UnitList.push_back( dstUnit );

		// サブセット //

		subset.refractClass = Mix::Scene::Common::RRC_UNKNOWN;
		subset.minZ = 0.0f;
		subset.maxZ = 0.0f;
		subset.quadCount = 0;
		subset.quads = NULL;
		subset.bounds.center = Mix::Vector3::Zero();
		subset.bounds.radius = 0.0f;

//		以下は FlashSubsets で設定される
//		  subset.priority;
//		  subset.pMaterial;

		m_SubsetList.push_back( subset );
	}

	FlashSubsets();

	UpdateUnitsTransform();

#ifdef _DEBUG
	Mix::StringW tempStr;
	tempStr.Sprintf( L"%s/Quads", MIX_SAFE_NAME( pDebugName ) );
	m_Quads.Initialize( Planter::QUADS_DEF_SIZE, Planter::QUADS_RESIZE_STEP, tempStr.GetConstPtr() );
#else //_DEBUG
	m_Quads.Initialize( Planter::QUADS_DEF_SIZE, Planter::QUADS_RESIZE_STEP );
#endif //_DEBUG

	UpdateQuads( FlowerPackage::DAT_NEAR );
}

Planter::~Planter( void )
{
	MIX_ASSERT( m_pOctObj == NULL );

	if( m_pFlowerPackage != NULL )
	{
		m_pFlowerPackage->DetachPlanter( this );
	}

	MIX_RELEASE( m_pFlowerPackage );
}

void Planter::UpdateUnitsTransform( void )
{
	if( m_UnitList.size() > 0 )
	{
		Planter::INTERNAL_UNIT* pUnit = &( m_UnitList[0] );
		Planter::INTERNAL_UNIT* pUnitEnd = pUnit + m_UnitList.size();

		while( pUnit != pUnitEnd )
		{
			pUnit->worldMat = pUnit->src.worldMat * m_WorldMat;
			pUnit++;
		}
	}
}

void Planter::UpdateQuads( void )
{
	m_Status = FlowerPackage::DAT_NEAR;

	UpdateQuads( m_Status );
}

void Planter::UpdateQuads( UInt32 index )
{
	MIX_ASSERT( m_UnitList.size() == m_SubsetList.size() );

	size_t numUnit = m_UnitList.size();

	m_Quads.Clear();

	if( ( numUnit == 0 ) || ( index >= FlowerPackage::DAT_MAX ) )
	{
		m_Bounds.min = m_WorldMat.GetTranslation();
		m_Bounds.max = m_Bounds.min;
		m_Bounds.ComputePoints();
		return;
	}

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

	Planter::INTERNAL_UNIT* pUnitBegin = &( m_UnitList[0] );
	Planter::INTERNAL_UNIT* pUnitEnd = pUnitBegin + numUnit;
	Planter::INTERNAL_UNIT* pUnit;

	Mix::Scene::Common::WIDGET_SPHERE_SUBSET* pSubsetBegin = &( m_SubsetList[0] );
	Mix::Scene::Common::WIDGET_SPHERE_SUBSET* pSubset;

	Mix::Vector3 unitMin( +MIX_FLOAT_MAX, +MIX_FLOAT_MAX, +MIX_FLOAT_MAX );
	Mix::Vector3 unitMax( -MIX_FLOAT_MAX, -MIX_FLOAT_MAX, -MIX_FLOAT_MAX );
	Mix::Vector3 aabbMin( +MIX_FLOAT_MAX, +MIX_FLOAT_MAX, +MIX_FLOAT_MAX );
	Mix::Vector3 aabbMax( -MIX_FLOAT_MAX, -MIX_FLOAT_MAX, -MIX_FLOAT_MAX );

	UInt32 i;
	UInt32 j;

	pUnit = pUnitBegin;
	pSubset = pSubsetBegin;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ユニット、サブセット( 一部 )の更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	while( pUnit != pUnitEnd )
	{
		const Mix::Matrix4x4& worldMat = pUnit->worldMat;
		const FlowerPackage::INTERNAL_MODEL* pModel = pUnit->pModel;
		const FlowerPackage::INTERNAL_MODEL_DATA& dat = pModel->dats[index];
		UInt32 numPoint = dat.numPoint;
		const Mix::Vector3* points = dat.points;

		Mix::Scene::Common::WIDGET_QUAD* pQuad;
		Mix::Vector3* quadPoints;
		Mix::Vector3 quadCenter;

		pUnit->quadStart = m_Quads.GetCount();

		unitMin.Set( +MIX_FLOAT_MAX, +MIX_FLOAT_MAX, +MIX_FLOAT_MAX );
		unitMax.Set( -MIX_FLOAT_MAX, -MIX_FLOAT_MAX, -MIX_FLOAT_MAX );

		for( i = 0; i < numPoint; i += 4 )
		{
			pQuad = m_Quads.Add();

			quadPoints = &( pQuad->points[0] );
			quadCenter = Mix::Vector3::Zero();

			for( j = 0; j < 4; j++ )
			{
				quadPoints[j] = worldMat * points[i + j];
				quadCenter += quadPoints[j];

				unitMin = Mix::Vector3::Min( unitMin, quadPoints[j] );
				unitMax = Mix::Vector3::Max( unitMax, quadPoints[j] );
			}

			pQuad->pos = quadCenter * 0.25f; // quadCenter / 4
			pQuad->localPoints = NULL;
//			pQuad->points;
			pQuad->dataSize = 28; // color( Mix::Vector4 ) + normal( Mix::Vector3 )
			pQuad->color.Set( 1.0f, 1.0f, 1.0f, 1.0f );
			pQuad->normal = worldMat * Mix::Vector3( 0.0f, 1.0f, 0.0f );
			pQuad->texCoords = &( dat.texCoords[( i < dat.numTexCoord )? 0 : 4] );
			pQuad->texWriteOffset = 40; // pos( Mix::Vector3 ) + color( Mix::Vector4 ) + normal( Mix::Vector3 )
		}

		aabbMin = Mix::Vector3::Min( aabbMin, unitMin );
		aabbMax = Mix::Vector3::Max( aabbMax, unitMax );

		pSubset->quadCount = numPoint >> 2;
		pSubset->bounds.center = ( unitMin + unitMax ) * 0.5f;
		pSubset->bounds.radius = ( unitMax - unitMin ).GetLengthF() * 0.5f;

		pSubset++;
		pUnit++;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// サブセットのクワッドのポインタを設定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	pUnit = pUnitBegin;
	pSubset = pSubsetBegin;

	while( pUnit != pUnitEnd )
	{
		pSubset->quads = m_Quads.GetBeginPtr() + pUnit->quadStart;

		pSubset++;
		pUnit++;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 境界を更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	m_Bounds.min = aabbMin;
	m_Bounds.max = aabbMax;
	m_Bounds.ComputePoints();
}

void Planter::FlashSubsets( void )
{
	Mix::Scene::IMaterial* pMaterial = m_pFlowerPackage->GetMaterialPtr();

	if( m_SubsetList.size() > 0 )
	{
		Mix::Scene::Common::WIDGET_SPHERE_SUBSET* pSubset = &( m_SubsetList[0] );
		Mix::Scene::Common::WIDGET_SPHERE_SUBSET* pSubsetEnd = pSubset + m_SubsetList.size();

		while( pSubset != pSubsetEnd )
		{
			pSubset->priority = m_TRPriority;
			pSubset->pMaterial = pMaterial;

			pSubset++;
		}
	}
}

void Planter::Attach( Mix::Scene::Common::PlanterObject* pOctObj )
{
	MIX_ASSERT( pOctObj != NULL );
	MIX_ASSERT( m_pOctObj == NULL );

	m_pOctObj = pOctObj;

	RendererObject::SetRendering( True );
}

void Planter::Detach( void )
{
	if( m_pOctObj != NULL )
	{
		m_pOctObj->Destroy();
		m_pOctObj = NULL;
	}

	RendererObject::SetRendering( False );
}

Boolean Planter::InternalRefresh( Float32 foStartZ, Float32 foMidZ, Float32 foEndZ, Float32 z )
{
	MIX_ASSERT( m_bDraw == True );

	UInt32 nextStatus;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ステータスを決める
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( foStartZ > z )
	{
		nextStatus = FlowerPackage::DAT_NEAR;
	}
	else if( foMidZ > z )
	{
		nextStatus = FlowerPackage::DAT_MID;
	}
	else if( foEndZ > z )
	{
		nextStatus = FlowerPackage::DAT_FAR;
	}
	else
	{
		nextStatus = FlowerPackage::DAT_MAX;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ステータスに応じて状態を変換
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( nextStatus != m_Status )
	{
		m_Status = nextStatus;
		UpdateQuads( m_Status );
	}

	return ( m_Quads.IsEmpty() == False );
}

UInt32 Planter::GetSubsetCount( void ) const
{
	return MIX_UIT_TO_UI32( m_SubsetList.size() );
}

Mix::Scene::Common::WIDGET_SPHERE_SUBSET* Planter::GetSubsets( void )
{
	return &( m_SubsetList[0] );
}

void Planter::OnMaterialChanged( void )
{
	FlashSubsets();
}

void Planter::OnFlowerModelModified( UInt32 index )
{
	UpdateQuads( m_Status );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IPlanter
////////////////////////////////////////////////////////////////////////////////////////////////////

const Mix::Matrix4x4& Planter::GetWorldMatrix( void ) const
{
	return m_WorldMat;
}

void Planter::SetWorldMatrix( const Mix::Matrix4x4& worldMat )
{
	m_WorldMat = worldMat;

	UpdateUnitsTransform();
	UpdateQuads();
}

UInt32 Planter::GetUnitCount( void ) const
{
	return MIX_UIT_TO_UI32( m_UnitList.size() );
}

const Mix::Scene::IPlanter::UNIT& Planter::GetUnit( UInt32 index ) const
{
	MIX_ASSERT( m_UnitList.size() > index );

	return m_UnitList[index].src;
}

Boolean Planter::SetUnit( UInt32 index, const Mix::Scene::IPlanter::UNIT& unit )
{
	if( ( m_UnitList.size() <= index ) ||
		( m_pFlowerPackage->GetModelCount() <= unit.modelIndex ) )
	{
		return False;
	}

	Planter::INTERNAL_UNIT* pInternalUnit = &( m_UnitList[index] );

	pInternalUnit->src = unit;
	pInternalUnit->worldMat = unit.worldMat * m_WorldMat;
	pInternalUnit->pModel = &( m_pFlowerPackage->GetInternalModels()[unit.modelIndex] );

	// クワッドを更新
	UpdateQuads();

	return True;
}

Boolean Planter::AddUnit( const Mix::Scene::IPlanter::UNIT& unit )
{
	if( m_pFlowerPackage->GetModelCount() <= unit.modelIndex )
	{
		return False;
	}

	Planter::INTERNAL_UNIT internalUnit;
	Mix::Scene::Common::WIDGET_SPHERE_SUBSET subset;

	//ユニットの追加

	internalUnit.src = unit;
	internalUnit.pModel = &( m_pFlowerPackage->GetInternalModels()[unit.modelIndex] );
	internalUnit.worldMat = unit.worldMat;
	internalUnit.quadStart = 0;

	m_UnitList.push_back( internalUnit );

	//サブセットの追加

	subset.refractClass = Mix::Scene::Common::RRC_UNKNOWN;
	subset.minZ = 0.0f;
	subset.maxZ = 0.0f;
	subset.quadCount = 0;
	subset.quads = NULL;
	subset.bounds.center = Mix::Vector3::Zero();
	subset.bounds.radius = 0.0f;
	subset.priority = m_TRPriority;
	subset.pMaterial = m_pFlowerPackage->GetMaterialPtr();

	m_SubsetList.push_back( subset );

	// クワッドを更新

	UpdateQuads();

	return True;
}

Boolean Planter::RemoveUnit( UInt32 index )
{
	if( m_UnitList.size() <= index )
	{
		return False;
	}

	//ユニットの削除
	m_UnitList.erase( m_UnitList.begin() + index );

	//サブセットの削除
	m_SubsetList.erase( m_SubsetList.begin() + index );

	// クワッドを更新
	UpdateQuads();

	return True;
}

void Planter::ClearUnits( void )
{
	// クリア
	m_UnitList.clear();
	m_SubsetList.clear();

	// クワッドを更新
	UpdateQuads();
}

void Planter::Refresh( void )
{
	if( m_pOctObj != NULL )
	{
		m_pOctObj->Refresh();
	}
}

const Mix::Geometry::AABB& Planter::GetBounds( void ) const
{
	return m_Bounds;
}

Boolean Planter::IsIgnored( void ) const
{
	return ( m_pOctObj != NULL )? m_pOctObj->IsIllegal() : True;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IWidget
////////////////////////////////////////////////////////////////////////////////////////////////////

Boolean Planter::CanLocalLighting( void ) const
{
	return ( ( m_pFlowerPackage->GetMaterialPtr()->IsLighting() == True ) && ( m_bLocalLighting == True ) );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IEntity
////////////////////////////////////////////////////////////////////////////////////////////////////

Boolean Planter::IsDraw( void ) const
{
	return m_bDraw;
}

void Planter::SetDraw( Boolean state )
{
	m_bDraw = state;
}

Boolean Planter::IsLocalLighting( void ) const
{
	return m_bLocalLighting;
}

void Planter::SetLocalLighting( Boolean state )
{
	if( m_bLocalLighting != state )
	{
		m_bLocalLighting = state;

		FlashSubsets();
	}
}

UInt32 Planter::GetTransparencyPriority( void ) const
{
	return m_TRPriority;
}

void Planter::SetTransparencyPriority( UInt32 priority )
{
	if( m_TRPriority != priority )
	{
		m_TRPriority = priority;

		FlashSubsets();
	}
}

Mix::Scene::WATER_CONTAINS_RESULT Planter::GetDefaultWaterContainsResult( void ) const
{
	return m_WCResult;
}

void Planter::SetDefaultWaterContainsResult( Mix::Scene::WATER_CONTAINS_RESULT result )
{
	m_WCResult = result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Scene::IRendererObject
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Scene::IRendererObject::TYPE Planter::GetType( void ) const
{
	return IRendererObject::PLANTER;
}

Boolean Planter::IsRendering( void ) const
{
	return RendererObject::IsRendering();
}

}}}
