//////////////////////////////////////////////////////////////////////////////
//
// Tools_EntityNode_CoverpointNode.cpp
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

// core
#include "Core/Asura.h"

#ifdef ASURA_USE_AXON

// header
#include "Entity/Locations/OrientedNode/CoverpointNode/Tools_EntityNode_CoverpointNode.h"

// asura
#include "Camera/Asura_Camera.h"
#include "Core/Asura_List.h"
#include "Core/Asura_Timers.h"
#include "Core/Asura_UserInput.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Overlay.h"
#include "Physics/Asura_Physics_RigidBody.h"
#include "Rendering/Asura_Render.h"

// tools
#include "Entity/Tools_Entity_Data.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Misc/Tools_LOS.h"
#include "Misc/Tools_Timed_Messages.h"

// S H A R E D _ P A R A M S /////////////////////////////////////////////////

TOOLS_DECLARE_SHARED_PARAM( bool, uTOOLS_SHAREDPARAMHASH_COVERPOINT_LOWCOVER, Asura_Coverpoint_LowCover );
TOOLS_DECLARE_SHARED_PARAM( float, uTOOLS_SHAREDPARAMHASH_COVERPOINT_MINRANGE, Asura_Coverpoint_MinRange );
TOOLS_DECLARE_SHARED_PARAM( float, uTOOLS_SHAREDPARAMHASH_COVERPOINT_MAXRANGE, Asura_Coverpoint_MaxRange );

//////////////////////////////////////////////////////////////////////////////
//
// Tools_Object_CoverpointEmergePoint
//
//////////////////////////////////////////////////////////////////////////////

Tools_Object_CoverpointEmergePoint::Tools_Object_CoverpointEmergePoint()
: m_pxMyCoverpoint( NULL ), m_pxPosition( NULL ), m_uIndex( 0 ), m_pxNext( NULL )
{
    SetMesh( "ToolsShape_CutsceneTrackPoint" );
	SetDistantIconTexture("\\tools\\ObjectIcons\\Entity.tga");

}

Tools_Object_CoverpointEmergePoint::Tools_Object_CoverpointEmergePoint( const Tools_Object_CoverpointEmergePoint& xRhs )
: m_pxMyCoverpoint( xRhs.m_pxMyCoverpoint ), m_pxPosition( xRhs.m_pxPosition ), m_uIndex( xRhs.m_uIndex ), m_pxNext( NULL )
{
    SetMesh( "ToolsShape_CutsceneTrackPoint" );
	SetDistantIconTexture("\\tools\\ObjectIcons\\Entity.tga");
}

void Tools_Object_CoverpointEmergePoint::UpdatePositionInAsuraData( bool bUpdateLinkedObjects )
{
    PARENT::UpdatePositionInAsuraData( bUpdateLinkedObjects );

    // Update our game position from our position.
    *m_pxPosition = Position;
    m_pxMyCoverpoint->SetRequiresValidation();
}

void Tools_Object_CoverpointEmergePoint::Destroy()
{
    m_pxMyCoverpoint->DestroyEmergePoint( *this );
    m_pxMyCoverpoint->SetRequiresValidation();
    Tools_Object_System::SelectObject( m_pxMyCoverpoint );
    PARENT::Destroy();
}

bool Tools_Object_CoverpointEmergePoint::IsHidden()
{
	return m_pxMyCoverpoint->IsHidden();
}

void Tools_Object_CoverpointEmergePoint::SetOwnerCoverpoint( Tools_EntityNode_CoverpointNode* pxMyCoverpoint )
{
    m_pxMyCoverpoint = pxMyCoverpoint;
}

//////////////////////////////////////////////////////////////////////////////
//
// Tools_EntityNode_CoverpointNode
//
//////////////////////////////////////////////////////////////////////////////

Asura_List< Asura_Physics_RigidBodyInstance > Tools_EntityNode_CoverpointNode::s_xRigidBodyIgnoreList;
Asura_List< Asura_Physics_RigidBodyInstance > Tools_EntityNode_CoverpointNode::s_xRigidBodyNonIgnoreList;

Tools_EntityNode_CoverpointNode* Tools_EntityNode_CoverpointNode::s_pxThisToolsCoverpointNode = NULL;

