///////////////////////////////////////////////////////////////////////////////
//
// File: Tools_Entity_CameraVolume.cpp
//
// Created by Martin G.Harris (7/9/2006)
// 
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"
#include "Entity/CameraVolume/Tools_Entity_CameraVolume.h"

#ifdef ASURA_USE_CAMERA_VOLUME

// Includes
#include "Core/Asura_OrientedVolume.h"
#include "Debugging/Asura_Assert.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/Triggers/Asura_ClientEntity_CameraVolume.h"
#include "Entity/Triggers/Asura_ServerEntity_CameraVolume.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Overlay.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Network/Asura_Message.h"
#include "Rendering/Asura_Text_Output.h"

#include "Objects/Tools_Object_System.h"

// Static data
bool Tools_Entity_CameraVolume::s_bMoveVolumeWithObject = true;
bool Tools_Entity_CameraVolume::s_bMovePropertiesWithObject = true;
bool Tools_Entity_CameraVolume::s_bShowAllVolumes = false;
bool Tools_Entity_CameraVolume::s_bShowSelectedVolume = false;

///////////////////////////////
// Tools_Entity_CameraVolume //
///////////////////////////////
Tools_Entity_CameraVolume::Tools_Entity_CameraVolume(Asura_Entity* pxEntity)
: Tools_Entity_Activatable_Object(pxEntity)
, m_pxVolumeController(0)
, m_iPriority(iASURA_CAMERA_VOLUME_DEFAULT_PRIORITY)
{
	Asura_Assert(pxEntity && pxEntity->GetClassification()==AsuraEntityClass_CameraVolume, "Wrong classification should be camera volume entity.");

	Type = TOT_Entity_CameraVolume;
	Mesh = Asura_Mesh_System::GetMesh("ToolsShape_CamV");
	m_xPreviousPosition = Position;

	const Asura_ClientEntity_CameraVolume* pxClient = Asura_ClientEntity_CameraVolume::FindEntity(GetGuid());
	if (pxClient)
	{
		m_iPriority = pxClient->m_xVolume.GetPriority();
	}
}

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

