//////////////////////////////////////////////////////////////////////
// Tools_VolumeGroupEdit_Controller.cpp						//
// SJL 13/11/07														//
// Copyright (C) 2007 Rebellion, All Rights Reserved.			    //
//////////////////////////////////////////////////////////////////////
#include "Core/Asura.h"

// This file's header
#include "Objects/Tools_VolumeGroupEdit_Controller.h"

// Asura
#include "Camera/Asura_Camera.h"
#include "Core/Asura_Los.h"
#include "Core/Asura_UserInput.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Assert.h"
#include "Interface/Asura_Interface.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Cursor.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Mesh/Asura_Mesh.h"
#include "Rendering/Asura_Text_Output.h"
#include "Rendering/Asura_Module.h"
#include "Rendering/Asura_Render.h"
#include "Volume/Asura_VolumeGroup.h"

// Tools
#include "Tools_Overlays.h"
#include "Misc/Tools_Render.h"
#include "Misc/Tools_LOS.h"
#include "Misc/Tools_Help.h"
#include "Misc/Tools_Timed_Messages.h"
#include "Misc/Tools_Modal_Overlay.h"
#include "Objects/Tools_Object_System.h"
#include "Objects/Tools_VolumeBaseEdit_Object.h"
#include "Objects/Tools_VolumeCuboidEdit_Object.h"
#include "Objects/Tools_VolumeSphereEdit_Object.h"
#include "Objects/Tools_VolumeCylinderEdit_Object.h"



Tools_VolumeGroupEdit_Controller::Tools_VolumeGroupEdit_Controller(bool* pbModified)
{
	m_iNumberOfVolumes = 0;
	m_puVolumeIDs = 0;
	m_pbModified = pbModified;
	m_iSelectedIndex = 0;
	m_bSelectedIndexChanged = false;
}

Tools_VolumeGroupEdit_Controller::~Tools_VolumeGroupEdit_Controller()
{
	DeleteVolumeObjects();
}

void Tools_VolumeGroupEdit_Controller::CreateVolumeObjects(bool bSelect)
{
	if(!m_iNumberOfVolumes)
	{
		m_iNumberOfVolumes = GetNumberOfVolumes();
		if(!m_iNumberOfVolumes) return;

		m_puVolumeIDs = new u_int[m_iNumberOfVolumes];
		for(int i=0;i<m_iNumberOfVolumes;i++)
		{
			m_puVolumeIDs[i] = 0;
		}
	}
	else
	{
		Asura_Assert(m_iNumberOfVolumes == GetNumberOfVolumes(),"Number of volumes out of sync");
	}


	//create a volume object for each volume
	for(int i=0;i<m_iNumberOfVolumes;i++)
	{
		CreateVolumeObject(i);
	}
	if(bSelect) SelectVolume(m_iSelectedIndex);
}

void Tools_VolumeGroupEdit_Controller::CreateVolumeObject(int iIndex)
{
	if(!m_iNumberOfVolumes) return;
	Asura_Assert(iIndex>=0 && iIndex<m_iNumberOfVolumes,"");

	Tools_VolumeBaseEdit_Object* pxVolume = Tools_VolumeBaseEdit_Object::Find(m_puVolumeIDs[iIndex]);
	if(!pxVolume)
	{
		char szVolumeName[200];
		sprintf(szVolumeName,"%s - Volume %d",GetName(),iIndex);

		switch (GetVolume(iIndex)->GetClassification())
		{
			case ASURA_VOLUME_CLASSIFICATION_CUBOID:
				pxVolume = new Tools_VolumeCuboidEdit_Object(szVolumeName  , static_cast<Asura_Volume_Cuboid*>(GetVolume(iIndex)), m_pbModified);
				break;
			case ASURA_VOLUME_CLASSIFICATION_SPHERE:
				pxVolume = new Tools_VolumeSphereEdit_Object(szVolumeName  , static_cast<Asura_Volume_Sphere*>(GetVolume(iIndex)), m_pbModified);
				break;
			case ASURA_VOLUME_CLASSIFICATION_CYLINDER:
				pxVolume = new Tools_VolumeCylinderEdit_Object(szVolumeName, static_cast<Asura_Volume_Cylinder*>(GetVolume(iIndex)), m_pbModified);
				break;
			default:
				Asura_Assert(0,"Code error");

			
		}
		Asura_Assert(pxVolume, "");

		if(GetVolumeOrient(iIndex)) { pxVolume->SetVolumeOrientation(GetVolumeOrient(iIndex)); }
		pxVolume->SetController(this);

		//store the ID for the volume
		m_puVolumeIDs[iIndex] = pxVolume->GetID();	
	}

}

