//////////////////////////////////////////////////////////////////////////////
//
// Tools_EntityNode_GuardZone.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

#ifdef ASURA_USE_AXON

// This file's header.
#include "Entity/Locations/GuardZone/Tools_EntityNode_GuardZone.h"

//Asura
#include "Camera/Asura_Camera.h"
#include "Core/Asura_Maths.h"
#include "Core/Asura_UserInput.h"
#include "Core/Memory/Asura_Mem.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/Locations/GuardZone/Asura_ServerNode_GuardZone.h"
#include "Entity/Locations/OrientedNode/Asura_ServerNode_OrientedNode.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Overlay.h"
#include "Rendering/Asura_Render.h"

//Tools
#include "Entity/Locations/OrientedNode/Tools_EntityNode_OrientedNode.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Misc/Tools_LOS.h"
#include "Misc/Tools_Timed_Messages.h"
#include "Objects/Tools_Object_System.h"

bool Tools_EntityNode_GuardZone::s_bShowAllVolumes = false;
Tools_EntityNode_GuardZone* Tools_EntityNode_GuardZone::s_pxThisToolsGuardZone = NULL;

Asura_Hashed_List< Tools_EntityNode_GuardZone > Tools_EntityNode_GuardZone::s_xList(2);

//////////////////////////////////////////////////////////////////////////////
//
// Tools_EntityNode_GuardZone
//
//////////////////////////////////////////////////////////////////////////////
Tools_EntityNode_GuardZone::Tools_EntityNode_GuardZone( Asura_Entity* pxEntity )
: PARENT( pxEntity )
, m_pxGameNode( NULL )
, m_bMoveVolumeWithObject( true )
, m_bOverlayChanged( false )
, m_bFirstUpdate( true )
, m_pxVolumeController( NULL )
, m_xPreviousPosition( Asura_Maths::ZeroVector3 )
{
	Type = TOT_Entity_GuardZone;

	SetMesh( "ToolsShape_GZMarker" );

	if ( Asura_ServerEntity_Node::GetList()->FindEntity( pxEntity->GetGuid() ) )
	{
		m_pxGameNode = static_cast< Asura_ServerNode_GuardZone* >( pxEntity );
		Asura_Assert( m_pxGameNode->IsBaseOrDerivativeOf( AsuraEntityClass_GuardZone ),
			"Tools_EntityNode_GuardZone::Tools_EntityNode_GuardZone - wrong entity type" );
	}

	m_xPreviousPosition = Position;

	s_xList.AddToList( this );
}

Tools_EntityNode_GuardZone::~Tools_EntityNode_GuardZone()
{
	delete m_pxVolumeController;

	s_xList.RemoveFromList( this );
}

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