Tools_EntityNode_CoverpointNode::Tools_EntityNode_CoverpointNode( Asura_Entity* pxEntity )
: PARENT( pxEntity )
, m_pxEmergePoints( NULL )
, m_bRequiresValidation( false )
, m_bChanged( false )
, m_bIsLowCover( false )
, m_fCoverAngle( 90.0f )
, m_fMinCoverRange( 0.f )
, m_fMaxCoverRange( 40.0f )
, m_xPrevPosition( Asura_Maths::ZeroVector3 )
, m_xPrevOrientation( Asura_Maths::IdentityMatrix3x3 )
, m_pxGameEntity( static_cast< Asura_ServerNode_Coverpoint* >( Asura_ServerEntity_Node::GetList()->FindEntity( pxEntity->GetGuid() ) ) )
{
	// set tools type and mesh
	Type = TOT_Entity_CoverpointNode;

    SetMesh( "ToolsShape_Coverpoint" );

    if ( m_pxGameEntity )
    {
        m_bIsLowCover    = m_pxGameEntity->m_bIsLowCover;
        m_fCoverAngle    = m_pxGameEntity->m_fCoverAngle;
		m_fMinCoverRange = m_pxGameEntity->m_fMinCoverRange;
		m_fMaxCoverRange = m_pxGameEntity->m_fMaxCoverRange;
        m_xPrevPosition  = m_pxGameEntity->GetPosition();
        m_pxGameEntity->GetOrientation( m_xPrevOrientation );

        for ( u_int u = 0; u < Asura_ServerNode_Coverpoint::MAX_NUM_EMERGE_POINTS; ++u )
        {
            if ( !m_pxGameEntity->m_aucEmergePointFlags[ u ].m_bExists )
            {
                continue;
            }

            Tools_Object_CoverpointEmergePoint* pxEmergePoint = new Tools_Object_CoverpointEmergePoint();
            pxEmergePoint->SetOwnerCoverpoint( this );
            pxEmergePoint->SetPositionPointer( &m_pxGameEntity->m_axEmergePoint[ u ].m_xPosition );
			m_pxGameEntity->m_axEmergePoint[u].m_xOrientation.ToMatrix(pxEmergePoint->Orientation);
            pxEmergePoint->SetIndex( u );
            pxEmergePoint->SetNext( m_pxEmergePoints );
            m_pxEmergePoints = pxEmergePoint;
        }
    }
}

Tools_EntityNode_CoverpointNode::~Tools_EntityNode_CoverpointNode()
{
    // Delete our emerge points.
    while ( m_pxEmergePoints )
    {
        Tools_Object_CoverpointEmergePoint* pxNext = m_pxEmergePoints->GetNext();
        Tools_Object_System::DeleteObject( m_pxEmergePoints );
        m_pxEmergePoints = pxNext;
    }
}

void Tools_EntityNode_CoverpointNode::AddOverlayControls(Asura_Overlay* pxNewOverlay, Asura_Vector_2& xOffset)
{
	PARENT::AddOverlayControls(pxNewOverlay, xOffset);
	xOffset.y += Asura_Dialog::LargeSpacing() - Asura_Dialog::DefaultSpacing();

	Asura_Dialog* pxNewDialog;

	// Angle
	pxNewDialog = new Asura_Numerical_Dialog( "Angle", xOffset, &m_fCoverAngle, 0.0f, 180.0f, 1.0f, "%.1f", &m_bChanged );
	pxNewDialog->SetHelpText("Angle that this point provides cover over.");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();
	
	// Min Range
	pxNewDialog = new Asura_Numerical_Dialog( "Min Range", xOffset, &g_xParam_Asura_Coverpoint_MinRange, 0.0f, 9999.9f, 1.0f, "%.1f" );
	pxNewDialog->SetHelpText("Min Range that this point provides cover over.");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	// Max Range
	pxNewDialog = new Asura_Numerical_Dialog( "Max Range", xOffset, &g_xParam_Asura_Coverpoint_MaxRange, 0.0f, 9999.0f, 1.0f, "%.1f" );
	pxNewDialog->SetHelpText("Max Range that this point provides cover over.");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	// Low Cover
    pxNewDialog = new Asura_SharedTickbox_Dialog( "Low Cover", xOffset, &g_xParam_Asura_Coverpoint_LowCover );
    pxNewDialog->SetHelpText("Whether or not the NPC should crouch when using this cover.");
    pxNewOverlay->AddDialog(pxNewDialog);
    xOffset.y += Asura_Dialog::DefaultSpacing();

    // Emerge points.
    pxNewDialog = new Asura_Function_Dialog( "Add (E)merge Point", xOffset, AddEmergePoint, &m_bChanged, CanAddEmergePoint );
    pxNewDialog->SetHelpText("Adds a new emerge point.");
    pxNewOverlay->AddDialog(pxNewDialog);
    xOffset.y += Asura_Dialog::LargeSpacing();

	pxNewDialog = new Asura_Function_Dialog( "Validate All Coverpoints", xOffset, CheckCoverpointsForValidity );
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();
}

