//////////////////////////////////////////////////////////////////////
// Tools_Entity_VolumeTrigger.cpp									//
// SJL 6/11/07														//
// Copyright (C) 2007 Rebellion, All Rights Reserved.			    //
//////////////////////////////////////////////////////////////////////

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

#include "Debugging/Asura_Assert.h"
#include "Entity/Asura_Entity.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/Triggers/Asura_ServerEntity_VolumeTrigger.h"
#include "Entity/Asura_StaticMessage_System.h"
#include "Entity/Triggers/Asura_ServerEntity_CountedTrigger.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Overlay.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Objects/Tools_VolumeBaseEdit_Object.h"
#include "Objects/Tools_Object_System.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Entity/Tools_Entity_PhysicalObject.h"
#include "Misc/Tools_Timed_Messages.h"

#include "Volume/Asura_Volume.h"
#include "Volume/Asura_Volume_Cuboid.h"
#include "Volume/Asura_Volume_Sphere.h"
#include "Volume/Asura_Volume_Cylinder.h"
#include "Volume/Asura_VolumeGroup.h"

// Our header
#include "Tools_Entity_VolumeTrigger.h"

bool Tools_Entity_VolumeTrigger::s_bMoveVolumeWithObject = true;
bool Tools_Entity_VolumeTrigger::s_bShowAllVolumes = false;
bool Tools_Entity_VolumeTrigger::s_bShowSelectedVolume = false;

static Tools_Entity_VolumeTrigger* s_pxLastSelected = NULL;

//flag values for use with tickboxes... must correspond to the bitfield positions in the entity flags
#define ASURAVOLUMETRIGGERFLAG_TRIGGERONCE 0x00000002
#define ASURAVOLUMETRIGGERFLAG_TRIGGERONNOTOCCUPIED 0x00000004
#define ASURAVOLUMETRIGGERFLAG_TRIGGERONCLIENT 0x0000008
#define ASURAVOLUMETRIGGERFLAG_TRIGGERONSERVER 0x0000010

class Tools_ChooseVolumesParentObject : public Tools_ChooseObject
{
public :
	Tools_ChooseVolumesParentObject (Tools_Entity_VolumeTrigger& pxVolumeTrigger)
		: m_pxVolumeTrigger (pxVolumeTrigger)
	{}

	virtual bool CanBeSelected (Tools_Object* pxObject);
	virtual void ObjectSelected (Tools_Object* pxObject);
	virtual void OnFinish () { delete this; }

private:
	Tools_Entity_VolumeTrigger& m_pxVolumeTrigger;
};

Asura_Function_Dialog* Tools_Entity_VolumeTrigger::s_pxLinkToParentDialogButton = NULL;

Tools_Entity_VolumeTrigger::Tools_Entity_VolumeTrigger(Asura_Entity* pxEntity)
:Tools_Entity_Trigger_Object(pxEntity)
{
	Type = TOT_Entity_VolumeTrigger;
	Mesh = Asura_Mesh_System::GetMesh("ToolsShape_V");
	m_xPreviousPosition = Position;
	m_xPreviousOrientation = Orientation;
	m_pxVolumeController = 0;
}

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