void Tools_VolumeGroupEdit_Controller::DeleteVolumeObjects()
{
	for(int i=0;i<m_iNumberOfVolumes;i++)
	{
		Tools_VolumeBaseEdit_Object::Delete(m_puVolumeIDs[i]);
	}
	delete [] m_puVolumeIDs;
	m_puVolumeIDs = 0;
	m_iNumberOfVolumes = 0;
}

void Tools_VolumeGroupEdit_Controller::ShowVolumes(bool bShow)
{
	if(bShow)
	{
		CreateVolumeObjects(false);
	}
	else 
	{
		DeleteVolumeObjects();
	}
}

bool Tools_VolumeGroupEdit_Controller::IsSelected() const
{
	Tools_VolumeBaseEdit_Object* pxSelection = Tools_VolumeBaseEdit_Object::GetSelectedVolume();
	if(!pxSelection) return false;

	for(int i=0;i<m_iNumberOfVolumes;i++)
	{
		if(m_puVolumeIDs[i] == pxSelection->GetID())
		{
			return true;
		}
	}
	return false;
}


void Tools_VolumeGroupEdit_Controller::VolumeSelected(const Tools_VolumeBaseEdit_Object* pxVolume)
{
	m_bSelectedIndexChanged = false;
	m_iSelectedIndex = -1;
	for(int i=0;i<m_iNumberOfVolumes;i++)
	{
		if(m_puVolumeIDs[i] == pxVolume->GetID())
		{
			m_iSelectedIndex = i;
			break;
		}
	}
	Asura_Assert(m_iSelectedIndex != -1,"");
}