bool Tools_EntityNode_CoverpointNode::IsBaseOrDerivativeOf( int iToolsObjectType ) const
{
    if ( iToolsObjectType == TOT_Entity_CoverpointNode )
    {
        return true;
    }
    else
    {
        return PARENT::IsBaseOrDerivativeOf( iToolsObjectType );
    }
}

void Tools_EntityNode_CoverpointNode::OnSelection()
{
	s_pxThisToolsCoverpointNode = this;
    PARENT::OnSelection();
}

void Tools_EntityNode_CoverpointNode::OnDeselection()
{
    PARENT::OnDeselection();
	s_pxThisToolsCoverpointNode = NULL;
}

void Tools_EntityNode_CoverpointNode::Render()
{
    extern void Render_Sphere( const Asura_Vector_3& xCentre, float fRadius, int iColour );

	PARENT::Render();

	bool bRenderEverything = IsRootObjectGroupSelected();
    
    if(bRenderEverything)
	{
    	unsigned int	uColour = 0xffffffff;
    	float			fCoverAngle = Asura_Maths::DegToRad( m_fCoverAngle );

    	const float fLowOffset  = m_bIsLowCover ? -0.4f : -1.0f;
    	const float fHighOffset = m_bIsLowCover ?  0.4f :  1.0f;

    	for (float fOffset=fLowOffset; fOffset<fHighOffset; fOffset+=0.2f)
    	{
    	    Asura_Vector_3 xPos = Position + Asura_Vector_3(0.f, fOffset, 0.f);

    	    // draw in lines representing the cover angle
    	    Asura_Vector_3 xDirection;
    	    xDirection.x = Orientation.e13;
    	    xDirection.y = Orientation.e23;
    	    xDirection.z = Orientation.e33;

    	    Asura_Vector_3 xYAxis;
    	    xYAxis.x = Orientation.e12;
    	    xYAxis.y = Orientation.e22;
    	    xYAxis.z = Orientation.e32;

    	    Asura_Quat xQuat;

    	    // clockwise
    	    xQuat.FromRotation( fCoverAngle * 0.5f, xYAxis );
    	    Asura_Vector_3 xClockwise = xQuat.RotateVector( xDirection );

    	    // anticlockwise
    	    xQuat.FromRotation( -fCoverAngle * 0.5f, xYAxis );
    	    Asura_Vector_3 xAnticlockwise = xQuat.RotateVector( xDirection );

			Asura_Vector_3		xClockwiseStart;
			Asura_Vector_3		xAnticlockwiseStart;

    	    if ( IsSelected() )
    	    {
				xClockwiseStart = xClockwise;
				xAnticlockwiseStart = xAnticlockwise;

				xClockwiseStart		*= m_fMinCoverRange;
				xAnticlockwiseStart *= m_fMinCoverRange;

    	        xClockwise			*= m_fMaxCoverRange;
    	        xAnticlockwise		*= m_fMaxCoverRange;
    	    }
			else
			{
				xClockwiseStart = Asura_Vector_3( 0.f, 0.f, 0.f );
				xAnticlockwiseStart = Asura_Vector_3( 0.f, 0.f, 0.f );
			}

    	    Asura_Render::RenderLine( xPos + xClockwiseStart, xPos + xClockwise, uColour );
    	    Asura_Render::RenderLine( xPos + xAnticlockwiseStart, xPos + xAnticlockwise, uColour );

    	    // show the cover range by drawing an arc
    	    static const int s_iNumIterations = 12;

    	    if ( IsSelected() )
    	    {
    	        Asura_Vector_3 xPrevVector = xAnticlockwise;
    	        Asura_Vector_3 xNextVector;

    	        float          fCurrentAngle = fCoverAngle * -0.5f;

    	        for ( int i = 1; i <= s_iNumIterations; ++i )
    	        {
    	            xQuat.FromRotation( fCurrentAngle + ( ( i * 1.0f / static_cast< float >( s_iNumIterations ) ) * fCoverAngle ), xYAxis );
    	            xNextVector = xQuat.RotateVector( xDirection );

    	            xNextVector *= m_fMaxCoverRange;

    	            Asura_Render::RenderLine( xPos + xPrevVector, xPos + xNextVector, uColour );

    	            xPrevVector = xNextVector;
    	        }

				if ( m_fMinCoverRange > 0.f )
				{
					xPrevVector = xAnticlockwiseStart;
					fCurrentAngle = fCoverAngle * -0.5f;

					for ( int i = 1; i <= s_iNumIterations; ++i )
					{
						xQuat.FromRotation( fCurrentAngle + ( ( i * 1.0f / static_cast< float >( s_iNumIterations ) ) * fCoverAngle ), xYAxis );
						xNextVector = xQuat.RotateVector( xDirection );

						xNextVector *= m_fMinCoverRange;

						Asura_Render::RenderLine( xPos + xPrevVector, xPos + xNextVector, uColour );

						xPrevVector = xNextVector;
					}
				}
    	    }
    	}
	}

   	if ( m_pxGameEntity )
	{
		if ( RequiresValidation() )
		{
			// Render a yellow sphere around us.
			Render_Sphere( m_pxGameEntity->GetPosition(), 0.75f, 0xffffff00 );
		}
   	    else if ( !m_pxGameEntity->IsValid() )
   	    {
   	        // Render a red sphere around us.
   	        Render_Sphere( m_pxGameEntity->GetPosition(), 0.75f, 0xffff0000 );
   	    }
   	    else if ( IsSelected() )
   	    {
   	        // Render a green sphere around us showing how far away from stuff it should be put.
   	        Render_Sphere( m_pxGameEntity->GetPosition(), 0.75f, 0xff00ff00 );
   	    }
   	}

    if(bRenderEverything)
    {
    	// Render a line to our emerge points.
    	Tools_Object_CoverpointEmergePoint* pxEmergePoint = m_pxEmergePoints;    
    	while ( pxEmergePoint )
    	{
    	    Asura_Render::RenderLine( pxEmergePoint->Position, Position, 0xffffffff );

    	    // If the emerge point is invalid then render a red sphere around it.
    	    if ( !m_pxGameEntity->m_aucEmergePointFlags[ pxEmergePoint->GetIndex() ].m_bIsValid )
    	    {
    	        Render_Sphere( pxEmergePoint->Position, 0.25f, 0xffff0000 );
    	    }

    	    pxEmergePoint = pxEmergePoint->GetNext();
    	}
	}
}

