//////////////////////////////////////////////////////////////////////////////
//
// Tools_Entity_WaypointBlockerVolume.cpp
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

// This file's header.
#include "Entity/Tools_Entity_WaypointBlockerVolume.h"

//Asura
#include "AI/Asura_AI_Navigation.h"
#include "Camera/Asura_Camera.h"
#include "Interface/Asura_Dialog.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Network/Asura_Message.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Text_Output.h"

//Tools
#include "Entity/Tools_Entity_Data.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Objects/Tools_Object.h"
#include "Objects/Tools_Object_System.h"
#include "Objects/Tools_ObjectSystem_Param.h"

bool Tools_Entity_WaypointBlockerVolume::s_bMoveVolumeWithObject = true;
bool Tools_Entity_WaypointBlockerVolume::s_bShowAllVolumes = false;
bool Tools_Entity_WaypointBlockerVolume::s_bShowBlockedLinks = false;


//////////////////////////////////////////////////////////////////////////////
//
// Tools_Entity_WaypointBlockerVolume
//
//////////////////////////////////////////////////////////////////////////////

Tools_Entity_WaypointBlockerVolume::Tools_Entity_WaypointBlockerVolume( Asura_Entity* pxEntity )
: TOOLS_PARENT( pxEntity )
, m_bOverlayChanged( false )
, m_bUseAdvancedVolumes( false )
, m_pxVolumeController( NULL )
{
	Type = TOT_Entity_WaypointBlockerVolume;
	Mesh = Asura_Mesh_System::GetMesh( "ToolsShape_WaypointBlocker" );
	m_xPreviousPosition = Position;

	if ( pxEntity )
	{
		Asura_ServerEntity_WaypointBlockerVolume* pxAsuraEntity = static_cast< Asura_ServerEntity_WaypointBlockerVolume* >( pxEntity );

		m_bUseAdvancedVolumes = ( pxAsuraEntity->m_iNumVolumes > 0 );
	}
}

Tools_Entity_WaypointBlockerVolume::~Tools_Entity_WaypointBlockerVolume()
{
	delete m_pxVolumeController;
}