////////////////////////
// AddOverlayControls //
////////////////////////
void Tools_Entity_CameraVolume::AddOverlayControls(Asura_Overlay* pxOverlay, Asura_Vector_2& xOffset)
{
	PARENT::AddOverlayControls(pxOverlay, xOffset);

	// Edit volume
	Asura_Dialog* pxDialog;
	pxDialog = new Asura_Function_Dialog("Edit Volume", xOffset, EditVolume);
	pxDialog->SetHelpText("Edit the volume for this entity.");
	pxOverlay->AddDialog(pxDialog);

	// Show all
	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
 	pxDialog = new Asura_Tickbox_Dialog("Show All", xOffset, &s_bShowAllVolumes);
	pxDialog->SetHelpText("Show volumes for all camera volume entities.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.x =  1.0f;
	xOffset.y += Asura_Dialog::DefaultSpacing();

	// Show selected volume
	pxDialog = new Asura_Tickbox_Dialog("Show Selected Volume", xOffset, &s_bShowSelectedVolume);
	pxDialog->SetHelpText("Show volume for the selected camera volume entity.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	// Move volume with object
 	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 entity's tools object is moved.\nThis is a global setting for all camera volumes.");
	pxOverlay->AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	// Move properties with object
	pxDialog = new Asura_Tickbox_Dialog("Move Properties With Object", xOffset, &s_bMovePropertiesWithObject);
	pxDialog->SetHelpText("Any camera positions or rails attached to this entity should be moved when the entity's tools object is moved.\nThis is a global setting for all camera volumes.");
	pxOverlay->AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::LargeSpacing();

	// Add priority
	pxDialog = new Asura_Numerical_Dialog("Priority", xOffset, &m_iPriority, 0, 100, 10, "%d", &Changed);
	pxDialog->SetHelpText("Set priority (0-100).");
	pxOverlay->AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::LargeSpacing();
}

///////////////////////////////
// UpdatePositionInAsuraData //
///////////////////////////////
void Tools_Entity_CameraVolume::UpdatePositionInAsuraData(bool bUpdateLinkedObjects)
{
	const Asura_Vector_3 xMove = (Position-m_xPreviousPosition);

	// Shift the bounding box by the amount that the object has moved.
	if (s_bMoveVolumeWithObject)
	{
		Asura_ClientEntity_CameraVolume* pxClient = Asura_ClientEntity_CameraVolume::FindEntity(GetGuid());

		if (pxClient)
		{
			pxClient->m_xVolume.Displace(xMove);	
		}
		if (m_pxVolumeController)
		{
			m_pxVolumeController->MoveVolumeObjects(xMove);
		}
	}

	if (s_bMovePropertiesWithObject)
	{
		// Displace the properties too.
		DisplaceProperties(xMove);
	}

	m_xPreviousPosition = Position;

	PARENT::UpdatePositionInAsuraData(bUpdateLinkedObjects);
}

////////////
// Update //
////////////
void Tools_Entity_CameraVolume::Update()
{
	PARENT::Update();
    
    bool bIsMessageEditing = Tools_Object_System::IsMessageEditing();
	ShowVolume( s_bShowAllVolumes || ( IsSelected() && s_bShowSelectedVolume && !bIsMessageEditing ) );

	// Copy priority across.
	Asura_ClientEntity_CameraVolume* pxClient = Asura_ClientEntity_CameraVolume::FindEntity(GetGuid());
	if (pxClient)
	{
		pxClient->m_xVolume.SetPriority(m_iPriority);
	}
}

////////////////
// EditVolume //
////////////////
void Tools_Entity_CameraVolume::EditVolume()
{
	Asura_Assert(ThisEntity, "Null entity");
	if (!ThisEntity) return;

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

	// Get it to create the volume objects.
	pxToolsEntity->m_pxVolumeController->StartVolumeEditing();
}
	
////////////////
// ShowVolume //
////////////////
void Tools_Entity_CameraVolume::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_CameraVolume_VolumeEdit_Controller(this);
	}

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

////////////////////////////
// CreateObjectOfThisType //
////////////////////////////
Tools_Object* Tools_Entity_CameraVolume::CreateObjectOfThisType() const
{
	Tools_Object* pxOldSelection = Tools_Object_System::SelectedObject;
	Project_Create();
	Tools_Object* pxNewSelection = Tools_Object_System::SelectedObject;

	return (pxOldSelection!=pxNewSelection) ? pxNewSelection : 0;
}

//////////
// Copy //
//////////
void Tools_Entity_CameraVolume::Copy(const Tools_Object* pxObject)
{
	PARENT::Copy(pxObject); // Copy active flag
	
	Asura_ClientEntity_CameraVolume* pxFromEntity = Asura_ClientEntity_CameraVolume::FindEntity(pxObject->GetGuid());
	Asura_ClientEntity_CameraVolume* pxToEntity   = Asura_ClientEntity_CameraVolume::FindEntity(GetGuid());

	if (!pxFromEntity || !pxToEntity)
	{
		Asura_Assert(0,"Couldn't find client entity to do copy");
		return;
	}

	// Delete the volume controller if it exists, since we will be replacing the volumes.
	delete m_pxVolumeController;
	m_pxVolumeController = 0;

	// Copy everything between the entities.
	const Asura_CameraMultiVolume& xVolumeFrom = pxFromEntity->m_xVolume;
	Asura_CameraMultiVolume& xVolumeTo = pxToEntity->m_xVolume;
	
	xVolumeTo = xVolumeFrom;

	if (s_bMoveVolumeWithObject)
	{
		// Move the volumes to take into account the different object positions.
		const Asura_Vector_3 xMove = Position - pxObject->Position;
		xVolumeTo.Displace(xMove);
	}

	// Copy the priority back to tools
	m_iPriority = pxToEntity->m_xVolume.GetPriority();
}

//////////////////
// UpdateClient //
//////////////////
void Tools_Entity_CameraVolume::UpdateClient()
{
	PARENT::UpdateClient(); // Copy the active flag to the client.
}

////////////////////////
// HandlesThisMessage //
////////////////////////
bool Tools_Entity_CameraVolume::HandlesThisMessage(int iMessageID)
{
	if (iMessageID == ASURA_MSG_ACTIVATE ||
	    iMessageID == ASURA_MSG_DEACTIVATE)
	{
		return true;
	}
	return false;

}

void Tools_Entity_CameraVolume::PrintInfo(float& fX, float& fY)
{
	PARENT::PrintInfo(fX, fY);

	const Asura_ClientEntity_CameraVolume* pxClient = Asura_ClientEntity_CameraVolume::FindEntity(GetGuid());

	if (pxClient)
	{
		const int iNumVol = pxClient->m_xVolume.GetNumVolumes();
		if (iNumVol)
		{
			Asura_Text_Output::Print(fX, fY++, 0xffffffff, 0, "Num Volumes : %d", iNumVol);
		}
		else
		{
			Asura_Text_Output::Print(fX,    fY,   0xffffffff, 0, "Num Volumes : 0");
			Asura_Text_Output::Print(fX+16, fY++, 0xffff0000, 0, "(Global)");
		}

		Asura_Text_Output::Print(fX, fY++, 0xffffffff, 0, "Priority : %d", pxClient->m_xVolume.GetPriority());
	}
}

//////////////////////////////////////////////
// Tools_CameraVolume_VolumeEdit_Controller //
//////////////////////////////////////////////
Tools_CameraVolume_VolumeEdit_Controller::Tools_CameraVolume_VolumeEdit_Controller(Tools_Entity_CameraVolume* pxToolsEntity)
: Tools_VolumeEdit_Controller(&pxToolsEntity->Changed)
, m_pxToolsEntity(pxToolsEntity)
, m_pxMultiVolume(0)
{
	if (pxToolsEntity && pxToolsEntity->Entity)
	{
		Asura_ClientEntity_CameraVolume* pxClient = Asura_ClientEntity_CameraVolume::FindEntity(pxToolsEntity->GetGuid());
		if (pxClient)
		{
			m_pxMultiVolume = &pxClient->m_xVolume;
		}
	}

	Asura_Assert(m_pxMultiVolume, "Failed to find data for volume edit controller.");
}

////////////////////////
// StartVolumeEditing //
////////////////////////
void Tools_CameraVolume_VolumeEdit_Controller::StartVolumeEditing()
{
	Tools_VolumeEdit_Controller::StartVolumeEditing();
	
	// Create a volume if none exists yet.
	if (!GetNumberOfVolumes()) CreateVolume();
}

//////////////////////
// EndVolumeEditing //
//////////////////////
void Tools_CameraVolume_VolumeEdit_Controller::EndVolumeEditing()
{
	Tools_VolumeEdit_Controller::EndVolumeEditing();
	// Select the owner.
	Tools_Object_System::SelectObject(m_pxToolsEntity);
}

//////////////////////
// CreateVolumeMain //
//////////////////////
void Tools_CameraVolume_VolumeEdit_Controller::CreateVolumeMain()
{
	if (m_pxMultiVolume)
	{
		m_pxMultiVolume->AddVolume();
	}
	else
	{
		Asura_Assert(0, "Error: no volume data specified.");
	}
}

//////////////////////
// DeleteVolumeMain //
//////////////////////
void Tools_CameraVolume_VolumeEdit_Controller::DeleteVolumeMain(int iIndex)
{
	if (m_pxMultiVolume)
	{
		m_pxMultiVolume->DeleteVolume(iIndex);
	}
	else
	{
		Asura_Assert(0, "Error: no volume data specified.");
	}
}

////////////////////////
// GetNumberOfVolumes //
////////////////////////
// Gets the current number of volumes.
int Tools_CameraVolume_VolumeEdit_Controller::GetNumberOfVolumes()
{
	return m_pxMultiVolume ? m_pxMultiVolume->GetNumVolumes() : 0;
}

//////////////////
// GetVolumeBox //
//////////////////
// Gets a pointer to the bounding box for a given volume.
Asura_Bounding_Box* Tools_CameraVolume_VolumeEdit_Controller::GetVolumeBox(int iIndex)
{
	if (m_pxMultiVolume)
	{
		Asura_OrientedVolume* const pxOrientVol = m_pxMultiVolume->GetVolume(iIndex);
		if (pxOrientVol)
		{
			return &(pxOrientVol->m_xBox);
		}
	}
	return 0;
}

/////////////////////
// GetVolumeOrient //
/////////////////////
// Gets a pointer to the orientation for a given volume. Returns 0 if there is none.
Asura_Quat* Tools_CameraVolume_VolumeEdit_Controller::GetVolumeOrient(int iIndex)
{
	if (m_pxMultiVolume)
	{
		Asura_OrientedVolume* const pxOrientVol = m_pxMultiVolume->GetVolume(iIndex);
		if (pxOrientVol)
		{
			return &(pxOrientVol->m_xOrient);
		}
	}
	return 0;
}

///////////////////
// IsHighlighted //
///////////////////
// Should highlight the volumes when rendering them?
bool Tools_CameraVolume_VolumeEdit_Controller::IsHighlighted() const
{
	if (Tools_Entity_CameraVolume::ShouldShowSelectedVolume() &&
		Tools_Entity_CameraVolume::ShouldShowAllVolumes())
	{
		// When doing "Show all" and "Show selected volume" then colour the volumes
		// associated with the selected camera volume entity.
		return m_pxToolsEntity ? m_pxToolsEntity->IsSelected() : false;
	}
	return false;
}

#endif //ASURA_USE_CAMERA_VOLUME