void Tools_EntityNode_GuardZone::AddOverlayControls( class Asura_Overlay* pxNewOverlay, class Asura_Vector_2& xOffset )
{
	PARENT::AddOverlayControls( pxNewOverlay, xOffset );

	Asura_Dialog* pxNewDialog = NULL;
	pxNewDialog = new Asura_Tickbox_Dialog( "Use Advanced Volumes", xOffset, &m_pxGameNode->m_bUseAdvancedVolumes, &m_bOverlayChanged );
	pxNewDialog->SetHelpText( "Switch from using a sphere volume to advaced volumes" );
	pxNewOverlay->AddDialog( pxNewDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	if ( m_pxGameNode->m_bUseAdvancedVolumes )
	{
		pxNewDialog = new Asura_Function_Dialog( "Edit Volume", xOffset, EditVolume );
		pxNewDialog->SetHelpText( "Edits the volume(s) for this GuardZone." );
		pxNewOverlay->AddDialog( pxNewDialog );
		xOffset.y += Asura_Dialog::DefaultSpacing();

		pxNewDialog = new Asura_Tickbox_Dialog( "Move Volume With Object", xOffset, &m_bMoveVolumeWithObject, &m_bOverlayChanged );
		pxNewDialog->SetHelpText( "The volume(s) attached to this GuardZone should be moved when the tools object is moved." );
		pxNewOverlay->AddDialog( pxNewDialog );
		xOffset.y += Asura_Dialog::DefaultSpacing();
	}
	else
	{
		pxNewDialog = new Asura_Numerical_Dialog( "Radius", xOffset, &m_pxGameNode->m_fRadius, 1.0f, 200.0f, 0.25f, "%.2f" );
		pxNewOverlay->AddDialog( pxNewDialog );
		xOffset.y += Asura_Dialog::DefaultSpacing();
	}
	//xOffset.x = pxNewDialog->MaxCoord.x + 1.0f;
	pxNewDialog = new Asura_Tickbox_Dialog( "Show All Volumes", xOffset, &s_bShowAllVolumes, &m_bOverlayChanged );
	pxNewDialog->SetHelpText( "Shows all volumes associated with all GuardZones." );
	pxNewOverlay->AddDialog( pxNewDialog );
	//xOffset.x =  1.0f;
	xOffset.y += Asura_Dialog::LargeSpacing();

	pxNewDialog = new Asura_Dialog( "Add Guard Point (G)", xOffset );
	pxNewOverlay->AddDialog( pxNewDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

}

void Tools_EntityNode_GuardZone::OnSelection()
{
	PARENT::OnSelection();

	s_pxThisToolsGuardZone = this;
}

void Tools_EntityNode_GuardZone::OnDeselection()
{
	PARENT::OnDeselection();

	s_pxThisToolsGuardZone = NULL;
}

void Tools_EntityNode_GuardZone::Render()
{
	PARENT::Render();

	if ( ( IsSelected() || s_bShowAllVolumes ) )
	{
		if ( !m_pxGameNode->m_bUseAdvancedVolumes )
		{
			extern void Render_Sphere( const Asura_Vector_3& position, float radius, const Asura_Vector_3* direction=0, float angle=0); 

			// render the radius set using a sphere
			if ( m_pxGameNode != NULL )
			{
				Render_Sphere( m_pxGameNode->GetPosition(), m_pxGameNode->m_fRadius );
			}
		}

		//render a line from guardzone to guardpoints
		for ( int i = 0; i < m_pxGameNode->m_iNumGuardPoints; ++i )
		{
			Asura_ServerEntity_Node* pxNode = Asura_ServerNode_OrientedNode::FindEntity( m_pxGameNode->m_puGuardPointGuids[ i ] );
			if ( pxNode )
			{
				Asura_Render::RenderLine( Position, pxNode->GetPosition(), 0xffffffff );
			}
		}
	}
}

void Tools_EntityNode_GuardZone::Update()
{
	if ( m_bFirstUpdate )
	{
		// (TI - 20.06.2007) - Tell all our guardpoints we're their guardzone
		for ( int i = 0; i < m_pxGameNode->m_iNumGuardPoints; ++i )
		{
			Tools_EntityNode_OrientedNode* const pxNode = 
				static_cast< Tools_EntityNode_OrientedNode* >( Tools_EntityNode_OrientedNode::FindEntity( m_pxGameNode->m_puGuardPointGuids[ i ] ) );
			if ( pxNode )
			{
				pxNode->SetGuardZoneGuid( GetGuid() );
			}
		}

		m_bFirstUpdate = false;
	}

	PARENT::Update();

	ShowVolume( s_bShowAllVolumes );
}

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

	//shift the bounding box by the amount that the object has moved
	if( m_bMoveVolumeWithObject )
	{
		for( int i = 0; i < m_pxGameNode->m_iNumVolumes; ++i )
		{
			m_pxGameNode->m_pxVolumes[ i ].m_xBox.Displace( Position - m_xPreviousPosition );
		}

		if( m_pxVolumeController )
		{
			m_pxVolumeController->MoveVolumeObjects( Position - m_xPreviousPosition );
		}
	}

	m_xPreviousPosition = Position;
}

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

	if ( Asura_UserInput::DebouncedKeyPressed( 'g' ) )      // 'g' to add a guard point
	{
		Tools_EntityNode_GuardZone::AddGuardPoint();
	}

	if( m_bOverlayChanged )
	{
		m_bOverlayChanged = false;
		
		Tools_Object_System::DeselectObject( this );
		Tools_Object_System::SelectObject( this );

		SetEntityModified();
	}
}