void Tools_EntityNode_CoverpointNode::UpdatePositionInAsuraData( bool bUpdateLinkedObjects )
{
    PARENT::UpdatePositionInAsuraData( bUpdateLinkedObjects );

    const Asura_Vector_3 xDisplacement = Position - m_xPrevPosition;

    // Apply the new movement and rotation to the emerge points that are not currently selected.
    Tools_Object_CoverpointEmergePoint* pxEmergePoint = m_pxEmergePoints;
    while ( pxEmergePoint )
    {
        if ( !pxEmergePoint->IsSelected() && !pxEmergePoint->IsGroupSelected() )
        {
            pxEmergePoint->Position += xDisplacement;

            Asura_Vector_3 xToEmergePoint = pxEmergePoint->Position - Position;
            xToEmergePoint = m_xPrevOrientation.ApplyTranspose( xToEmergePoint );
            xToEmergePoint = xToEmergePoint * Orientation;
            pxEmergePoint->Position = Position + xToEmergePoint;
            pxEmergePoint->UpdatePositionInAsuraData( bUpdateLinkedObjects );
        }

        pxEmergePoint = pxEmergePoint->GetNext();
    }

    m_xPrevPosition    = Position;
    m_xPrevOrientation = Orientation;

	SetRequiresValidation();
}