void Tools_Entity_VolumeTrigger::AddOverlayControls(class Asura_Overlay* pxOverlay,class Asura_Vector_2& xOffset)
{
	Tools_Entity_Trigger_Object::AddOverlayControls(pxOverlay,xOffset);

	Asura_ServerEntity_VolumeTrigger* pxAsuraEntity = (Asura_ServerEntity_VolumeTrigger*) Entity;
	s_pxLastSelected = this;
	
	Asura_Dialog* pxDialog;
	pxDialog = new Asura_Function_Dialog("Edit volume",xOffset,EditVolume);
	pxDialog->SetHelpText("Edits the volume for this trigger.");
	pxOverlay->AddDialog(pxDialog);

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

	pxDialog = new Asura_Tickbox_Dialog( "Show selected", xOffset, &s_bShowSelectedVolume);
	pxDialog->SetHelpText("Shows volume for last selected volume trigger.");
	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 trigger should be moved when the trigger's tools object is moved.\nThis is a global setting for all trigger volumes.");
	pxOverlay->AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::LargeSpacing();

    pxDialog = new Asura_FlagTickbox_Dialog( "Trigger once", xOffset, &pxAsuraEntity->m_uFlags,ASURAVOLUMETRIGGERFLAG_TRIGGERONCE,&Changed);
	pxDialog->SetHelpText("This trigger will deactivate itself once it becomes untriggered.\n Therefore it will only be triggerable once , unless it is re-activated.");
	pxOverlay->AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

    pxDialog = new Asura_FlagTickbox_Dialog( "Trigger on not occupied", xOffset, &pxAsuraEntity->m_uFlags,ASURAVOLUMETRIGGERFLAG_TRIGGERONNOTOCCUPIED,&Changed);
	pxDialog->SetHelpText("The volume trigger becomes triggered if it isn't occupied by anyone.");
	pxOverlay->AddDialog( pxDialog );
	xOffset.y += Asura_Dialog::LargeSpacing();

	if (AllowClientVolumes())
	{
		pxDialog = new Asura_FlagTickbox_Dialog( "Trigger on client", xOffset, &pxAsuraEntity->m_uFlags,ASURAVOLUMETRIGGERFLAG_TRIGGERONCLIENT,&Changed);
		pxDialog->SetHelpText("The volume trigger triggers on the client.");
		pxOverlay->AddDialog( pxDialog );
		xOffset.y += Asura_Dialog::LargeSpacing();
	}

	if (pxAsuraEntity->GetPhysicalObjectParentGuid () == ASURA_GUID_UNREGISTERED)
	{
		s_pxLinkToParentDialogButton = new Asura_Function_Dialog("Link to Parent", xOffset, LinkToParent);
	}
	else
	{
		Tools_Entity_PhysicalObject* pxParentObject = static_cast <Tools_Entity_PhysicalObject *> (Tools_Entity_PhysicalObject::FindEntity (pxAsuraEntity->GetPhysicalObjectParentGuid ()));
		//Asura_Assert (pxParentObject, "Could not find parent object from GUID");

		if (pxParentObject)
		{
			Asura_Char szBuffer [256];

			Asura_PrintfToBfr (szBuffer, "Link to Parent (%s)", 256, true, pxParentObject->GetName ());

			s_pxLinkToParentDialogButton = new Asura_Function_Dialog (szBuffer, xOffset, LinkToParent);
		}
		else
		{
			Tools_Timed_Message_System::AddUnloggedMessage (0xff0000, "ERROR: Volume trigger parent's GUID is no longer valid.  Has this object been deleted?");
			Tools_MessageLog::AddMessage ("Volume Triggers", "Invalid GUID", new Tools_MessageLogAction_GotoEntity (GetGuid ()), "ERROR: Volume trigger parent's GUID is no longer valid.  Has this object been deleted?");

			//SetParentObjectGuid (ASURA_GUID_UNREGISTERED);

			s_pxLinkToParentDialogButton = new Asura_Function_Dialog("Link to Parent", xOffset, LinkToParent);
		}
	}

	s_pxLinkToParentDialogButton->SetHelpText ("This will attach a parent physical object to this volume trigger.  All volumes in this volume trigger will follow the parent around.");

	pxOverlay->AddDialog(s_pxLinkToParentDialogButton);
	xOffset.y += s_pxLinkToParentDialogButton->LargeSpacing ();
}

void Tools_Entity_VolumeTrigger::LinkToParent ()
{
	Asura_Assert(ThisEntity,"");

	Tools_Entity_VolumeTrigger* pxToolsEntity = (Tools_Entity_VolumeTrigger*) ThisEntity;

	Tools_ChooseVolumesParentObject* pxChooseObject = new Tools_ChooseVolumesParentObject (*pxToolsEntity);

	Tools_Object_System::ChooseObject ("Please select an object to attach to the volume trigger.", pxChooseObject);
}

void Tools_Entity_VolumeTrigger::SetParentObjectGuid (const Asura_Guid uGuid)
{
	// Set the link in the Asura_ServerEntity_VolumeTrigger version of this object to point to the parent

	Asura_ServerEntity_VolumeTrigger* pxServerVolumeTrigger = static_cast <Asura_ServerEntity_VolumeTrigger *> (Asura_ServerEntity_VolumeTrigger::FindEntity (GetGuid ()));
	Asura_Assert (pxServerVolumeTrigger, "Failed to find server entity volume trigger");

	if (pxServerVolumeTrigger)
	{
		pxServerVolumeTrigger->SetPhysicalObjectParentGuid (uGuid);

		SetEntityModified ();

		// Update the overlay so that the button mentions the parent object's name in brackets

		this->UpdateOverlay ();
	}
}

void Tools_Entity_VolumeTrigger::UpdateOverlayControls (class Asura_Overlay* pxOverlay)
{
	Asura_ServerEntity_VolumeTrigger* pxAsuraEntity = (Asura_ServerEntity_VolumeTrigger*) Entity;

	if (pxAsuraEntity->GetPhysicalObjectParentGuid () == ASURA_GUID_UNREGISTERED)
	{
		s_pxLinkToParentDialogButton->m_xName = "Link to Parent";
	}
	else
	{
		Tools_Entity_PhysicalObject* pxParentObject = static_cast <Tools_Entity_PhysicalObject *> (Tools_Entity_PhysicalObject::FindEntity (pxAsuraEntity->GetPhysicalObjectParentGuid ()));
		//Asura_Assert (pxParentObject, "Could not find parent object from GUID");

		if (pxParentObject)
		{
			Asura_Char szBuffer [256];

			Asura_PrintfToBfr (szBuffer, "Link to Parent (%s)", 256, true, pxParentObject->GetName ());

			s_pxLinkToParentDialogButton->m_xName = szBuffer;
		}
	}
}

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