void Tools_EntityNode_GuardZone::Destroy()
{
	// Destroy all guardpoints as well.
	while ( m_pxGameNode->m_iNumGuardPoints > 0 )
	{
		Tools_Object* pxObject = Tools_Object_System::GetObjectFromGuid( m_pxGameNode->m_puGuardPointGuids[ 0 ] );
		if ( pxObject )
		{
			pxObject->Destroy();
		}
		else
		{
			Tools_Timed_Message_System::AddMessage( Tools_Timed_Message_System::COLOUR_ERROR, "Unable to destroy all associated guard points." );
			break;
		}
	}

	PARENT::Destroy();
}

bool Tools_EntityNode_GuardZone::AddGuardPoint( const Asura_Vector_3& xPosition )
{
	if ( !s_pxThisToolsGuardZone || !s_pxThisToolsGuardZone->m_pxGameNode )
	{
		Asura_Assert( false, "No guard zone selected?" );
		return false;
	}

	if ( !IsPositionInsideGuardZone( xPosition ) )
	{
		Tools_Timed_Message_System::AddMessage( Tools_Timed_Message_System::COLOUR_WARNING, "Point is not inside guard zone" );
	}

	Tools_EntityNode_OrientedNode* pxGuardPoint = NULL;
	Tools_EntityNode_OrientedNode::CreateGuardPoint( &pxGuardPoint, s_pxThisToolsGuardZone->GetGuid() );
	Tools_Object_System::SelectObject( this );
	if ( pxGuardPoint )
	{
		pxGuardPoint->Position = xPosition;
		pxGuardPoint->UpdatePositionInAsuraData( false );

		//add guardpoint to game stuff
		s_pxThisToolsGuardZone->m_pxGameNode->AddGuardPoint( pxGuardPoint->GetGuid() );
	}

	return true;
}

bool Tools_EntityNode_GuardZone::IsPositionInsideGuardZone( const Asura_Vector_3& xPosition ) const
{
	//check that the position is within range.
	if ( !m_pxGameNode->m_bUseAdvancedVolumes )
	{
		const float fDistSq     = ( Position - xPosition ).MagnitudeSquared();
		const float fZoneRadius = m_pxGameNode->GetRadius();
		const bool bInside = fDistSq < fZoneRadius * fZoneRadius;

		return bInside;
	}
	else
	{
		bool bOk = false;
		for ( int i = 0; i < m_pxGameNode->m_iNumVolumes && !bOk; ++i )
		{
			Asura_OrientedVolume xVolume = m_pxGameNode->m_pxVolumes[ i ];
			if ( xVolume.Contains( xPosition ) )
			{
				bOk = true;
			}
		}

		return bOk;
	}
}

void Tools_EntityNode_GuardZone::AddGuardPoint()
{
	if ( s_pxThisToolsGuardZone )
	{
		Asura_Vector_3 xGuardPointPosition = Asura_Maths::ZeroVector3;

		int iDummy1, iDummy2;
		if ( Tools_LOS::FindPolyAndPositionUnderCursor( iDummy1, iDummy2, xGuardPointPosition ) )
		{
			// make a new guard point
			if ( s_pxThisToolsGuardZone->AddGuardPoint( xGuardPointPosition ) )
			{
				Tools_Timed_Message_System::AddMessage( 0x00ff00, "Added guard point" );
			}
		}
		else
		{
			Tools_Timed_Message_System::AddMessage( Tools_Timed_Message_System::COLOUR_ERROR, "Please select a valid poly" );
		}
	}
}

void Tools_EntityNode_GuardZone::RemoveGuardPoint( Asura_Guid uGPGuid )
{
	if ( m_pxGameNode )
	{
		m_pxGameNode->RemoveGuardPoint( uGPGuid );
	}
}