void Tools_EntityNode_CoverpointNode::Update()
{
    PARENT::Update();

    // So these guys update correctly when part of a template we use this test.
    if ( IsRootObjectGroupSelected() )
    {
        // Set the angles of the emerge points (not in while loop because it gets the latest one first).
        Tools_Object_CoverpointEmergePoint* pxEmergePoint = m_pxEmergePoints;
        if ( pxEmergePoint )
        {
            m_pxGameEntity->m_axEmergePoint[0].m_xOrientation.FromMatrix(pxEmergePoint->Orientation);
            m_pxGameEntity->m_axEmergePoint[1].m_xOrientation.FromMatrix(pxEmergePoint->Orientation);
            pxEmergePoint = pxEmergePoint->GetNext();
        }
        if ( pxEmergePoint )
        {
            m_pxGameEntity->m_axEmergePoint[0].m_xOrientation.FromMatrix(pxEmergePoint->Orientation);
        }

        if ( m_bChanged )
        {
            m_bChanged = false;
			SetRequiresValidation();

            if ( m_pxGameEntity )
            {
                m_pxGameEntity->m_bIsLowCover = m_bIsLowCover;
                m_pxGameEntity->m_fCoverAngle = m_fCoverAngle;
                m_pxGameEntity->m_fMinCoverRange = m_fMinCoverRange;
                m_pxGameEntity->m_fMaxCoverRange = m_fMaxCoverRange;

				DeleteOverlay();
				CreateOverlay();

                SetEntityModified();
            }
        }
    }
}

void Tools_EntityNode_CoverpointNode::UpdateWhileSelected()
{
    PARENT::UpdateWhileSelected();

    if ( Asura_UserInput::DebouncedKeyPressed( 'e' ) && CanAddEmergePoint() )
    {
        // Add an emerge point under the cursor.
        int iPolyIndex, iModuleIndex;
        Asura_Vector_3 xPosition;
        if ( Tools_LOS::FindPolyAndPositionUnderCursor( iPolyIndex, iModuleIndex, xPosition ) )
        {
            ThisAddEmergePoint( &xPosition );
        }
    }
}

bool Tools_EntityNode_CoverpointNode::GetParameter( Asura_Hash_ID uParamID, Tools_Parameter* pxParam ) const
{
	switch ( uParamID )
	{
		case g_uHash_Asura_Coverpoint_LowCover:
			pxParam->SetBool( m_bIsLowCover );
			break;

		case g_uHash_Asura_Coverpoint_MinRange:
			pxParam->SetFloat( m_fMinCoverRange );
			break;

		case g_uHash_Asura_Coverpoint_MaxRange:
			pxParam->SetFloat( m_fMaxCoverRange );
			break;

		default:
			return PARENT::GetParameter( uParamID, pxParam );
	}

	return true;
}

bool Tools_EntityNode_CoverpointNode::SetParameter( Asura_Hash_ID uParamID, const Tools_Parameter* pxParam )
{
	if ( !m_pxGameEntity ) { return false; }

	switch ( uParamID )
	{
		case g_uHash_Asura_Coverpoint_LowCover:
			m_bIsLowCover = pxParam->GetBool();
			m_pxGameEntity->m_bIsLowCover = pxParam->GetBool();
			SetRequiresValidation();
			break;

		case g_uHash_Asura_Coverpoint_MinRange:
			m_fMinCoverRange = pxParam->GetFloat();
			m_pxGameEntity->m_fMinCoverRange = pxParam->GetFloat();
			break;

		case g_uHash_Asura_Coverpoint_MaxRange:
			m_fMaxCoverRange = pxParam->GetFloat();
			m_pxGameEntity->m_fMaxCoverRange = pxParam->GetFloat();
			break;

		default:
			return PARENT::SetParameter( uParamID, pxParam );
	}

	SetEntityModified();
	return true;
}