void Tools_VolumeGroupEdit_Controller::AddOverlayControls(class Asura_Overlay* pxOverlay,Asura_Vector_2& xOffset)
{
	Asura_Dialog* pxDialog;

	if(CanCreateVolume())
	{
		xOffset.y+=Asura_Dialog::LargeSpacing()-Asura_Dialog::DefaultSpacing();
		pxDialog = new Asura_Function_Dialog("Create Cuboid",xOffset,CreateVolumeCuboid,(int) this);
		pxDialog->SetHelpText("Add an extra cuboid volume to this object.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
	}

	if(CanCreateVolume())
	{
//		xOffset.y+=Asura_Dialog::LargeSpacing()-Asura_Dialog::DefaultSpacing();
		pxDialog = new Asura_Function_Dialog("Create Sphere",xOffset,CreateVolumeSphere,(int) this);
		pxDialog->SetHelpText("Add an extra sphere volume to this object.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
	}
	if(CanCreateVolume())
	{
//		xOffset.y+=Asura_Dialog::LargeSpacing()-Asura_Dialog::DefaultSpacing();
		pxDialog = new Asura_Function_Dialog("Create Cylinder",xOffset,CreateVolumeCylinder,(int) this);
		pxDialog->SetHelpText("Add an extra cylinder volume to this object.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
	}

	if(CanDeleteVolume())
	{
		xOffset.y+=Asura_Dialog::LargeSpacing()-Asura_Dialog::DefaultSpacing();
		pxDialog = new Asura_Function_Dialog("Delete Volume",xOffset,DeleteVolume,(int) this);
		pxDialog->SetHelpText("Permanently delete this volume.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
	}
	if(m_iNumberOfVolumes>1)
	{
		xOffset.y+=Asura_Dialog::LargeSpacing()-Asura_Dialog::DefaultSpacing();
		pxDialog = new Asura_Numerical_Dialog("Selected Volume",xOffset,&m_iSelectedIndex,0,m_iNumberOfVolumes-1,1,"%d",&m_bSelectedIndexChanged);
		pxDialog->SetHelpText("Change the currently selected volume in this set.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
	}

	Asura_Vector_2* pxPosition = GetOverlayPosition();
	if(pxPosition)
	{
		pxOverlay->m_xPosition = *pxPosition;
		pxOverlay->m_pxPositionBackup = pxPosition;
	}

}

void Tools_VolumeGroupEdit_Controller::Update()
{
	//check for selection being changed using overlay controls
	if(m_bSelectedIndexChanged)
	{
		m_bSelectedIndexChanged = false;
		SelectVolume(m_iSelectedIndex);
	}

	// If anything gets deselected, reselect it.
	// We must have one of our volumes selected at any one time.
	const Tools_Object* pxSelectedObject = Tools_Object_System::SelectedObject;
	if(!pxSelectedObject || pxSelectedObject->Type != TOT_VolumeGroupEdit)
	{
		SelectVolume(m_iSelectedIndex);
	}
}

void Tools_VolumeGroupEdit_Controller::PrintInfo(float& x,float& y)
{
	Asura_Text_Output::Print(x,y++,0xffffffff,0,"Volumes : %d",GetNumberOfVolumes());
}

void Tools_VolumeGroupEdit_Controller::MoveVolumeObjects(const Asura_Vector_3& xOffset)
{
	for(int i=0;i<m_iNumberOfVolumes;i++)
	{
		Tools_VolumeBaseEdit_Object* pxVolume = Tools_VolumeBaseEdit_Object::Find(m_puVolumeIDs[i]);
		if(pxVolume)
		{
			pxVolume->Displace(xOffset);
		}
	}
}

void Tools_VolumeGroupEdit_Controller::RotateVolumeObjectsAbout(const Asura_Vector_3& xCenter, const Asura_Matrix_3x3& xOri)
{
	for(int i=0;i<m_iNumberOfVolumes;i++)
	{
		Tools_VolumeBaseEdit_Object* pxVolume = Tools_VolumeBaseEdit_Object::Find(m_puVolumeIDs[i]);
		if(pxVolume)
		{
			pxVolume->RotateAbout(xCenter, xOri);
		}
	}
}


void Tools_VolumeGroupEdit_Controller::StartVolumeEditing()
{
	CreateVolumeObjects();
}

void Tools_VolumeGroupEdit_Controller::EndVolumeEditing()
{
	DeleteVolumeObjects();
}

void Tools_VolumeGroupEdit_Controller::CreateVolumeCuboid()
{
	if(!CanCreateVolume()) return;

	//Need to delete all the volume objects first (since the pointers will most likely become invalid)
	DeleteVolumeObjects();

	//Add a new volume
	CreateVolumeCuboidMain();

	m_iSelectedIndex = GetNumberOfVolumes()-1;

	//And create the volume objects again
	CreateVolumeObjects();

	Tools_Timed_Message_System::AddMessage(0xffffff,"Volume created");
	if(m_pbModified) *m_pbModified = true;
}

void Tools_VolumeGroupEdit_Controller::CreateVolumeSphere()
{
	if(!CanCreateVolume()) return;

	//Need to delete all the volume objects first (since the pointers will most likely become invalid)
	DeleteVolumeObjects();

	//Add a new volume
	CreateVolumeSphereMain();

	m_iSelectedIndex = GetNumberOfVolumes()-1;

	//And create the volume objects again
	CreateVolumeObjects();

	Tools_Timed_Message_System::AddMessage(0xffffff,"Volume created");
	if(m_pbModified) *m_pbModified = true;
}

void Tools_VolumeGroupEdit_Controller::CreateVolumeCylinder()
{
	if(!CanCreateVolume()) return;

	//Need to delete all the volume objects first (since the pointers will most likely become invalid)
	DeleteVolumeObjects();

	//Add a new volume
	CreateVolumeCylinderMain();

	m_iSelectedIndex = GetNumberOfVolumes()-1;

	//And create the volume objects again
	CreateVolumeObjects();

	Tools_Timed_Message_System::AddMessage(0xffffff,"Volume created");
	if(m_pbModified) *m_pbModified = true;
}


void Tools_VolumeGroupEdit_Controller::DeleteVolume(u_int uVolumeID /* = 0 */)
{
	if(!CanDeleteVolume()) return;
	Asura_Assert(m_iNumberOfVolumes,"");

	int iIndexToDelete = m_iSelectedIndex;
	if(uVolumeID)
	{
		iIndexToDelete = -1;
		for(int iVolume = 0; iVolume < m_iNumberOfVolumes; ++iVolume)
		{
			if(m_puVolumeIDs[iVolume] == uVolumeID)
			{
				iIndexToDelete = iVolume;
			}
		}
		Asura_Assert(iIndexToDelete >= 0, "Could not find volume to delete");
	}
	if(iIndexToDelete < 0 || iIndexToDelete >= m_iNumberOfVolumes) return;

	YesNo_Overlay xOverlay("Delete this volume?.");
	if(!xOverlay.DoModal()) return;

	//Delete the volume objects
	DeleteVolumeObjects();
	//delete the volume
	DeleteVolumeMain(iIndexToDelete);
	//and recreate the volume objects
	CreateVolumeObjects();

	if(m_pbModified) *m_pbModified = true;

}

void Tools_VolumeGroupEdit_Controller::SelectVolume(int iIndex)
{
	if(!m_iNumberOfVolumes) return;
	m_iSelectedIndex = Asura_Maths::ClampToRange(iIndex,0,m_iNumberOfVolumes-1);

	//make the volume to be selected , if needed
	CreateVolumeObject(m_iSelectedIndex);

	//and select it
	Tools_VolumeBaseEdit_Object* pxVolume = Tools_VolumeBaseEdit_Object::Find(m_puVolumeIDs[m_iSelectedIndex]);
	Tools_Object_System::SelectObject(pxVolume);

}

const Tools_VolumeBaseEdit_Object* Tools_VolumeGroupEdit_Controller::GetToolsVolume(u_int uIndex) const
{
	return Tools_VolumeBaseEdit_Object::Find(m_puVolumeIDs[uIndex]);
}


//////////////////////////////////////////////////////////////////////////////////////////////////
/*------------------------------------------**
** Tools_Default_VolumeGroupEdit_Controller **
**------------------------------------------*/

Tools_Default_VolumeGroupEdit_Controller::Tools_Default_VolumeGroupEdit_Controller(const char* szName, Asura_VolumeGroup* pxVolumeGroup, bool* pbChangedFlag)
:Tools_VolumeGroupEdit_Controller(pbChangedFlag)
, m_pxVolumeGroup(pxVolumeGroup)
, m_szName(szName)
{
	Asura_Assert(pxVolumeGroup,"");
	Asura_Assert(szName,"");
}

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

void Tools_Default_VolumeGroupEdit_Controller::CreateVolumeCuboidMain()
{
	Asura_Volume_Cuboid* pxVolume = new Asura_Volume_Cuboid();
	pxVolume->SetPosition(GetVolumeCreationPosition());
	m_pxVolumeGroup->AddVolume(pxVolume);
}

void Tools_Default_VolumeGroupEdit_Controller::CreateVolumeSphereMain()
{
	Asura_Volume_Sphere* pxVolume = new Asura_Volume_Sphere();
	pxVolume->SetPosition(GetVolumeCreationPosition());
	m_pxVolumeGroup->AddVolume(pxVolume);
}

void Tools_Default_VolumeGroupEdit_Controller::CreateVolumeCylinderMain()
{
	Asura_Volume_Cylinder* pxVolume = new Asura_Volume_Cylinder();
	pxVolume->SetPosition(GetVolumeCreationPosition());
	m_pxVolumeGroup->AddVolume(pxVolume);
}

void Tools_Default_VolumeGroupEdit_Controller::DeleteVolumeMain(int iIndex)
{
	m_pxVolumeGroup->RemoveAndDestroyVolume(static_cast<u_int>(iIndex));
	if(!m_pxVolumeGroup->GetNumberOfVolumes())
	{
		// No more volumes, give up editing.
		EndVolumeEditing();
	}
}

const Asura_Vector_3& Tools_Default_VolumeGroupEdit_Controller::GetVolumeCreationPosition() const
{
	return *Asura_Camera::GetPosition();
}

Asura_Volume* Tools_Default_VolumeGroupEdit_Controller::GetVolume(u_int uIndex)
{
	return m_pxVolumeGroup->GetVolume(uIndex);
}

int Tools_Default_VolumeGroupEdit_Controller::GetNumberOfVolumes()
{
	return m_pxVolumeGroup->GetNumberOfVolumes();
}

Asura_Quat* Tools_Default_VolumeGroupEdit_Controller::GetVolumeOrient(int iIndex)
{
	return m_pxVolumeGroup->GetVolumeOrient(static_cast<u_int>(iIndex));
}

//////////////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------------------------**
** Tools_Object_VolumeGroupEdit_Controller **
**-----------------------------------------*/

Tools_Object_VolumeGroupEdit_Controller::Tools_Object_VolumeGroupEdit_Controller(Tools_Object* pxOwnerObject, Asura_VolumeGroup* pxVolumeGroup, bool* pbChangedFlag)
: Tools_Default_VolumeGroupEdit_Controller(pxOwnerObject->GetName(), pxVolumeGroup, pbChangedFlag)
, m_pxOwnerObject(pxOwnerObject)
{
	Asura_Assert(pxOwnerObject,"");
}

void Tools_Object_VolumeGroupEdit_Controller::EndVolumeEditing()
{
	Tools_Default_VolumeGroupEdit_Controller::EndVolumeEditing();
	//select the owner
	Tools_Object_System::SelectObject(m_pxOwnerObject);
}

const Asura_Vector_3& Tools_Object_VolumeGroupEdit_Controller::GetVolumeCreationPosition() const
{
	return m_pxOwnerObject->Position;
}