void Tools_Entity_VolumeTrigger::UpdatePositionInAsuraData(bool bUpdateLinkedObjects)
{
	Asura_ServerEntity_VolumeTrigger* pxAsuraEntity = (Asura_ServerEntity_VolumeTrigger*) Entity;
	if(s_bMoveVolumeWithObject)
	{
		Asura_Vector_3 xOffset = Position-m_xPreviousPosition;
		pxAsuraEntity->DisplaceVolumes(xOffset);
		if(m_pxVolumeController)
		{
			m_pxVolumeController->MoveVolumeObjects(xOffset);
		}
	
#if 1
		Asura_Euler_3 xLast;
		xLast.FromMatrix(m_xPreviousOrientation);
		Asura_Euler_3 xThis;
		xThis.FromMatrix(Orientation);
		Asura_Euler_3 xRot = xThis;
		xThis.Element[0]-=xLast.Element[0];
		xThis.Element[1]-=xLast.Element[1];
		xThis.Element[2]-=xLast.Element[2];
		Asura_Matrix_3x3 xAsMat;
		xThis.ToMatrix(xAsMat);

		if (xThis.MagnitudeSquared() > 0.0f)
		{
			pxAsuraEntity->RotateVolumesAbout(Position, xAsMat);
			if(m_pxVolumeController)
			{
				m_pxVolumeController->RotateVolumeObjectsAbout(Position, xAsMat);
			}
		}
#endif

	}
	


	m_xPreviousOrientation = Orientation;
	m_xPreviousPosition = Position;
	Tools_Entity_Trigger_Object::UpdatePositionInAsuraData(bUpdateLinkedObjects);
}

void Tools_Entity_VolumeTrigger::Update()
{
	Tools_Entity_Trigger_Object::Update();

	ShowVolume(s_bShowAllVolumes);
}

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

	Tools_Entity_VolumeTrigger* pxToolsEntity = (Tools_Entity_VolumeTrigger*) ThisEntity;
	//create controller for editing volumes if it hasn't been done yet

	if(!pxToolsEntity->m_pxVolumeController)
	{
		pxToolsEntity->m_pxVolumeController = new Tools_VolumeTrigger_VolumeEdit_Controller(pxToolsEntity);
	}

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


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

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

	if (s_bShowSelectedVolume)
	{
		if (this == s_pxLastSelected) bShow =true;
	}

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

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

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

void Tools_Entity_VolumeTrigger::Copy(const Tools_Object* pxObject)
{
	Tools_Entity_Trigger_Object::Copy(pxObject);
	
	const Tools_Entity_VolumeTrigger* pxFromObject = (const Tools_Entity_VolumeTrigger*) pxObject;

	Asura_ServerEntity_VolumeTrigger* pxFromEntity = (Asura_ServerEntity_VolumeTrigger*) pxFromObject->Entity;
	Asura_ServerEntity_VolumeTrigger* pxToEntity = (Asura_ServerEntity_VolumeTrigger*) Entity;

	//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
	pxToEntity->m_uFlags = pxFromEntity->m_uFlags;
//	pxToEntity->m_iListFlags = pxFromEntity->m_iListFlags;
	pxToEntity->m_uSpecificGuid = pxFromEntity->m_uSpecificGuid;
	
	pxToEntity->m_pxVolumeGroup->Copy(pxFromEntity->m_pxVolumeGroup);

	Asura_Vector_3 xDisplace = Position-pxObject->Position;
	if (s_bMoveVolumeWithObject) pxToEntity->m_pxVolumeGroup->DisplaceVolumes(xDisplace);
}

void Tools_Entity_VolumeTrigger::UpdateClient()
{
	if(!m_pxClientEntity) return;

#if 0 // TODO work out what this is for n redo.
	Tools_Entity_Trigger_Object::UpdateClient();

	Asura_ServerEntity_VolumeTrigger* pxServerEntity = (Asura_ServerEntity_VolumeTrigger*) Entity;
	Asura_ClientEntity_VolumeTrigger* pxClientEntity = (Asura_ClientEntity_VolumeTrigger*) m_pxClientEntity;

	pxClientEntity->m_iListFlags = pxServerEntity->m_iListFlags;
	pxClientEntity->m_uSpecificGuid = pxServerEntity->m_uSpecificGuid;
	pxClientEntity->m_uFlags = pxServerEntity->m_uFlags;

	delete [] pxClientEntity->m_pxVolumes;

	pxClientEntity->m_iNumberOfVolumes = pxServerEntity->m_iNumberOfVolumes;
	pxClientEntity->m_pxVolumes = pxClientEntity->m_iNumberOfVolumes ? new  Asura_OrientedVolume[pxClientEntity->m_iNumberOfVolumes] : 0;

	for(int i =0;i<pxClientEntity->m_iNumberOfVolumes;i++)
	{
		pxClientEntity->m_pxVolumes[i] = pxServerEntity->m_pxVolumes[i];
	}
#endif
}