void Tools_EntityNode_CoverpointNode::Copy( const Tools_Object* pxObject )
{
    // Copy parent data.
    PARENT::Copy( pxObject );

    // Copy our data.
    const Tools_EntityNode_CoverpointNode* pxFromNodeObject = static_cast< const Tools_EntityNode_CoverpointNode* >( pxObject );
    m_bChanged    = pxFromNodeObject->m_bChanged;
    m_bIsLowCover = pxFromNodeObject->m_bIsLowCover;
    m_fCoverAngle = pxFromNodeObject->m_fCoverAngle;
	m_fMinCoverRange = pxFromNodeObject->m_fMinCoverRange;
    m_fMaxCoverRange = pxFromNodeObject->m_fMaxCoverRange;

    // Destroy our current emerge points.
	DeleteAllEmergePoints();

    // Copy the engine entity's data across and create our new emerge points.
    const Asura_ServerNode_Coverpoint* pxFromEntity = static_cast< const Asura_ServerNode_Coverpoint* >( pxFromNodeObject->Entity );
    m_pxGameEntity->m_fCosHalfFOV             = pxFromEntity->m_fCosHalfFOV;
    m_pxGameEntity->m_fCoverAngle             = pxFromEntity->m_fCoverAngle;
	m_pxGameEntity->m_fMinCoverRange		  = pxFromEntity->m_fMinCoverRange;
    m_pxGameEntity->m_fMaxCoverRange          = pxFromEntity->m_fMaxCoverRange;
    m_pxGameEntity->m_uCoverpointDataBitfield = pxFromEntity->m_uCoverpointDataBitfield;
    for ( u_int u = 0; u < Asura_ServerNode_Coverpoint::MAX_NUM_EMERGE_POINTS; ++u )
    {
		Asura_Matrix_3x3 xOrientation; pxFromEntity->GetOrientation(xOrientation);
		m_pxGameEntity->m_axEmergePoint[ u ].m_xPosition    = xOrientation.ApplyTranspose(pxFromEntity->m_axEmergePoint[ u ].m_xPosition - pxFromEntity->GetPosition());
		m_pxGameEntity->m_axEmergePoint[ u ].m_xOrientation = pxFromEntity->m_axEmergePoint[ u ].m_xOrientation;
        m_pxGameEntity->m_aucEmergePointFlags[ u ] = pxFromEntity->m_aucEmergePointFlags[ u ];

        if ( m_pxGameEntity->m_aucEmergePointFlags[ u ].m_bExists )
        {
            Tools_Object_CoverpointEmergePoint* pxNewEmergeObject = new Tools_Object_CoverpointEmergePoint();
            pxNewEmergeObject->SetOwnerCoverpoint( this );
            pxNewEmergeObject->SetPositionPointer( &m_pxGameEntity->m_axEmergePoint[ u ].m_xPosition );
			m_pxGameEntity->m_axEmergePoint[ u ].m_xOrientation.ToMatrix(pxNewEmergeObject->Orientation);
            pxNewEmergeObject->SetIndex( u );
            pxNewEmergeObject->SetNext( m_pxEmergePoints );
            m_pxEmergePoints = pxNewEmergeObject;
        }
    }

	SetRequiresValidation();
}

Tools_Object* Tools_EntityNode_CoverpointNode::CreateObjectOfThisType() const
{
    return CreateAndReturn();
}

void Tools_EntityNode_CoverpointNode::Create()
{
    CreateAndReturn();
}

Tools_EntityNode_CoverpointNode* Tools_EntityNode_CoverpointNode::CreateAndReturn()
{
    // Get a new GUID
    const Asura_Guid uGuid = Asura_Entity_System::GetCreatedByToolsGuid();

    // Set up tools data and auto-create a unique name (prompt for new name if there is a clash).
    Tools_Entity_Data* pxToolsData = CreateToolsDataWithDefaultName( "Coverpoint Node", "Coverpoint", uGuid, GetNamePrefix() );
    if ( !pxToolsData ) { return NULL; }

    // Create the game specfic server object and set its data.
    Asura_ServerNode_Coverpoint* pxGameNode = new Asura_ServerNode_Coverpoint( uGuid );
    if ( !pxGameNode ) { return NULL; }

    pxGameNode->ToolsData = pxToolsData;

    // Create the game specific tools object and select it.
    Tools_EntityNode_CoverpointNode* pxToolsNode = new Tools_EntityNode_CoverpointNode( pxGameNode );
    if ( !pxToolsNode ) { return NULL; }

    Tools_Object_System::SelectObject( pxToolsNode );

	pxToolsNode->SetRequiresValidation();
    pxToolsNode->Position = *Asura_Camera::GetPosition();
    pxToolsNode->Orientation = *Asura_Camera::GetOrientation();

    pxGameNode->SetPosition( pxToolsNode->Position );
    pxGameNode->SetOrientation( pxToolsNode->Orientation );

    Tools_Timed_Message_System::AddMessage( Tools_Timed_Message_System::COLOUR_DEFAULT, "Created new Coverpoint Node" );
    Tools_SaveAsura::SetEntityModified();

    return pxToolsNode;
}

