#include "Mix/Private/Dynamics/ObjectContext.h"

#include "Mix/Dynamics/IObject.h"
#include "Mix/Dynamics/IObjectListener.h"
#include "Mix/Dynamics/ICompoundShape.h"
#include "Mix/Private/Dynamics/Shape.h"

namespace Mix{ namespace Dynamics{

////////////////////////////////////////////////////////////////////////////////////////////////////
// ObjectContext
////////////////////////////////////////////////////////////////////////////////////////////////////

ObjectContext::ObjectContext( Mix::Dynamics::IObject* pOwner, const wchar_t* pDebugName ) :
m_pOwner( pOwner ),
m_Material( Mix::Dynamics::DEF_MATERIAL )
{
	MIX_ASSERT( m_pOwner != NULL );
}

ObjectContext::~ObjectContext( void )
{
	if( m_ListenerList.size() > 0 )
	{
		for( ObjectContext::ListenerList::iterator it = m_ListenerList.begin(); it != m_ListenerList.end(); ++it )
		{
			Mix::Dynamics::IObjectListener* pListener = ( *it );
			MIX_RELEASE( pListener );
		}

		m_ListenerList.clear();
	}
}

Mix::Dynamics::IObject* ObjectContext::GetOwnerPtr( void ) const
{
	return m_pOwner;
}

Boolean ObjectContext::IsExistsListener( void ) const
{
	return ( m_ListenerList.size() > 0 );
}

Boolean ObjectContext::AddListener( Mix::Dynamics::IObjectListener* pListener )
{
	if( pListener == NULL )
	{
		return False;
	}

	if( std::find( m_ListenerList.begin(), m_ListenerList.end(), pListener ) == m_ListenerList.end() )
	{
		MIX_ADD_REF( pListener );
		m_ListenerList.push_back( pListener );
	}
	else
	{
		return False;
	}

	return True;
}

void ObjectContext::RemoveListener( Mix::Dynamics::IObjectListener* pListener )
{
	if( pListener == NULL )
	{
		return;
	}

	ObjectContext::ListenerList::iterator it = std::find( m_ListenerList.begin(), m_ListenerList.end(), pListener );
	if( it != m_ListenerList.end() )
	{
		m_ListenerList.erase( it );
		MIX_RELEASE( pListener );
	}
}

void ObjectContext::NotifyAddedToWorld( Mix::Dynamics::IWorld* pWorld )
{
	ObjectContext::ListenerList::iterator it_begin = m_ListenerList.begin();
	ObjectContext::ListenerList::iterator it_end = m_ListenerList.end();
	ObjectContext::ListenerList::iterator it;

	for( it = it_begin; it != it_end; ++it )
	{
		( *it )->OnAddedToWorld( m_pOwner, pWorld );
	}
}

void ObjectContext::NotifyRemovedFromWorld( Mix::Dynamics::IWorld* pWorld )
{
	ObjectContext::ListenerList::iterator it_begin = m_ListenerList.begin();
	ObjectContext::ListenerList::iterator it_end = m_ListenerList.end();
	ObjectContext::ListenerList::iterator it;

	for( it = it_begin; it != it_end; ++it )
	{
		( *it )->OnRemovedFromWorld( m_pOwner, pWorld );
	}
}

void ObjectContext::NotifyContact( const Mix::Dynamics::MANIFOLD& manifold )
{
	ObjectContext::ListenerList::iterator it_begin = m_ListenerList.begin();
	ObjectContext::ListenerList::iterator it_end = m_ListenerList.end();
	ObjectContext::ListenerList::iterator it;

	for( it = it_begin; it != it_end; ++it )
	{
		( *it )->OnContact( m_pOwner, manifold );
	}
}

const Mix::Dynamics::MATERIAL& ObjectContext::GetMaterial( void ) const
{
	return m_Material;
}

void ObjectContext::SetMaterial( const Mix::Dynamics::MATERIAL& material )
{
	m_Material = material;
}

const Mix::Dynamics::MATERIAL& ObjectContext::QueryMaterial( UInt32 partIndex, UInt32 polygonIndex ) const
{
	return m_Material;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// StaticMeshContext
////////////////////////////////////////////////////////////////////////////////////////////////////

StaticMeshContext::StaticMeshContext( Mix::Dynamics::IObject* pOwner, const wchar_t* pDebugName ) : ObjectContext( pOwner, pDebugName ),
m_TotalPolygonCount( 0 )
{
}

StaticMeshContext::~StaticMeshContext( void )
{
}

void StaticMeshContext::InitializeParts( UInt32 count )
{
	MIX_ASSERT( m_Parts.size() == 0 );
	m_Parts.resize( count );
}

void StaticMeshContext::InitializePolygonMaterialIndices( UInt32 partIndex, UInt32 polygonCount )
{
	MIX_ASSERT( m_Parts.size() > partIndex );

	if( polygonCount > 0 )
	{
		m_Parts[partIndex].resize( polygonCount, 0 );
	}
}

void StaticMeshContext::InitializeMaterials( UInt32 count )
{
	MIX_ASSERT( m_Materials.size() == 0 );
	m_Materials.resize( count );
}

void StaticMeshContext::InitializeFinish( void )
{
	for( UInt32 i = 0; i < m_Parts.size(); i++ )
	{
		m_TotalPolygonCount += MIX_UIT_TO_UI32( m_Parts[i].size() );
	}
}

UInt32 StaticMeshContext::GetPartCount( void ) const
{
	return MIX_UIT_TO_UI32( m_Parts.size() );
}

UInt32 StaticMeshContext::GetPolygonCount( UInt32 partIndex ) const
{
	if( m_Parts.size() <= partIndex )
	{
		return 0;
	}

	return MIX_UIT_TO_UI32( m_Parts[partIndex].size() );
}

UInt32 StaticMeshContext::GetTotalPolygonCount( void ) const
{
	return m_TotalPolygonCount;
}

UInt32 StaticMeshContext::GetLocalMaterialCount( void ) const
{
	return MIX_UIT_TO_UI32( m_Materials.size() );
}

UInt32 StaticMeshContext::GetMaterialIndex( UInt32 partIndex, UInt32 polygonIndex ) const
{
	if( ( m_Parts.size() <= partIndex ) ||
		( m_Parts[partIndex].size() <= polygonIndex ) )
	{
		return 0;
	}

	return m_Parts[partIndex][polygonIndex];
}

Boolean StaticMeshContext::SetMaterialIndex( UInt32 partIndex, UInt32 polygonIdnex, UInt32 materialIndex )
{
	if( ( m_Parts.size() <= partIndex ) ||
		( m_Parts[partIndex].size() <= polygonIdnex ) ||
		( m_Materials.size() <= materialIndex ) )
	{
		return False;
	}

	m_Parts[partIndex][polygonIdnex] = materialIndex;

	return True;
}

UInt32 StaticMeshContext::LockMaterialIndices( UInt32 partIndex, UInt32 polygonStartIndex, UInt32 polygonCount, UInt32** ppMaterialIndices )
{
	if( m_Parts.size() <= partIndex )
	{
		return 0;
	}

	UInt32 count = MIX_UIT_TO_UI32( m_Parts[partIndex].size() );

	if( ( count == 0 ) ||
		( polygonCount == 0 ) ||
		( count <= polygonStartIndex ) )
	{
		return 0;
	}

	( *ppMaterialIndices ) = &( m_Parts[partIndex][polygonStartIndex] );

	if( count < ( polygonStartIndex + polygonCount ) )
	{
		polygonCount = count - polygonStartIndex;
	}

	return polygonCount;
}

const Mix::Dynamics::MATERIAL& StaticMeshContext::GetLocalMaterial( UInt32 index )
{
	return ( m_Materials.size() > index )? m_Materials[index] : m_Material;
}

Boolean StaticMeshContext::GetLocalMaterial( UInt32 index, Mix::Dynamics::MATERIAL& material )
{
	if( m_Materials.size() > index )
	{
		material = m_Materials[index];
	}
	else
	{
		return False;
	}

	return True;
}

Boolean StaticMeshContext::SetLocalMaterial( UInt32 index, const Mix::Dynamics::MATERIAL& material )
{
	if( m_Materials.size() <= index )
	{
		return False;
	}

	m_Materials[index] = material;

	return True;
}

UInt32 StaticMeshContext::LockLocalMaterials( UInt32 startIndex, UInt32 count, Mix::Dynamics::MATERIAL** ppMaterials )
{
	UInt32 materialCount = MIX_UIT_TO_UI32( m_Materials.size() );

	if( ( materialCount == 0 ) ||
		( count == 0 ) ||
		( materialCount <= startIndex ) )
	{
		return 0;
	}

	( *ppMaterials ) = &( m_Materials[startIndex] );

	if( materialCount < ( startIndex + count ) )
	{
		count = materialCount - startIndex;
	}

	return count;
}

const Mix::Dynamics::MATERIAL& StaticMeshContext::QueryMaterial( UInt32 partIndex, UInt32 polygonIndex ) const
{
	if( ( m_Parts.size() <= partIndex ) ||
		( m_Parts[partIndex].size() <= polygonIndex ) )
	{
		return m_Material;
	}

	UInt32 materialIndex = m_Parts[partIndex][polygonIndex];

	if( m_Materials.size() <= materialIndex )
	{
		return m_Material;
	}

	return m_Materials[materialIndex];
}

}}