///////////////////////////////////////////////
// Tools_VolumeTrigger_VolumeEdit_Controller //
///////////////////////////////////////////////


Tools_VolumeTrigger_VolumeEdit_Controller::Tools_VolumeTrigger_VolumeEdit_Controller(Tools_Entity_VolumeTrigger* pxToolsEntity)
:Tools_VolumeGroupEdit_Controller(&pxToolsEntity->Changed)
{
	m_pxToolsEntity = pxToolsEntity;
	m_pxAsuraEntity = (Asura_ServerEntity_VolumeTrigger*) pxToolsEntity->Entity;
}

void Tools_VolumeTrigger_VolumeEdit_Controller::StartVolumeEditing()
{
	Tools_VolumeGroupEdit_Controller::StartVolumeEditing();
	if(GetNumberOfVolumes() == 0)
	{
		//create a volume if none exists yet
		CreateVolumeCuboid();
	}
}

void Tools_VolumeTrigger_VolumeEdit_Controller::EndVolumeEditing()
{
	Tools_VolumeGroupEdit_Controller::EndVolumeEditing();
	//select the owner
	Tools_Object_System::SelectObject(m_pxToolsEntity);
}


void Tools_VolumeTrigger_VolumeEdit_Controller::CreateVolumeCuboidMain()
{
	Asura_Volume_Cuboid* pxVolume = new Asura_Volume_Cuboid();
	Asura_Bounding_Box xBox;
	xBox.MinX = m_pxToolsEntity->Position.x-1.0f;
	xBox.MaxX = m_pxToolsEntity->Position.x+1.0f;
	xBox.MinY = m_pxToolsEntity->Position.y-4.0f;
	xBox.MaxY = m_pxToolsEntity->Position.y+1.0f;
	xBox.MinZ = m_pxToolsEntity->Position.z-4.0f;
	xBox.MaxZ = m_pxToolsEntity->Position.z+4.0f;
	pxVolume->SetBoundingBox(xBox);
	m_pxAsuraEntity->AddVolume(pxVolume);
}

void Tools_VolumeTrigger_VolumeEdit_Controller::CreateVolumeSphereMain()
{
	Asura_Volume_Sphere* pxVolume = new Asura_Volume_Sphere();
	pxVolume->SetPosition(m_pxToolsEntity->Position);
	m_pxAsuraEntity->AddVolume(pxVolume);
}

void Tools_VolumeTrigger_VolumeEdit_Controller::CreateVolumeCylinderMain()
{
	Asura_Volume_Cylinder* pxVolume = new Asura_Volume_Cylinder();
	pxVolume->SetPosition(m_pxToolsEntity->Position);
	m_pxAsuraEntity->AddVolume(pxVolume);
}

void Tools_VolumeTrigger_VolumeEdit_Controller::DeleteVolumeMain(int iIndex)
{
	m_pxAsuraEntity->RemoveVolume(static_cast<u_int>(iIndex));
}

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

bool Tools_ChooseVolumesParentObject::CanBeSelected (Tools_Object* pxObject)
{
	Asura_Assert(pxObject, "Null tools object");
	if (!pxObject) return false;

	//allow object if its derived physical object , or if it's the player
	const Asura_Guid uGuid = pxObject->GetGuid();
	if(uGuid == ASURA_GUID_UNREGISTERED) return false;

	// Add all the special guids except for a few that obviously make no sense.
	if (pxObject && pxObject->Type == TOT_SpecialGuid)
	{
		if (uGuid == ASURA_GUID_SERVER ||
			uGuid == ASURA_GUID_ALL_CLIENTMACHINES ||
			uGuid == ASURA_GUID_DIALOGUE_SPEAKER)
		{
			return false;
		}
		return true;
	}

	if(Asura_ServerEntity_PhysicalObject::FindEntity(uGuid))
	{
		return true;
	}

	return false;
}

void Tools_ChooseVolumesParentObject::ObjectSelected (Tools_Object* pxObject)
{
	if (pxObject)
	{
		m_pxVolumeTrigger.SetParentObjectGuid (pxObject->GetGuid ());
	}
	else
	{
		m_pxVolumeTrigger.SetParentObjectGuid (ASURA_GUID_UNREGISTERED);
	}
}

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