void Tools_EntityNode_CoverpointNode::DestroyEmergePoint( const Tools_Object_CoverpointEmergePoint& xEmergePoint )
{
    // Search through our points and unlink the specified point.
    Tools_Object_CoverpointEmergePoint* pxPrev = NULL;
    Tools_Object_CoverpointEmergePoint* pxCurr = m_pxEmergePoints;
    while ( pxCurr )
    {
        if ( pxCurr == &xEmergePoint )
        {
            if ( pxPrev )
            {
                pxPrev->SetNext( pxCurr->GetNext() );
            }
            else
            {
                // If we haven't a previous node then we must be right at the beginning.
                m_pxEmergePoints = pxCurr->GetNext();
            }

            break;
        }
        else
        {
            pxPrev = pxCurr;
            pxCurr = pxCurr->GetNext();
        }
    }

    m_pxGameEntity->m_aucEmergePointFlags[ xEmergePoint.GetIndex() ].m_bExists = false;
    --m_pxGameEntity->m_uEmergepointCount;
}

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

void Tools_EntityNode_CoverpointNode::DeleteAllEmergePoints()
{
	while ( m_pxEmergePoints )
	{
		Tools_Object_CoverpointEmergePoint* pxNext = m_pxEmergePoints->GetNext();
		Tools_Object_System::DeleteObject( m_pxEmergePoints );
		m_pxEmergePoints = pxNext;
	}
}

bool Tools_EntityNode_CoverpointNode::CanAddEmergePoint()
{
    return ( s_pxThisToolsCoverpointNode &&
        s_pxThisToolsCoverpointNode->m_pxGameEntity->m_uEmergepointCount < Asura_ServerNode_Coverpoint::MAX_NUM_EMERGE_POINTS );
}

void Tools_EntityNode_CoverpointNode::AddEmergePoint()
{
    if ( s_pxThisToolsCoverpointNode ) { s_pxThisToolsCoverpointNode->ThisAddEmergePoint(); }
}

void Tools_EntityNode_CoverpointNode::ThisAddEmergePoint( const Asura_Vector_3* pxPosition /*= NULL*/ )
{
    u_int uIndex = UINT_MAX;
    for ( u_int u = 0; u < Asura_ServerNode_Coverpoint::MAX_NUM_EMERGE_POINTS; ++u )
    {
        if ( !m_pxGameEntity->m_aucEmergePointFlags[ u ].m_bExists )
        {
            uIndex = u;
            break;
        }
    }
    if ( uIndex == UINT_MAX )
    {
        Asura_Assert( false, "Tools_EntityNode_CoverpointNode::ThisAddEmergePoint - could not find a free emerge point index" );
        return;
    }

    if ( pxPosition )
    {
        m_pxGameEntity->m_axEmergePoint[ uIndex ].m_xPosition = *pxPosition;
    }
    else
    {
        m_pxGameEntity->m_axEmergePoint[ uIndex ].m_xPosition = Position + Asura_Vector_3( 0.0f, -1.0f, 0.0f );
    }
    m_pxGameEntity->m_aucEmergePointFlags[ uIndex ].m_bExists = true;
    ++m_pxGameEntity->m_uEmergepointCount;

    Tools_Object_CoverpointEmergePoint* pxEmergePoint = new Tools_Object_CoverpointEmergePoint();
    pxEmergePoint->SetOwnerCoverpoint( this );
    pxEmergePoint->SetPositionPointer( &m_pxGameEntity->m_axEmergePoint[ uIndex ].m_xPosition );
    pxEmergePoint->SetIndex( uIndex );
    pxEmergePoint->SetNext( m_pxEmergePoints );
    m_pxEmergePoints = pxEmergePoint;

    SetEntityModified();
    SetRequiresValidation();
    Tools_Timed_Message_System::AddMessage( Tools_Timed_Message_System::COLOUR_INFO, "Added emerge point" );
}