void Tools_Entity_WaypointBlockerVolume::AddOverlayControls( Asura_Overlay* pxOverlay, Asura_Vector_2& xOffset )
{
	TOOLS_PARENT::AddOverlayControls( pxOverlay, xOffset );

	Asura_ServerEntity_WaypointBlockerVolume* pxAsuraEntity = static_cast< Asura_ServerEntity_WaypointBlockerVolume* >( Entity );

	Asura_Dialog* pxDialog;

	if ( m_bUseAdvancedVolumes )
	{
		pxDialog = new Asura_Function_Dialog( "Use Spherical Volume", xOffset, UseSphericalVolume );
		pxDialog->SetHelpText( "Switch from using a sphere volume to advaced volumes" );
		pxOverlay->AddDialog( pxDialog );
		xOffset.y += Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Function_Dialog( "Edit Volume", xOffset, EditVolume );
		pxDialog->SetHelpText("Edits the volume(s) for this waypoint blocker.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y += Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Tickbox_Dialog( "Move Volume With Object", xOffset, &s_bMoveVolumeWithObject );
		pxDialog->SetHelpText( "The volume attached to this entity should be moved when the tools object is moved.\nThis is a global setting for all waypoint blocker volumes." );
		pxOverlay->AddDialog( pxDialog );
		xOffset.y += Asura_Dialog::DefaultSpacing();
	}
	else
	{
		pxDialog = new Asura_Function_Dialog( "Use Advanced Volumes", xOffset, UseAdvancedVolumes );
		pxDialog->SetHelpText( "Switch from using a sphere volume to advaced volumes" );
		pxOverlay->AddDialog( pxDialog );
		xOffset.y += Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog( "Radius", xOffset, &pxAsuraEntity->m_fSphericalVolumeRadius, 1.0f, 200.0f, 0.25f, "%.2f", &m_bOverlayChanged );
		pxOverlay->AddDialog( pxDialog );
		xOffset.y += Asura_Dialog::DefaultSpacing();
	}

	pxDialog = new Asura_Tickbox_Dialog( "Show All", xOffset, &s_bShowAllVolumes );
	pxDialog->SetHelpText( "Shows volumes for all waypoint blockers." );
	pxOverlay->AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog( "Render Blocked Links", xOffset, &s_bShowBlockedLinks );
	pxDialog->SetHelpText( "Renders the blocked links for all waypoint blockers." );
	pxOverlay->AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();
}

void Tools_Entity_WaypointBlockerVolume::PrintInfo( float& fX, float& fY )
{
	TOOLS_PARENT::PrintInfo( fX, fY );

	Asura_ServerEntity_WaypointBlockerVolume* pxWBV = static_cast< Asura_ServerEntity_WaypointBlockerVolume* >( Entity );
	Asura_Text_Output::Print( fX, fY++, 0xffffffff, 0, "Number Of Links Blocked : %u", pxWBV->GetNumLinksToBlock() );
}

bool Tools_Entity_WaypointBlockerVolume::HandlesThisMessage( int iMessageID )
{
	switch( iMessageID )
	{
		case ASURA_MSG_DESTRUCT:
		case ASURA_MSG_ACTIVATE:
		case ASURA_MSG_DEACTIVATE:
			return true;

		default:
			return false;
	}
}

void Tools_Entity_WaypointBlockerVolume::OnDeselection()
{
	TOOLS_PARENT::OnDeselection();

	if ( IsBeingDestroyed() )
	{
		//No point in calculating link blocks on an object being destroyed.
		return;
	}

	Asura_ServerEntity_WaypointBlockerVolume* pxAsuraEntity = static_cast< Asura_ServerEntity_WaypointBlockerVolume* >( Entity );
	if ( !pxAsuraEntity ) { return; }

	//recalc blocked links
	Asura_ServerEntity_WaypointBlockerVolume::InitialiseWaypointLinkBlockCalculations();
	pxAsuraEntity->CalculateWaypointLinkBlocks();
	pxAsuraEntity->UpdateWaypointLinkBlocks();
	Asura_ServerEntity_WaypointBlockerVolume::ShutdownWaypointLinkBlockCalculations();
}

void Tools_Entity_WaypointBlockerVolume::Render()
{
	TOOLS_PARENT::Render();

	Asura_ServerEntity_WaypointBlockerVolume* pxAsuraEntity = static_cast< Asura_ServerEntity_WaypointBlockerVolume* >( Entity );
	if ( !pxAsuraEntity ) { return; }

	if ( ( IsSelected() || s_bShowAllVolumes ) )
	{
		if ( !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
			Render_Sphere( pxAsuraEntity->GetPosition(), pxAsuraEntity->m_fSphericalVolumeRadius );
		}
	}

	if ( s_bShowBlockedLinks )
	{
		for ( u_int uIndex = 0; uIndex < pxAsuraEntity->m_uNumLinksToBlock; uIndex++ )
		{
			Asura_AI_NavVertex* pxWaypoint1 = Asura_AI_Navigation_System::GetWaypoint( pxAsuraEntity->m_pxLinksToBlock[ uIndex ].m_usWaypointIndex );
			if ( !pxWaypoint1 ) { continue; }
			if ( pxAsuraEntity->m_pxLinksToBlock[ uIndex ].m_usConnectionIndex >= pxWaypoint1->m_ucNumConnections ) { continue; }
			Asura_AI_NavVertex* pxWaypoint2 = Asura_AI_Navigation_System::GetWaypoint( pxWaypoint1->m_pxConnections[ pxAsuraEntity->m_pxLinksToBlock[ uIndex ].m_usConnectionIndex ].m_usIndex );
			if ( !pxWaypoint2 ) { continue; }
			Asura_Vector_3 xPos1 = pxWaypoint1->m_xPosition;
			xPos1.y -= 0.5f;
			Asura_Vector_3 xPos2 = pxWaypoint2->m_xPosition;
			xPos2.y -= 0.5f;
			Asura_Render::RenderLine( xPos1, xPos2, 0xffffffff );
		}
	}
}

void Tools_Entity_WaypointBlockerVolume::Update()
{
	TOOLS_PARENT::Update();

	ShowVolume( s_bShowAllVolumes );
}

void Tools_Entity_WaypointBlockerVolume::UpdatePositionInAsuraData(bool bUpdateLinkedObjects)
{
	//shift the bounding box by the amount that the object has moved
	Asura_ServerEntity_WaypointBlockerVolume* pxAsuraEntity = static_cast< Asura_ServerEntity_WaypointBlockerVolume* >( Entity );
	if( s_bMoveVolumeWithObject )
	{
		for( int i = 0; i < pxAsuraEntity->m_iNumVolumes; i++ )
		{
			pxAsuraEntity->m_pxVolumes[i].m_xBox.Displace( Position - m_xPreviousPosition );
		}
		if( m_pxVolumeController )
		{
			m_pxVolumeController->MoveVolumeObjects( Position - m_xPreviousPosition );
		}
	}

	m_xPreviousPosition = Position;

	TOOLS_PARENT::UpdatePositionInAsuraData(bUpdateLinkedObjects);
}

void Tools_Entity_WaypointBlockerVolume::UpdateWhileSelected()
{
	TOOLS_PARENT::UpdateWhileSelected();

	if ( m_bOverlayChanged )
	{
		m_bOverlayChanged = false;

		Tools_Object_System::DeselectObject( this );
		Tools_Object_System::SelectObject( this );

		SetEntityModified();
	}
}

void Tools_Entity_WaypointBlockerVolume::Create()
{
	Tools_Entity_WaypointBlockerVolume* pxToolsObj = CreateMain();
	if ( pxToolsObj )
	{    
		Tools_Object_System::SelectObject( pxToolsObj );
	}
}

Tools_Entity_WaypointBlockerVolume* Tools_Entity_WaypointBlockerVolume::CreateMain()
{
	const Asura_Guid uGUID = Asura_Entity_System::GetCreatedByToolsGuid();

	Tools_Entity_Data*					      pxToolsData = NULL;
	Asura_ServerEntity_WaypointBlockerVolume* pxEntity    = NULL;
	Tools_Entity_WaypointBlockerVolume*		  pxToolsObj  = NULL;

	pxToolsData = CreateToolsDataWithDefaultName( "Waypoint Blocker Volume", "Waypoint Blocker Volume", uGUID, GetNamePrefix() );
	if( !pxToolsData )
	{ 
		return NULL;
	}

	pxEntity = new Asura_ServerEntity_WaypointBlockerVolume( uGUID );
	pxEntity->ToolsData = pxToolsData;
	if( !pxEntity )
	{
		delete pxToolsData;
		return NULL;
	}

	pxToolsObj = new Tools_Entity_WaypointBlockerVolume( pxEntity );
	if( !pxToolsObj )
	{
		delete pxToolsData;
		pxEntity->PreDestructionShutdown();
		delete pxEntity;
		return NULL;
	}

	Tools_SaveAsura::SetEntityModified();

	return pxToolsObj;
}

void Tools_Entity_WaypointBlockerVolume::EditVolume()
{
	Asura_Assert( ThisEntity, "" );

	Tools_Entity_WaypointBlockerVolume* pxToolsEntity = static_cast< Tools_Entity_WaypointBlockerVolume* >( ThisEntity );
	//create controller for editing volumes if it hasn't been done yet
	if( !pxToolsEntity->m_pxVolumeController )
	{
		pxToolsEntity->m_pxVolumeController = new Tools_WaypointBlockerVolume_VolumeEdit_Controller( pxToolsEntity );
	}

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

void Tools_Entity_WaypointBlockerVolume::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_WaypointBlockerVolume_VolumeEdit_Controller(this);
	}

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

void Tools_Entity_WaypointBlockerVolume::UseAdvancedVolumes()
{
	Tools_Entity_WaypointBlockerVolume* pxToolsEntity = static_cast< Tools_Entity_WaypointBlockerVolume* >( ThisEntity );
	pxToolsEntity->m_bUseAdvancedVolumes = true;
	pxToolsEntity->m_bOverlayChanged = true;
}

void Tools_Entity_WaypointBlockerVolume::UseSphericalVolume()
{
	Tools_Entity_WaypointBlockerVolume* pxToolsEntity = static_cast< Tools_Entity_WaypointBlockerVolume* >( ThisEntity );
	Asura_ServerEntity_WaypointBlockerVolume* pxAsuraEntity = static_cast< Asura_ServerEntity_WaypointBlockerVolume* >( pxToolsEntity->Entity );

	if ( pxAsuraEntity->m_iNumVolumes > 0 )
	{
		char acString[ 128 ];
		Asura_PrintfToBfr( acString, "Are you sure? There are advanced volumes created, selecting Yes will result in their deletion.", 128, true );

		Asura_String xQuestion( acString );
		YesNo_Overlay xYesNoOverlay( xQuestion );
		if ( xYesNoOverlay.DoModal() )
		{
			delete [] pxAsuraEntity->m_pxVolumes;
			pxAsuraEntity->m_pxVolumes = NULL;
			pxAsuraEntity->m_iNumVolumes = 0;
			pxToolsEntity->m_bUseAdvancedVolumes = false;
			pxToolsEntity->m_bOverlayChanged = true;

			if ( pxToolsEntity->m_pxVolumeController )
			{
				delete [] pxToolsEntity->m_pxVolumeController;
				pxToolsEntity->m_pxVolumeController = NULL;
			}
		}
	}
	else
	{
		pxToolsEntity->m_bUseAdvancedVolumes = false;
		pxToolsEntity->m_bOverlayChanged = true;

		if ( pxToolsEntity->m_pxVolumeController )
		{
			delete [] pxToolsEntity->m_pxVolumeController;
			pxToolsEntity->m_pxVolumeController = NULL;
		}
	}
}

bool Tools_Entity_WaypointBlockerVolume::SetParameter(Asura_Hash_ID uParamID,const class Tools_Parameter* pxParam)
{
	const bool bReturnValue = TOOLS_PARENT::SetParameter( uParamID, pxParam );

	Asura_ServerEntity_WaypointBlockerVolume* const pxAsuraEntity = (Asura_ServerEntity_WaypointBlockerVolume*) Entity;

	switch(uParamID)
	{
		case g_uHash_Entity_StartsActive :
		{
			pxAsuraEntity->UpdateWaypointLinkBlocks();

		} break;

	default :
		break;
	}

	return bReturnValue;
}

//////////////////////////////////////////////////////////////////////////////
//
// Tools_WaypointBlockerVolume_VolumeEdit_Controller
//
//////////////////////////////////////////////////////////////////////////////
Tools_WaypointBlockerVolume_VolumeEdit_Controller::Tools_WaypointBlockerVolume_VolumeEdit_Controller( Tools_Entity_WaypointBlockerVolume* pxToolsEntity )
:Tools_VolumeEdit_Controller( &pxToolsEntity->Changed )
{
	m_pxToolsEntity = pxToolsEntity;
	m_pxAsuraEntity = static_cast< Asura_ServerEntity_WaypointBlockerVolume* >( pxToolsEntity->Entity );
}

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

	if( GetNumberOfVolumes() == 0 )
	{
		//create a volume if none exists yet
		CreateVolume();
	}
}

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


void Tools_WaypointBlockerVolume_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( m_pxToolsEntity->Position );
	pxVolumes[m_pxAsuraEntity->m_iNumVolumes].m_xBox.Grow( 1.5f );
	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_WaypointBlockerVolume_VolumeEdit_Controller::DeleteVolumeMain( int iIndex )
{
	Asura_Assert( iIndex >= 0 && iIndex < m_pxAsuraEntity->m_iNumVolumes, "Invalid volume index." );

	//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--;
}



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