//bool Tools_EntityNode_GuardZone::CanBeCopied() const
//{
//    // We can only be copied if we are the only thing selected.
//    if ( Tools_Object_System::SelectedObjectList.size() > 1 )
//    {
//        return false;
//    }
//
//    return ( Type == TOT_Entity_GamesceneNode );
//}
//
//void Tools_EntityNode_GuardZone::Copy( const Tools_Object* pxObject )
//{
//    // Copy parent data.
//    PARENT::Copy( pxObject );
//
//    // Copy our data.
//    const Tools_EntityNode_GuardZone* pxFromNodeObject = static_cast< const Tools_EntityNode_GuardZone* >( pxObject );
//    m_bMoveVolumeWithObject = pxFromNodeObject->m_bMoveVolumeWithObject;
//    m_bOverlayChanged       = pxFromNodeObject->m_bOverlayChanged;
//    m_xPreviousPosition     = pxFromNodeObject->m_xPreviousPosition;
//    delete m_pxVolumeController;
//    m_pxVolumeController = NULL;
//
//    // Copy the engine entity's data across.
//    const Asura_ServerNode_GuardZone* pxFromEntity = static_cast< const Asura_ServerNode_GuardZone* >( pxFromNodeObject->Entity );
//    Asura_ServerNode_GuardZone*       pxToEntity   = static_cast< Asura_ServerNode_GuardZone* >( Entity );
//    pxToEntity->m_iNumGuardPoints = pxFromEntity->m_iNumGuardPoints;
//    delete [] pxToEntity->m_puGuardPointGuids;
//    if ( pxFromEntity->m_iNumGuardPoints )
//    {
//        pxToEntity->m_puGuardPointGuids = new Asura_Guid [ pxFromEntity->m_iNumGuardPoints ];
//        Asura_Mem::Copy( pxToEntity->m_puGuardPointGuids, pxFromEntity->m_puGuardPointGuids, pxFromEntity->m_iNumGuardPoints );
//    }
//    else
//    {
//        pxToEntity->m_puGuardPointGuids = NULL;
//    }
//    pxToEntity->m_xGuardPointTree     = pxFromEntity->m_xGuardPointTree;
//    pxToEntity->m_fRadius             = pxFromEntity->m_fRadius;
//    pxToEntity->m_bUseAdvancedVolumes = pxFromEntity->m_bUseAdvancedVolumes;
//    pxToEntity->m_iNumVolumes         = pxFromEntity->m_iNumVolumes;
//    delete [] pxToEntity->m_pxVolumes;
//    if ( pxFromEntity->m_iNumVolumes )
//    {
//        pxToEntity->m_pxVolumes = new Asura_OrientedVolume [ pxFromEntity->m_iNumVolumes ];
//        Asura_Mem::Copy( pxToEntity->m_pxVolumes, pxFromEntity->m_pxVolumes, sizeof( Asura_OrientedVolume ) * pxFromEntity->m_iNumVolumes );
//    }
//    else
//    {
//        pxToEntity->m_pxVolumes = NULL;
//    }
//}
//
//Tools_Object* Tools_EntityNode_GuardZone::CreateObjectOfThisType() const
//{
//    return CreateAndReturn();
//}

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

Tools_EntityNode_GuardZone* Tools_EntityNode_GuardZone::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 = CreateToolsData( "Guard Zone", 0, GetNamePrefix() );
	if ( !pxToolsData ) { return NULL; }

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

	pxGameNode->ToolsData = pxToolsData;

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

	Tools_Object_System::SelectObject( pxToolsNode );

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

	pxGameNode->SetPosition( pxToolsNode->Position );

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

    return pxToolsNode;
}

void Tools_EntityNode_GuardZone::EditVolume()
{
	Asura_Assert( s_pxThisToolsGuardZone, "Tools_EntityNode_GuardZone::EditVolume - Not tools object selected?" );

	//create controller for editing volumes if it hasn't been done yet
	if( !s_pxThisToolsGuardZone->m_pxVolumeController )
	{
		s_pxThisToolsGuardZone->m_pxVolumeController = new Tools_GuardZone_VolumeEdit_Controller( s_pxThisToolsGuardZone );
	}

	//and get it to create the volume objects
	s_pxThisToolsGuardZone->m_pxVolumeController->StartVolumeEditing();
}

void Tools_EntityNode_GuardZone::ShowVolume( bool bShow )
{
	if( !m_pxVolumeController && !bShow ) return;

	//create controller for editing volumes if it hasn't been done yet
	if( !m_pxVolumeController )
	{
		m_pxVolumeController = new Tools_GuardZone_VolumeEdit_Controller( this );
	}

	//hide/show volume objects
	m_pxVolumeController->ShowVolumes( ( bShow || m_pxVolumeController->IsSelected() || IsSelected() ) && m_pxGameNode->m_bUseAdvancedVolumes );
}