void Tools_EntityNode_CoverpointNode::CheckCoverpointsForValidity()
{
	Asura_ServerNode_Coverpoint::s_xValidCoverpointList.Clear();
	Asura_ServerNode_Coverpoint::s_uNumValidCoverpoints = 0;

	RememberPhysicsObjectsStatus();
	SetPhysicsObjectsAsIgnored();

	bool bDataChanged = false;
	for ( Asura_Hashed_List_It< Asura_ServerEntity_Node > xNodeIter( Asura_ServerEntity_Node::GetList() ); !xNodeIter.Done(); xNodeIter.Next() )
	{
		Asura_ServerEntity_Node* pxNode = xNodeIter.GetCurrent();
		if ( !pxNode->IsBaseOrDerivativeOf( AsuraEntityClass_CoverpointNode ) )
		{
			continue;
		}

		Asura_ServerNode_Coverpoint* pxCoverpoint = static_cast< Asura_ServerNode_Coverpoint* >( pxNode );
		const bool bWasValid = pxCoverpoint->IsValid();
		pxCoverpoint->CheckForValidity();

		if ( pxCoverpoint->ToolsData->ToolsObject )
		{
			Tools_EntityNode_CoverpointNode* pxCoverpointToolsObject = static_cast< Tools_EntityNode_CoverpointNode* >( pxCoverpoint->ToolsData->ToolsObject );
			pxCoverpointToolsObject->m_bRequiresValidation = false;
		}

		if ( pxCoverpoint->IsValid() )
		{
			Asura_ServerNode_Coverpoint::s_xValidCoverpointList.AddToList( pxCoverpoint );
			++Asura_ServerNode_Coverpoint::s_uNumValidCoverpoints;
		}

		bDataChanged |= ( pxCoverpoint->IsValid() != bWasValid );
	}

	RestorePhysicsObjectsStatus();
	ForgetPhysicsObjectsStatus();

	if ( bDataChanged )
	{
		Tools_SaveAsura::SetEntityModified();
	}
}

void Tools_EntityNode_CoverpointNode::RememberPhysicsObjectsStatus()
{
	for ( Asura_Physics_RigidBodyInstance* pxInstance = Asura_Physics_RigidBodyInstance::GetDynamicList(); pxInstance; pxInstance = pxInstance->GetNextInstance() )
	{
		if ( pxInstance->IsIgnore() )
		{
			s_xRigidBodyIgnoreList.AddToList( pxInstance );
		}
		else
		{
			s_xRigidBodyNonIgnoreList.AddToList( pxInstance );
		}
	}

	for ( Asura_Physics_RigidBodyInstance* pxInstance = Asura_Physics_RigidBodyInstance::GetExternallyControlledList(); pxInstance; pxInstance = pxInstance->GetNextInstance() )
	{
		if ( pxInstance->IsIgnore() )
		{
			s_xRigidBodyIgnoreList.AddToList( pxInstance );
		}
		else
		{
			s_xRigidBodyNonIgnoreList.AddToList( pxInstance );
		}
	}

	for ( Asura_Physics_RigidBodyInstance* pxInstance = Asura_Physics_RigidBodyInstance::GetStaticList(); pxInstance; pxInstance = pxInstance->GetNextInstance() )
	{
		if ( pxInstance->IsIgnore() )
		{
			s_xRigidBodyIgnoreList.AddToList( pxInstance );
		}
		else
		{
			s_xRigidBodyNonIgnoreList.AddToList( pxInstance );
		}
	}
}

void Tools_EntityNode_CoverpointNode::SetPhysicsObjectsAsIgnored()
{
	for ( Asura_Physics_RigidBodyInstance* pxInstance = Asura_Physics_RigidBodyInstance::GetDynamicList(); pxInstance; pxInstance = pxInstance->GetNextInstance() )
	{
		pxInstance->SetIgnore( true );
	}

	for ( Asura_Physics_RigidBodyInstance* pxInstance = Asura_Physics_RigidBodyInstance::GetExternallyControlledList(); pxInstance; pxInstance = pxInstance->GetNextInstance() )
	{
		pxInstance->SetIgnore( true );
	}

	for ( Asura_Physics_RigidBodyInstance* pxInstance = Asura_Physics_RigidBodyInstance::GetStaticList(); pxInstance; pxInstance = pxInstance->GetNextInstance() )
	{
		pxInstance->SetIgnore( true );
	}
}

void Tools_EntityNode_CoverpointNode::RestorePhysicsObjectsStatus()
{
	for ( Asura_List_It< Asura_Physics_RigidBodyInstance > xListIt( &s_xRigidBodyIgnoreList ); !xListIt.Done(); xListIt.Next() )
	{
		xListIt.GetCurrent()->SetIgnore( true );
	}
	for ( Asura_List_It< Asura_Physics_RigidBodyInstance > xListIt( &s_xRigidBodyNonIgnoreList ); !xListIt.Done(); xListIt.Next() )
	{
		xListIt.GetCurrent()->SetIgnore( false );
	}
}

void Tools_EntityNode_CoverpointNode::ForgetPhysicsObjectsStatus()
{
	s_xRigidBodyIgnoreList.ClearList();
	s_xRigidBodyNonIgnoreList.ClearList();
}

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

#endif //ASURA_USE_AXON