//////////////////////////////////////////////////////////////////////////////
//
// Tools_GuardZone_VolumeEdit_Controller
//
//////////////////////////////////////////////////////////////////////////////
Tools_GuardZone_VolumeEdit_Controller::Tools_GuardZone_VolumeEdit_Controller( Tools_EntityNode_GuardZone* pxToolsEntity )
:Tools_VolumeEdit_Controller( &pxToolsEntity->Changed )
{
	m_pxToolsEntity = pxToolsEntity;
	m_pxAsuraEntity = pxToolsEntity->m_pxGameNode;

}

void Tools_GuardZone_VolumeEdit_Controller::StartVolumeEditing()
{
	Tools_VolumeEdit_Controller::StartVolumeEditing();

	//create a volume if none exists yet
	if( !GetNumberOfVolumes() ) CreateVolume();
}

void Tools_GuardZone_VolumeEdit_Controller::EndVolumeEditing()
{
	Tools_VolumeEdit_Controller::EndVolumeEditing();
	//select the owner
	Tools_Object_System::SelectObject( m_pxToolsEntity );
}


void Tools_GuardZone_VolumeEdit_Controller::CreateVolumeMain()
{
	//allocate a new array of volumes , and copy the old ones across
	Asura_OrientedVolume* pxVolumes = new Asura_OrientedVolume[ m_pxAsuraEntity->m_iNumVolumes + 1 ];

	for( int i = 0; i < m_pxAsuraEntity->m_iNumVolumes; i++ )
	{
		pxVolumes[ i ] = m_pxAsuraEntity->m_pxVolumes[ i ];
	}

	//initialise the new volume
	pxVolumes[ m_pxAsuraEntity->m_iNumVolumes ].m_xBox.Reset();
	pxVolumes[ m_pxAsuraEntity->m_iNumVolumes ].m_xBox.Set( Asura_Maths::ZeroVector3 );
	pxVolumes[ m_pxAsuraEntity->m_iNumVolumes ].m_xOrient = Asura_Maths::IdentityQuat;

	//replace the old array
	delete [] m_pxAsuraEntity->m_pxVolumes;
	m_pxAsuraEntity->m_pxVolumes = pxVolumes;
	m_pxAsuraEntity->m_iNumVolumes++;

}

void Tools_GuardZone_VolumeEdit_Controller::DeleteVolumeMain( int iIndex )
{
	Asura_Assert( iIndex >= 0 && iIndex < m_pxAsuraEntity->m_iNumVolumes, "GuardZone_VolumeEdit_Controller::DeleteVolumeMain - index to delete doesn't match with number of volumes" );

	//shuffle down any volumes after the one being deleted
	for( int i = iIndex; i < m_pxAsuraEntity->m_iNumVolumes - 1; i++ )
	{
		m_pxAsuraEntity->m_pxVolumes[ i ] = m_pxAsuraEntity->m_pxVolumes[ i + 1 ];
	}
	m_pxAsuraEntity->m_iNumVolumes--;
}

//////////////////////////////////////////////////////////////////////////////
//
// Tools_Object_GuardZone_ChooseGuardZoneObject 
//
//////////////////////////////////////////////////////////////////////////////

bool Tools_Object_GuardZone_ChooseGuardZoneObject ::CanBeSelected( Tools_Object* pxObject )
{
	if ( !pxObject ) { return false; }

	if ( pxObject->Type == TOT_Entity_GuardZone )
	{
		Tools_EntityNode_GuardZone* pxGuardZone = static_cast< Tools_EntityNode_GuardZone* >( pxObject );
		return pxGuardZone->IsBaseOrDerivativeOf( TOT_Entity_GuardZone );
	}

	return false;
}

void Tools_Object_GuardZone_ChooseGuardZoneObject ::ObjectSelected( Tools_Object* pxObject )
{
	*m_puGuidToSet = pxObject ? pxObject->GetGuid() : ASURA_GUID_UNREGISTERED;
	if ( m_bChanged ) { *m_bChanged = true; }
}

//////////////////////////////////////////////////////////////////////////////
//
//
//////////////////////////////////////////////////////////////////////////////
#endif //ASURA_USE_AXON