////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001.
// -------------------------------------------------------------------------
//  File name:   ObjectManager.cpp
//  Version:     v1.00
//  Created:     10/10/2001 by Timur.
//  Compilers:   Visual C++ 6.0
//  Description: ObjectManager implementation.
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "ObjectManager.h"

#include "..\DisplaySettings.h"

#include "TagPoint.h"
#include "TagComment.h"
//#include "StatObj.h"
#include "Entity.h"
#include "Group.h"
#include "Volume.h"
#include "SoundObject.h"
#include "ShapeObject.h"
#include "AIPoint.h"
#include "BrushObject.h"
#include "CloudObject.h"
#include "CloudGroup.h"
#include "VoxelObject.h"
#include "DecalObject.h"
#include "DistanceCloudObject.h"
#include "GravityVolumeObject.h"
#include "Vehicles\VehiclePrototype.h"
#include "Vehicles\VehicleHelperObject.h"
#include "Vehicles\VehiclePart.h"
#include "Vehicles\VehicleSeat.h"
#include "Vehicles\VehicleWeapon.h"
#include "Vehicles\VehicleComp.h"
#include "SmartObjectHelperObject.h"
#include "AutoCubemapObject.h"

#include "CameraObject.h"
#include "AIAnchor.h"
#include "AIReinforcementSpot.h"
#include "SmartObject.h"
#include "AreaBox.h"
#include "AreaSphere.h"
#include "WaterShapeObject.h"
#include "VisAreaShapeObject.h"
#include "ProtEntityObject.h"
#include "PrefabObject.h"
#include "Brush\SolidBrushObject.h"
#include "RoadObject.h"
#include "RiverObject.h"
#include "WaterWaveObject.h"
#include "SequenceObject.h"
#include "RopeObject.h"
#include "MeshObject.h"
#include "CharAttachHelper.h"

#include "Settings.h"
#include "Viewport.h"

#include "GizmoManager.h"
#include "ObjectLayerManager.h"
#include "AxisGizmo.h"

#include "ObjectPhysicsManager.h"

#include "EditMode\ObjectMode.h"

#include <IEntitySystem.h>
#include <io.h>

/*!
 *	Class Description used for object templates.
 *	This description filled from Xml template files.
 */
class CXMLObjectClassDesc : public CObjectClassDesc
{
public:
	CObjectClassDesc*	superType;
	CString type;
	CString category;
	CString fileSpec;
	GUID guid;
	
public:
	REFGUID ClassID()
	{
		return guid;
	}
	ObjectType GetObjectType() { return superType->GetObjectType(); };
	const char* ClassName() { return type; };
	const char* Category() { return category; };
	CRuntimeClass* GetRuntimeClass() { return superType->GetRuntimeClass(); };
	const char* GetFileSpec()
	{
		if (!fileSpec.IsEmpty())
			return fileSpec;
		else
			return superType->GetFileSpec();
	};
	virtual int GameCreationOrder() { return superType->GameCreationOrder(); };
};

//////////////////////////////////////////////////////////////////////////
//! Undo New Object
class CUndoBaseObjectNew : public IUndoObject
{
public:
	CUndoBaseObjectNew( CBaseObject *obj ) { m_object = obj; }
protected:
	virtual int GetSize() { return sizeof(*this); }; // Return size of xml state.
	virtual const char* GetDescription() { return "New BaseObject"; };

	virtual void Undo( bool bUndo )
	{
		if (bUndo)
		{
			m_redo = CreateXmlNode("Redo");
			// Save current object state.
			CObjectArchive ar(GetIEditor()->GetObjectManager(),m_redo,false);
			ar.bUndo = true;
			m_object->Serialize( ar );
		}
		// Delete this object.
		GetIEditor()->DeleteObject( m_object );
	}
	virtual void Redo()
	{
		if (m_redo)
		{
			IObjectManager *pObjMan = GetIEditor()->GetObjectManager();
			{
				CObjectArchive ar( pObjMan,m_redo,true );
				ar.bUndo = true;
				ar.LoadObject( m_redo,m_object );
			}
			pObjMan->SelectObject( m_object );
		}
	}
private:
	CBaseObjectPtr m_object;
	XmlNodeRef m_redo;
};

//////////////////////////////////////////////////////////////////////////
//! Undo Delete Object
class CUndoBaseObjectDelete : public IUndoObject
{
public:
	CUndoBaseObjectDelete( CBaseObject *obj )
	{
		m_object = obj;
		// Save current object state.
		m_undo = CreateXmlNode("Undo");
		CObjectArchive ar(GetIEditor()->GetObjectManager(),m_undo,false);
		ar.bUndo = true;
		m_object->Serialize( ar );
	}
protected:
	virtual int GetSize() { return sizeof(*this); }; // Return size of xml state.
	virtual const char* GetDescription() { return "Delete BaseObject"; };

	virtual void Undo( bool bUndo )
	{
		IObjectManager *pObjMan = GetIEditor()->GetObjectManager();
		CObjectArchive ar( pObjMan,m_undo,true );
		ar.bUndo = true;
		ar.LoadObject( m_undo,m_object );
		pObjMan->SelectObject( m_object );
	}
	virtual void Redo()
	{
		// Delete this object.
		GetIEditor()->DeleteObject( m_object );
	}
private:
	CBaseObjectPtr m_object;
	XmlNodeRef m_undo;
};

//////////////////////////////////////////////////////////////////////////
//! Undo Select Object
class CUndoBaseObjectSelect : public IUndoObject
{
public:
	CUndoBaseObjectSelect( CBaseObject *obj )
	{
		assert( obj != 0 );
		m_object = obj;
		m_bUndoSelect = obj->IsSelected();
	}
protected:
	virtual void Release() { delete this; };
	virtual int GetSize() { return sizeof(*this); }; // Return size of xml state.
	virtual const char* GetDescription() { return "Select Object"; };

	virtual void Undo( bool bUndo )
	{
		if (bUndo)
		{
			m_bRedoSelect = m_object->IsSelected();
		}
		if (m_bUndoSelect)
			GetIEditor()->GetObjectManager()->SelectObject(m_object);
		else
			GetIEditor()->GetObjectManager()->UnselectObject(m_object);
	}
	virtual void Redo()
	{
		if (m_bRedoSelect)
			GetIEditor()->GetObjectManager()->SelectObject(m_object);
		else
			GetIEditor()->GetObjectManager()->UnselectObject(m_object);
	}
private:
	CBaseObjectPtr m_object;
	bool m_bUndoSelect;
	bool m_bRedoSelect;
};

//////////////////////////////////////////////////////////////////////////
// CObjectManager implementation.
//////////////////////////////////////////////////////////////////////////
CObjectManager* g_pObjectManager = 0;

//////////////////////////////////////////////////////////////////////////
CObjectManager::CObjectManager()
{
	g_pObjectManager = this;
	m_currSelection = &m_defaultSelection;
	m_currEditObject = 0;

	m_selectCallback = 0;
	m_createGameObjects = true;
	m_bSingleSelection = false;
	m_nLastSelCount=0;

	m_bGenUniqObjectNames = true;

	m_bSelectionChanged = false;
	m_bVisibleObjectValid = true;
	m_lastHideMask = 0;

	m_pLoadProgress = 0;
	m_totalObjectsToLoad = 0;
	m_loadedObjects = 0;


	m_maxObjectViewDistRatio = 0.00001f;

	// Creates objects layers manager.
	m_pLayerManager = new CObjectLayerManager( this );

	// Creates gizmo manager.
	m_gizmoManager = new CGizmoManager;

	m_pPhysicsManager = new CObjectPhysicsManager;

	// Register default classes.
	CClassFactory *cf = CClassFactory::Instance();
	cf->RegisterClass( new CTagPointClassDesc );
	//cf->RegisterClass( new CRespawnPointClassDesc );
	//cf->RegisterClass( new CSpawnPointClassDesc );
	cf->RegisterClass( new CTagCommentClassDesc );
	//cf->RegisterClass( new CStaticObjectClassDesc );
	//cf->RegisterClass( new CBuildingClassDesc );
	cf->RegisterClass( new CEntityClassDesc );
	cf->RegisterClass( new CSimpleEntityClassDesc );
	cf->RegisterClass( new CGeomEntityClassDesc );
	cf->RegisterClass( new CGroupClassDesc );
	cf->RegisterClass( new CVolumeClassDesc );
	cf->RegisterClass( new CSoundObjectClassDesc );
	cf->RegisterClass( new CShapeObjectClassDesc );
	cf->RegisterClass( new CAIPathObjectClassDesc );
	cf->RegisterClass( new CAIShapeObjectClassDesc );
	cf->RegisterClass( new CAIForbiddenAreaObjectClassDesc );
	cf->RegisterClass( new CAIForbiddenBoundaryObjectClassDesc );
	cf->RegisterClass( new CAINavigationModifierObjectClassDesc );
	cf->RegisterClass( new CAIOcclusionPlaneObjectClassDesc );
	cf->RegisterClass( new CAIPointClassDesc );
	cf->RegisterClass( new CBrushObjectClassDesc );
	cf->RegisterClass( new CCameraObjectClassDesc );
	cf->RegisterClass( new CCameraObjectTargetClassDesc );
	cf->RegisterClass( new CAIAnchorClassDesc );
	cf->RegisterClass( new CAIReinforcementSpotClassDesc );
	cf->RegisterClass( new CSmartObjectClassDesc );
	cf->RegisterClass( new CAreaBoxClassDesc );
	cf->RegisterClass( new CAreaSphereClassDesc );
	cf->RegisterClass( new CWaterShapeObjectClassDesc );
	cf->RegisterClass( new CVisAreaShapeObjectClassDesc );
	cf->RegisterClass( new CPortalShapeObjectClassDesc );
	cf->RegisterClass( new COccluderShapeObjectClassDesc );
	cf->RegisterClass( new CProtEntityObjectClassDesc );
	cf->RegisterClass( new CPrefabObjectClassDesc );
	cf->RegisterClass( new CSolidBrushObjectClassDesc );
	cf->RegisterClass( new CCloudObjectClassDesc );
	cf->RegisterClass( new CCloudGroupClassDesc );
	cf->RegisterClass( new CVoxelObjectClassDesc );
  cf->RegisterClass( new CVehiclePrototypeClassDesc );
  cf->RegisterClass( new CVehicleHelperClassDesc );  
  cf->RegisterClass( new CVehiclePartClassDesc );
  cf->RegisterClass( new CVehicleSeatClassDesc );
  cf->RegisterClass( new CVehicleWeaponClassDesc );
  cf->RegisterClass( new CVehicleComponentClassDesc );
	cf->RegisterClass( new CRoadObjectClassDesc );
	cf->RegisterClass( new CRiverObjectClassDesc );  
	cf->RegisterClass( new CSmartObjectHelperClassDesc );
	cf->RegisterClass( new CDecalObjectClassDesc );
	cf->RegisterClass( new CSequenceObjectClassDesc );
	cf->RegisterClass( new CGravityVolumeObjectClassDesc );  
  cf->RegisterClass( new CWaterWaveObjectClassDesc );
	cf->RegisterClass( new CDistanceCloudObjectClassDesc );
	cf->RegisterClass( new CAutoCubemapObjectClassDesc );
	cf->RegisterClass( new CRopeObjectClassDesc );
	cf->RegisterClass( new CMeshObjectClassDesc );
	cf->RegisterClass( new CCharacterAttachHelperObjectClassDesc );
  
	LoadRegistry();
}

//////////////////////////////////////////////////////////////////////////
CObjectManager::~CObjectManager()
{
	SaveRegistry();
	DeleteAllObjects();

	if (m_gizmoManager)
		delete m_gizmoManager;

	if (m_pLayerManager)
		delete m_pLayerManager;

	delete m_pPhysicsManager;
}

void	CObjectManager::SaveRegistry()
{

}

void	CObjectManager::LoadRegistry()
{
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::NewObject( CObjectClassDesc *cls,CBaseObject *prev,const CString &file )
{
	ASSERT( cls != 0 );
	CRuntimeClass *rtClass = cls->GetRuntimeClass();
	ASSERT( rtClass->IsDerivedFrom(RUNTIME_CLASS(CBaseObject)) );
	if (prev)
	{
		// Both current and previous object must be of same type.
		ASSERT( cls == prev->GetClassDesc() );
	}

	// Suspend undo operations when initializing object.
	GetIEditor()->SuspendUndo();

	CBaseObjectPtr obj = (CBaseObject*)rtClass->CreateObject();
	obj->SetClassDesc( cls );
	obj->SetLayer( m_pLayerManager->GetCurrentLayer() );
	obj->InitVariables();
	CoCreateGuid( &obj->m_guid ); // generate uniq GUID for this object.

	GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( obj );
	if (obj->Init( GetIEditor(),prev,file ))
	{
		if (obj->GetName().IsEmpty())
		{
			obj->SetName( GenUniqObjectName( cls->ClassName() ) );;
		}
		/*
		// Check if object Init function changed its name.
		if (objName.Compare(obj->GetName()) != 0)
		{
			// Object changed name. we must make sure it unique and generate new id.
			obj->SetName( GenUniqObjectName(obj->GetName()) );
			// Generate new unique id for this object.
			int id = Crc32Gen::GetCRC32(obj->GetName());
			obj->SetId(id);	
		}
		*/

		// Create game object itself.
		obj->CreateGameObject();

		if (!AddObject( obj ))
			obj = 0;
	}
	else
	{
		obj = 0;
	}
	GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( NULL );

	GetIEditor()->ResumeUndo();
	
	if (obj != 0 && GetIEditor()->IsUndoRecording())
		GetIEditor()->RecordUndo( new CUndoBaseObjectNew(obj) );

	return obj;
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::NewObject( CObjectArchive &ar,CBaseObject *pUndoObject,bool bMakeNewId )
{
	// Suspend undo operations when initializing object.
	CUndoSuspend undoSuspender;

	XmlNodeRef objNode = ar.node;

	// Load all objects from XML.
	CString typeName;
	GUID id = GUID_NULL;
	if (!objNode->getAttr( "Type",typeName ))
		return 0;

	if (!objNode->getAttr( "Id",id ))
	{
		// Make new ID for object that doesn't have if.
		CoCreateGuid( &id );
	}

	if (bMakeNewId)
	{
		// Make new guid for this object.
		GUID newId;
		CoCreateGuid( &newId );
		ar.RemapID( id,newId ); // Mark this id remapped.
		id = newId;
	}

	CBaseObjectPtr pObject;
	if (pUndoObject)
	{
		// if undoing restore object pointer.
		pObject = pUndoObject;
	}
	else
	{
		// New object creation.

		CObjectClassDesc *cls = FindClass( typeName );
		if (!cls)
		{
			CLogFile::FormatLine( "Error: RuntimeClass %s not registered",(const char*)typeName );
			return 0;
		}

		CRuntimeClass *rtClass = cls->GetRuntimeClass();
		assert( rtClass->IsDerivedFrom(RUNTIME_CLASS(CBaseObject)) );

		pObject = (CBaseObject*)rtClass->CreateObject();
		pObject->SetClassDesc( cls );
		pObject->m_guid = id;
		pObject->SetLayer( m_pLayerManager->GetCurrentLayer() );
		pObject->InitVariables();

		CString objName;
		objNode->getAttr( "Name",objName );
		pObject->m_name = objName;

		// @FIXME: Make sure this id not taken.
		CBaseObject *obj = FindObject(pObject->GetId());
		if (obj)
		{
			// If id is taken.
			CString error;
			error.Format( _T("Object with ID %s (%s) already exist in Object Manager\r\nWhen trying to create object: %s\r\nNew Object Ignored."),GuidUtil::ToString(obj->GetId()),(const char*)obj->GetName(),(const char*)objName );
			CLogFile::WriteLine( error );
			if (!GetIEditor()->IsInTestMode())
				MessageBox( AfxGetMainWnd()->GetSafeHwnd(),error,_T("Object Id Collision"),MB_OK|MB_ICONWARNING );

			return 0;
			//CoCreateGuid( &pObject->m_guid ); // generate uniq GUID for this object.
		}
	}

	GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( pObject );
	if (!pObject->Init( GetIEditor(),0,"" ))
	{
		GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( NULL );
		return 0;
	}

	if (!AddObject( pObject ))
	{
		GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( NULL );
		return 0;
	}

	//pObject->Serialize( ar );

	GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( NULL );

	if (!pObject->GetLayer())
	{
		// Cannot be.
		assert(0);
	}

	if (pObject != 0 && pUndoObject == 0)
	{
		// If new object with no undo, record it.
		if (CUndo::IsRecording())
			GetIEditor()->RecordUndo( new CUndoBaseObjectNew(pObject) );
	}

	m_loadedObjects++;
	if (m_pLoadProgress && m_totalObjectsToLoad > 0)
		m_pLoadProgress->Step( (m_loadedObjects*100)/m_totalObjectsToLoad );

	return pObject;
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::NewObject( const CString &typeName,CBaseObject *prev,const CString &file )
{ 
	CObjectClassDesc *cls = FindClass( typeName );
	if (!cls)
	{
		GetIEditor()->GetSystem()->GetILog()->Log( "Warning: RuntimeClass %s not registered",(const char*)typeName );
		return 0;
	}	
	CBaseObject *pObject = NewObject( cls,prev,file );
	return pObject;
}
	
//////////////////////////////////////////////////////////////////////////
void	CObjectManager::DeleteObject( CBaseObject *obj )
{
	if (m_currEditObject == obj)
		EndEditParams();

	if (!obj)
		return;

	// If object already deleted.
	if (obj->CheckFlags(OBJFLAG_DELETED))
		return;

	// Check if object is a group then delete all childs.
	if (obj->IsKindOf(RUNTIME_CLASS(CGroup)))
	{
		((CGroup*)obj)->DeleteAllChilds();
	}

	// This will detach all childs and store Undo for each detachment (So they can be restored with Undo)
	//obj->DetachAll();

	// Must be after object DetachAll to support restoring Parent/Child relations.
	if (CUndo::IsRecording())
	{
		// Store undo for all child objects.
		for (int i = 0; i < obj->GetChildCount(); i++)
		{
			if (!obj->GetChild(i)->CheckFlags(OBJFLAG_PREFAB))
				obj->GetChild(i)->StoreUndo("DeleteParent");
		}
		CUndo::Record( new CUndoBaseObjectDelete(obj) );
	}

	GetIEditor()->SuspendUndo();
	//! If object is child remove it from parent.
	obj->DetachThis();

	// Release game resources.
	obj->Done();

	NotifyObjectListeners( obj,CBaseObject::ON_DELETE );

	RemoveObject( obj );
	GetIEditor()->ResumeUndo();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::DeleteAllObjects()
{
	EndEditParams();

	ClearSelection();
	int i;

	InvalidateVisibleList();

	// Delete all selection groups.
	std::vector<CSelectionGroup*> sel;
	stl::map_to_vector( m_selections,sel );
	for (i = 0; i < sel.size(); i++)
	{
		delete sel[i];
	}
	m_selections.clear();

	std::vector<CBaseObjectPtr> objectsHolder;
	GetAllObjects( objectsHolder );
	for (i = 0; i < objectsHolder.size(); i++)
	{
		objectsHolder[i]->Done();
	}
	// Clear map.
	m_objects.clear();
	//! Delete object instances.
	objectsHolder.clear();

	// Clear name map.
	m_nameNumbersMap.clear();
}

CBaseObject* CObjectManager::CloneObject( CBaseObject *obj )
{
	ASSERT( obj );
	//CRuntimeClass *cls = obj->GetRuntimeClass();
	//CBaseObject *clone = (CBaseObject*)cls->CreateObject();
	//clone->CloneCopy( obj );
	CBaseObject *clone = NewObject( obj->GetClassDesc(),obj );
	return clone;
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::FindObject( REFGUID guid ) const
{
	return stl::find_in_map( m_objects,guid,(CBaseObject*)0 );
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::FindObject( const CString &sName ) const
{
	for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *pObj = it->second;
		if (stricmp(pObj->GetName(), sName)==0)
			return pObj;
	}
	return NULL;
}

//////////////////////////////////////////////////////////////////////////
bool CObjectManager::AddObject( CBaseObject *obj )
{
	CBaseObjectPtr p = stl::find_in_map( m_objects,obj->GetId(),0 );
	if (p)
	{
		CErrorRecord err;
		err.error.Format( "New Object %s have Duplicate GUID %s, New Object Ignored",(const char*)obj->GetName(),GuidUtil::ToString(obj->GetId()) );
		err.severity = CErrorRecord::ESEVERITY_ERROR;
		err.pObject = obj;
		err.flags = CErrorRecord::FLAG_OBJECTID;
		GetIEditor()->GetErrorReport()->ReportError(err);

		return false;
	}
	m_objects[obj->GetId()] = obj;
  RegisterObjectName(obj->GetName());
	InvalidateVisibleList();
	NotifyObjectListeners( obj,CBaseObject::ON_ADD );
	return true;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::RemoveObject( CBaseObject *obj )
{
	assert( obj != 0 );
	
  UpdateRegisterObjectName(obj->GetName());
  InvalidateVisibleList();

	m_objects.erase(obj->GetId());
	
	// Remove this object from selection groups.
	m_currSelection->RemoveObject(obj);
	std::vector<CSelectionGroup*> sel;
	stl::map_to_vector( m_selections,sel );
	for (int i = 0; i < sel.size(); i++)
	{
		sel[i]->RemoveObject(obj);
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::GetAllObjects( std::vector<CBaseObjectPtr> &objects ) const
{
	objects.clear();
	objects.reserve( m_objects.size() );
	for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		objects.push_back( it->second );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::ChangeObjectId( CBaseObject *obj,int newId )
{
	/*
	assert( obj );
	CBaseObjectPtr pObject = obj;
	CBaseObjectPtr p;
	if (m_objects.Find(obj->GetId(),p))
	{
		// If object is already added to object list, change it in the map.
		m_objects.Erase( obj->GetId() );
		m_objects[newId] = pObject;
	}
	pObject->SetId( newId );
	*/
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::ChangeObjectName( CBaseObject *obj,const CString &newName )
{
	assert( obj );

	if (newName != obj->GetName())
	{
		/*
		// Check if this name already used.
		CBaseObject *pExisting = FindObject( newName );
		if (pExisting)
		{
			// If id is taken.
			CString str;
			str.Format( "Duplicate Object Name: %s\r\nName change ignored",(const char*)newName );
			AfxMessageBox( str,MB_OK|MB_ICONWARNING );
			return;
		}
		*/

		obj->SetName(newName);
		/*
		// Change ID of object together with its name.
		int id = Crc32Gen::GetCRC32(obj->GetName());
		ChangeObjectId( obj,id );
		*/
		NotifyObjectListeners( obj,CBaseObject::ON_RENAME );
	}
}

//////////////////////////////////////////////////////////////////////////
int CObjectManager::GetObjectCount() const
{
	return m_objects.size();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::GetObjects( CBaseObjectsArray &objects,CObjectLayer* layer )
{
	objects.clear();
	objects.reserve( m_objects.size() );
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		if (layer == 0 || it->second->GetLayer() == layer)
			objects.push_back( it->second );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::GetCameras( std::vector<CCameraObject*> &objects )
{
	objects.clear();
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *object = it->second;
		if (object->IsKindOf(RUNTIME_CLASS(CCameraObject)))
		{
			// Only consider camera sources.
			if (object->IsLookAtTarget())
				continue;
			objects.push_back( (CCameraObject*)object );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::SendEvent( ObjectEvent event )
{
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		if (obj->GetGroup())
			continue;
		obj->OnEvent( event );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::SendEvent( ObjectEvent event,const BBox &bounds )
{
	BBox box;
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		if (obj->GetGroup())
			continue;
		obj->GetBoundBox(box);
		if (bounds.IsIntersectBox(box))
			obj->OnEvent( event );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::Update()
{
	HWND hFocusWnd = GetFocus();

	bool bObjectMode = (GetIEditor()->GetEditTool() && GetIEditor()->GetEditTool()->IsKindOf(RUNTIME_CLASS(CObjectMode)));

	if (m_currSelection->GetCount() == 0)
	{
		// Nothing selected.
		EndEditParams();
		if (bObjectMode)
			GetIEditor()->ShowTransformManipulator(false);
	}
	else if (m_currSelection->GetCount() == 1)
	{
		if (!m_bSingleSelection)
			EndEditParams();

		CBaseObject *newSelObject = m_currSelection->GetObject(0);
		// Single object selected.
		if (m_currEditObject != m_currSelection->GetObject(0))
		{
			m_bSelectionChanged = false;
			if (!m_currEditObject || (m_currEditObject->GetRuntimeClass() != newSelObject->GetRuntimeClass()))
			{
				// If old object and new objects are of different classes.
				EndEditParams();
			}
			if (bObjectMode)
				BeginEditParams( newSelObject,OBJECT_EDIT );

			//AfxGetMainWnd()->SetFocus();
		}
	}
	else if (m_currSelection->GetCount() > 1)
	{
		// Multiple objects are selected.
		if (m_bSelectionChanged && bObjectMode)
		{
			m_bSelectionChanged = false;
			m_nLastSelCount=m_currSelection->GetCount();
			EndEditParams();
			bool bAllSameType = m_currSelection->SameObjectType();

			m_currEditObject = m_currSelection->GetObject(0);
			m_currEditObject->BeginEditMultiSelParams( bAllSameType );
		}
	}

	// Restore focus if it changed.
	if (hFocusWnd != GetFocus() && hFocusWnd)
		SetFocus( hFocusWnd );

	m_pPhysicsManager->Update();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::HideObject( CBaseObject *obj,bool hide )
{
	assert( obj != 0 );
	// Remove object from main object set and put it to hidden set.
	obj->SetHidden( hide );
	InvalidateVisibleList();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::UnhideAll()
{
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		obj->SetHidden(false);
	}
	InvalidateVisibleList();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::FreezeObject( CBaseObject *obj,bool freeze )
{
	assert( obj != 0 );
	// Remove object from main object set and put it to hidden set.
	obj->SetFrozen( freeze );
	InvalidateVisibleList();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::UnfreezeAll()
{
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		obj->SetFrozen(false);
	}
	InvalidateVisibleList();
}

//////////////////////////////////////////////////////////////////////////
bool CObjectManager::SelectObject( CBaseObject *obj )
{
	assert( obj );

	// Check if can be selected.
	if (!(obj->GetType() & gSettings.objectSelectMask))
		return false;

	if (!obj->IsSelectable())
		return false;

	if (m_selectCallback)
	{
		if (!m_selectCallback->OnSelectObject( obj ))
			return true;
	}

	/*
	if (GetIEditor()->IsUndoRecording() && !obj->IsSelected())
	{
		GetIEditor()->RecordUndo( new CUndoBaseObjectSelect(obj) );
	}
	*/

	m_currSelection->AddObject( obj );
	SetObjectSelected( obj,true );
	return true;
}

void CObjectManager::UnselectObject( CBaseObject *obj )
{
	/*
	if (GetIEditor()->IsUndoRecording() && obj->IsSelected())
	{
		GetIEditor()->RecordUndo( new CUndoBaseObjectSelect(obj) );
	}
	*/
	SetObjectSelected( obj,false );
	m_currSelection->RemoveObject( obj );
}

CSelectionGroup* CObjectManager::GetSelection( const CString &name ) const
{
	CSelectionGroup *selection = stl::find_in_map( m_selections,name,(CSelectionGroup*)0 );
	return selection;
}

void CObjectManager::NameSelection( const CString &name )
{
	if (m_currSelection->IsEmpty())
		return;

	CSelectionGroup *selection = stl::find_in_map( m_selections,name,(CSelectionGroup*)0 );
	if (selection)
	{
		ASSERT( selection != 0 );
		// Check if trying to rename itself to the same name.
		if (selection == m_currSelection)
			return;
		m_selections.erase( name );
		delete selection;
	}
	selection = new CSelectionGroup;
	selection->Copy( *m_currSelection );
	selection->SetName( name );
	m_selections[name] = selection;
	m_currSelection = selection;
	m_defaultSelection.RemoveAll();
}

//////////////////////////////////////////////////////////////////////////
int CObjectManager::ClearSelection()
{
	int numSel = m_currSelection->GetCount();
	UnselectCurrent();
	m_defaultSelection.RemoveAll();
	m_currSelection = &m_defaultSelection;
	m_bSelectionChanged = true;

	return numSel;
}

//////////////////////////////////////////////////////////////////////////
int CObjectManager::InvertSelection()
{
	int selCount = 0;
	// iterate all objects.
	for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *pObj = it->second;
		if (pObj->IsSelected())
			UnselectObject( pObj );
		else
		{
			if (SelectObject( pObj ))
				selCount++;
		}
	}
	return selCount;
}

void CObjectManager::SetSelection( const CString &name )
{
	CSelectionGroup *selection = stl::find_in_map( m_selections,name,(CSelectionGroup*)0 );
	if (selection)
	{
		UnselectCurrent();
		ASSERT( selection != 0 );
		m_currSelection = selection;
		SelectCurrent();
	}
}

void CObjectManager::RemoveSelection( const CString &name )
{
	CString selName = name;
	CSelectionGroup *selection = stl::find_in_map( m_selections,name,(CSelectionGroup*)0 );
	if (selection)
	{
		if (selection == m_currSelection)
		{
			UnselectCurrent();
			m_currSelection = &m_defaultSelection;
			m_defaultSelection.RemoveAll();
		}
		delete selection;
		m_selections.erase( selName );
	}
}

void CObjectManager::SelectCurrent()
{
	for (int i = 0; i < m_currSelection->GetCount(); i++)
	{
		CBaseObject *obj = m_currSelection->GetObject(i);
		if (GetIEditor()->IsUndoRecording() && !obj->IsSelected())
			GetIEditor()->RecordUndo( new CUndoBaseObjectSelect(obj) );

		SetObjectSelected( obj,true );
	}
}

void CObjectManager::UnselectCurrent()
{
	// Make sure to unlock selection.
	GetIEditor()->LockSelection( false );

	for (int i = 0; i < m_currSelection->GetCount(); i++)
	{
		CBaseObject *obj = m_currSelection->GetObject(i);
		if (GetIEditor()->IsUndoRecording() && obj->IsSelected())
			GetIEditor()->RecordUndo( new CUndoBaseObjectSelect(obj) );

		SetObjectSelected( obj,false );
	}
}

//////////////////////////////////////////////////////////////////////////
void	CObjectManager::Display( DisplayContext &dc )
{
	Vec3 org;

	FUNCTION_PROFILER( GetIEditor()->GetSystem(),PROFILE_EDITOR );

	if (!m_bVisibleObjectValid || gSettings.objectHideMask != m_lastHideMask)
	{
		m_lastHideMask = gSettings.objectHideMask;
		UpdateVisibilityList();
	}

	if (dc.settings->IsDisplayHelpers() || 	dc.view->GetVisibleObjectsCache()->GetObjectCount()==0)		// is display helpers or list is not populated (needed to select objects)
	{
		FindDisplayableObjects(dc, true);
	}

	if (m_gizmoManager)
	{
		m_gizmoManager->Display( dc );
	}
}

void CObjectManager::ForceUpdateVisibleObjectCache(DisplayContext& dc)
{
	FindDisplayableObjects(dc, false);
}

void CObjectManager::FindDisplayableObjects(DisplayContext& dc, bool bDisplay)
{
	CBaseObjectsCache *pDispayedViewObjects = dc.view->GetVisibleObjectsCache();

	const CCamera &camera = GetIEditor()->GetSystem()->GetViewCamera();
	BBox bbox;
	bbox.min.zero();
	bbox.max.zero();

	pDispayedViewObjects->ClearObjects();
	pDispayedViewObjects->Reserve(m_visibleObjects.size());

	if (dc.flags & DISPLAY_2D)
	{
		int numVis = m_visibleObjects.size();
		for (int i = 0; i < numVis; i++)
		{
			CBaseObject *obj = m_visibleObjects[i];

			obj->GetBoundBox( bbox );
			if (dc.box.IsIntersectBox( bbox ))
			{
				pDispayedViewObjects->AddObject(obj);

				if (bDisplay && dc.settings->IsDisplayHelpers())
					obj->Display( dc );
			}
		}
	}
	else
	{
		int numVis = m_visibleObjects.size();
		for (int i = 0; i < numVis; i++)
		{
			CBaseObject *obj = m_visibleObjects[i];

			obj->GetBoundBox( bbox );
			if (camera.IsAABBVisible_F( AABB(bbox.min,bbox.max) ))
			{
				// Check if object is too far.
				float l2 = max( 1.0f,(bbox.max - bbox.min).GetLengthSquared() );
				float camdist2 = (bbox.min - camera.GetPosition()).GetLengthSquared();
				float visRatio = 1000.0f;
				if (camdist2 != 0.0f)
					visRatio = l2 / camdist2;
				if (visRatio > m_maxObjectViewDistRatio || (dc.flags&DISPLAY_SELECTION_HELPERS) || obj->IsSelected())
				{
					pDispayedViewObjects->AddObject(obj);

					if (bDisplay && dc.settings->IsDisplayHelpers())
						obj->Display( dc );
				}
			}
		}
	}
}

void CObjectManager::BeginEditParams( CBaseObject *obj,int flags )
{
	ASSERT( obj != 0 );
	if (obj == m_currEditObject)
		return;

	if (GetSelection()->GetCount() > 1)
		return;

	HWND hFocusWnd = GetFocus();
	
	if (m_currEditObject)
	{
		//if (obj->GetClassDesc() != m_currEditObject->GetClassDesc())
		if (!obj->IsSameClass(m_currEditObject))
			EndEditParams( flags );
	}

	m_currEditObject = obj;

	if (flags & OBJECT_CREATE)
	{
		// Unselect all other objects.
		ClearSelection();
		// Select this object.
		SelectObject( obj );
	}
	
	m_bSingleSelection = true;
	m_currEditObject->BeginEditParams( GetIEditor(),flags );

	// Restore focus if it changed.
	if (hFocusWnd != GetFocus() && hFocusWnd)
		SetFocus( hFocusWnd );
}
	
void CObjectManager::EndEditParams( int flags )
{
	if (m_currEditObject)
	{
		if (m_bSingleSelection)
			m_currEditObject->EndEditParams( GetIEditor() );
		else
			m_currEditObject->EndEditMultiSelParams();
	}
	m_bSingleSelection = false;
	m_currEditObject = 0;
	m_bSelectionChanged = false;
}

//! Select objects withing specified distance from givven position.
int CObjectManager::SelectObjects( const BBox &box,bool bUnselect)
{
	int numSel = 0;

	BBox objBounds;
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;

		if (obj->IsHidden())
			continue;
		if (obj->IsFrozen())
			continue;

		if (obj->GetGroup())
			continue;

		obj->GetBoundBox( objBounds );
		if (box.IsIntersectBox(objBounds))
		{
			numSel++;
			if (!bUnselect)
				SelectObject( obj );
			else
				UnselectObject( obj );
		}
		// If its group.
		if (obj->GetRuntimeClass() == RUNTIME_CLASS(CGroup))
		{
			numSel += ((CGroup*)obj)->SelectObjects( box,bUnselect );
		}
	}
	return numSel;
}

//////////////////////////////////////////////////////////////////////////
int CObjectManager::MoveObjects( const BBox &box, const Vec3 & offset, bool bIsCopy )
{
	BBox objBounds;
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;

		if(obj->GetParent())
			continue;

		if (obj->GetGroup())
			continue;

		obj->GetBoundBox( objBounds );
		if (box.IsIntersectBox(objBounds))
		{
			obj->SetPos(obj->GetPos()+offset);
		}
	}
	return 0;
}


//////////////////////////////////////////////////////////////////////////
void CObjectManager::DeleteSelection()
{
	// Make sure to unlock selection.
	GetIEditor()->LockSelection( false );

	int i;
	std::vector<CBaseObjectPtr> objects;
	for (i = 0; i < m_currSelection->GetCount(); i++)
	{
		objects.push_back( m_currSelection->GetObject(i) );
	}
	
	RemoveSelection( m_currSelection->GetName() );
	m_currSelection = &m_defaultSelection;
	m_defaultSelection.RemoveAll();

	// When deleting group, subobject of group must become selected.
	std::vector<CBaseObjectPtr> toselect;

	for (i = 0; i < objects.size(); i++)
	{
		DeleteObject( objects[i] );
	}
}

/*
//! Check if new hit object is better suitable for selection then former selected hit object.
//! @return true if obj1 is better for selection.
inline bool CompareHitObjects( CBaseObject *obj1,HitContext &hc1,CBaseObject *obj2,HitContext &hc2 )
{
	if (hc1.geometryHit && hc2.geometryHit)
	{
		if (hc1.dist < hc2.dist)
			return true;
		else
			return false;
	}
	if (!hc1.geometryHit && hc2.geometryHit)
	{
		return false;
	}

}
*/


//////////////////////////////////////////////////////////////////////////
bool CObjectManager::HitTestObject(CBaseObject *obj, HitContext & hc)
{
	if (obj->IsFrozen())
		return false;

	if (obj->IsHidden())
		return false;

	ObjectType objType = obj->GetType();

	// Check if this object type is masked for selection.
	if (!(objType & gSettings.objectSelectMask))
		return false;

	BBox box;
	Vec3 tempPnt;

	bool bSelectionHelperHit = false;

	if (obj->HaveTextureIcon() && gSettings.viewports.bShowIcons && !hc.bUseSelectionHelpers)
	{
		// Hit Test icon of this object.
		Vec3 wp = obj->GetWorldPos();
		int y0 = -(OBJECT_TEXTURE_ICON_SIZEY/2);
		int y1 = +(OBJECT_TEXTURE_ICON_SIZEY/2);
		if (obj->CheckFlags(OBJFLAG_SHOW_ICONONTOP))
		{
			obj->GetBoundBox( box );
			wp.z = box.max.z;
			y0 = -(OBJECT_TEXTURE_ICON_SIZEY);
			y1 = 0;
		}
		CPoint pnt = hc.view->WorldToView( wp );
		if (hc.point2d.x >= pnt.x-(OBJECT_TEXTURE_ICON_SIZEX/2) && hc.point2d.x <= pnt.x+(OBJECT_TEXTURE_ICON_SIZEX/2) &&
			hc.point2d.y >= pnt.y+y0 && hc.point2d.y <= pnt.y+y1)
		{
			hc.dist = hc.raySrc.GetDistance(wp) - 0.2f;
			bSelectionHelperHit = true;
		}
	}
	else if (hc.bUseSelectionHelpers)
	{
		// Hit test helper.
		Vec3 origin = obj->GetWorldPos();

		Vec3 w = origin - hc.raySrc;
		w = hc.rayDir.Cross( w );
		float d = w.GetLengthSquared();

		float radius = hc.view->GetScreenScaleFactor(origin) * 0.008f;
		if (d < radius*radius + hc.distanceTollerance)
		{
			hc.dist = hc.raySrc.GetDistance(origin);
			bSelectionHelperHit = true;
		}
		else
			return false;
	}

	if (!bSelectionHelperHit)
	{
		obj->GetBoundBox( box );
		if (hc.camera)
		{
			if (!hc.camera->IsAABBVisible_F( AABB(box.min,box.max)))
			{
				return false;
			}
		}
		else
		{
			if (hc.bounds && !box.IsIntersectBox(*hc.bounds))
				return false;
		}
		// Fast bounding box check.
		if (hc.nSubObjFlags == 0)
		{
			if (!box.IsIntersectRay(hc.raySrc,hc.rayDir,tempPnt))
				return false;
		}
		else
		{
			// Do 2D space testing.
			if (!obj->HitTestRect(hc))
				return false;
		}
	}

	if (bSelectionHelperHit || obj->HitTest(hc))
	{
		return true;
	}
	
	return false;
}


//////////////////////////////////////////////////////////////////////////
bool CObjectManager::HitTest( HitContext &hitInfo )
{
	FUNCTION_PROFILER( GetIEditor()->GetSystem(),PROFILE_EDITOR );

	hitInfo.object = 0;
	hitInfo.dist = 0;
	hitInfo.axis = 0;
	hitInfo.manipulatorMode = 0;

	HitContext hcOrg = hitInfo;
	if (hcOrg.view)
	{
		hcOrg.b2DViewport = hcOrg.view->GetType() != ET_ViewportCamera;
	}
	hcOrg.rayDir = hcOrg.rayDir.GetNormalized();

	HitContext hc = hcOrg;
	//HitContext hcSelected;
	
	float mindist = FLT_MAX;

	if (!hitInfo.bIgnoreAxis && !hc.bUseSelectionHelpers)
	{
		// Test gizmos.
		if (m_gizmoManager->HitTest( hc ))
		{
			if (hc.axis != 0)
			{
				hitInfo.object = hc.object;
				hitInfo.gizmo = hc.gizmo;
				hitInfo.axis = hc.axis;
				hitInfo.manipulatorMode = hc.manipulatorMode;
				hitInfo.dist = hc.dist;
				return true;
			}
		}
	}

	if (hitInfo.bOnlyGizmo)
		return false;

	//BBox box;

	// Only HitTest objects, that where previously Displayed.
	CBaseObjectsCache *pDispayedViewObjects = hitInfo.view->GetVisibleObjectsCache();

	CBaseObject *selected = 0;
	int numVis = pDispayedViewObjects->GetObjectCount();
	for (int i = 0; i < numVis; i++)
	{
		CBaseObject *obj = pDispayedViewObjects->GetObject(i);

		//hc = hcOrg;

		//! Only check root objects.
		if (obj->GetGroup())
			continue;


		if(HitTestObject(obj, hc))
		{
			if (m_selectCallback && !m_selectCallback->CanSelectObject(obj))
				continue;

			// Check if this object is nearest.
			if (hc.axis != 0)
			{
				hitInfo.object = obj;
				hitInfo.axis = hc.axis;
				hitInfo.dist = hc.dist;
				return true;
			}
			
			// Compare objects obj and selected.
			// CompareHitObjects( obj,hc,selected,hcSelected );

			// If object is selected prefere unselected object.
			//if (hc.dist < mindist || (selected != 0 && selected->IsSelected()))
			if (hc.dist < mindist)
			{
				// If collided object specified, accept it, overwise take tested object itself.
				CBaseObject *hitObj = hc.object;
				if (!hitObj)
					hitObj = obj;

				hc.object = 0;

				// If object is selected prefere unselected object.
				//if (selected != 0 && !selected->IsSelected() && hitObj->IsSelected())
					//continue;

				mindist = hc.dist;
				selected = hitObj;
				//hcSelected = hc;
			}
		}
	}

	if (selected)
	{
		hitInfo.object = selected;
		hitInfo.dist = mindist;
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::SelectObjectsInRect( CViewport *view,const CRect &rect,bool bSelect )
{
	// Ignore too small rectangles.
	if (rect.Width() < 1 || rect.Height() < 1)
		return;

	CUndo undo( "Select Object(s)" );

	BBox box;

	HitContext hc;
	hc.view = view;
	hc.b2DViewport = view->GetType() != ET_ViewportCamera;
	hc.rect = rect;

	CBaseObjectsCache *pDispayedViewObjects = view->GetVisibleObjectsCache();
	int numVis = pDispayedViewObjects->GetObjectCount();
	for (int i = 0; i < numVis; i++)
	{
		CBaseObject *pObj = pDispayedViewObjects->GetObject(i);
		
		if (!pObj->IsSelectable())
			continue;

		// Retrieve world space bound box.
		pObj->GetBoundBox( box );
		
		// Check if object visible in viewport.
		if (!view->IsBoundsVisible(box))
			continue;

		if (pObj->HitTestRect(hc))
		{
			if (bSelect)
				SelectObject(pObj);
			else
				UnselectObject(pObj);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::RegisterObjectName( const CString &name )
{
	// Remove all numbers from the end of typename.
	CString typeName = name;
	int nameLen = typeName.GetLength();
	int len = nameLen;
	while (len > 0 && isdigit(typeName[len-1]))
		len--;

	typeName = typeName.Left(len);

	int num = 0;
	if (len < nameLen)
		num = atoi((const char*)name+len) + 0;

	int lastNumber = stl::find_in_map( m_nameNumbersMap,typeName,0 );
	int newLastNumber = MAX( num,lastNumber );
	if (newLastNumber != lastNumber)
		m_nameNumbersMap[typeName] = newLastNumber;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::UpdateRegisterObjectName( const CString &name )
{
  // Remove all numbers from the end of typename.
  CString typeName = name;
  int nameLen = typeName.GetLength();
  int len = nameLen;

  while (len > 0 && isdigit(typeName[len-1]))
    len--;

  typeName = typeName.Left(len);

  int num = 0;
  if (len < nameLen)
    num = atoi((const char*)name+len) + 0;

  NameNumbersMap::iterator it = m_nameNumbersMap.find(typeName);

  if (it != m_nameNumbersMap.end() && num == it->second)
  {
    if (--num < 1)
      m_nameNumbersMap.erase(it);
    else
      m_nameNumbersMap[typeName]= num;
  }
}

//////////////////////////////////////////////////////////////////////////
CString	CObjectManager::GenUniqObjectName( const CString &theTypeName )
{
	if (!m_bGenUniqObjectNames)
		return theTypeName;

	// Remove all numbers from the end of typename.
	CString typeName = theTypeName;
	int len = typeName.GetLength();
	while (len > 0 && isdigit(typeName[len-1]))
		len--;

	typeName = typeName.Left(len);

	int lastNumber = stl::find_in_map( m_nameNumbersMap,typeName,0 );
	lastNumber++;
	m_nameNumbersMap[typeName] = lastNumber;

	CString str;
	str.Format( "%s%d",(const char*)typeName,lastNumber );

	/*
	CString tpName = typeName;
	char str[1024];
	int num = 0;
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		const char *name = it->second->GetName();
		if (strncmp(name,tpName,len) == 0)
		{
			int n = atoi(name+len) + 1;
			num = MAX( num,n );
		}
	}
	//sprintf( str,"%s%02d",(const char*)typeName,num );
	sprintf( str,"%s%d",(const char*)typeName,num );
	*/
	return str;
}

//////////////////////////////////////////////////////////////////////////
bool CObjectManager::EnableUniqObjectNames( bool bEnable )
{
	bool bPrev = m_bGenUniqObjectNames;
	m_bGenUniqObjectNames = bEnable;
	return bPrev;
}

//////////////////////////////////////////////////////////////////////////
CObjectClassDesc* CObjectManager::FindClass( const CString &className )
{
	IClassDesc *cls = CClassFactory::Instance()->FindClass( className );
	if (cls != NULL && cls->SystemClassID() == ESYSTEM_CLASS_OBJECT)
	{
		return (CObjectClassDesc*)cls;
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::GetClassCategories( std::vector<CString> &categories )
{
	std::vector<IClassDesc*> classes;
	CClassFactory::Instance()->GetClassesBySystemID( ESYSTEM_CLASS_OBJECT,classes );
	std::set<CString> cset;
	for (int i = 0; i < classes.size(); i++)
	{
		const char *category = classes[i]->Category();
		if (strlen(category) > 0)
			cset.insert( category );
	}
	categories.clear();
	categories.reserve( cset.size() );
	for (std::set<CString>::iterator cit = cset.begin(); cit != cset.end(); ++cit)
	{
		categories.push_back( *cit );
	}
}
	
void CObjectManager::GetClassTypes( const CString &category,std::vector<CString> &types )
{
	std::vector<IClassDesc*> classes;
	CClassFactory::Instance()->GetClassesBySystemID( ESYSTEM_CLASS_OBJECT,classes );
	for (int i = 0; i < classes.size(); i++)
	{
		const char* cat = classes[i]->Category();
		if (stricmp(cat,category) == 0)
		{
			types.push_back( classes[i]->ClassName() );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::RegisterClassTemplate( XmlNodeRef &templ )
{
	CString typeName = templ->getTag();
	CString superTypeName;
	if (!templ->getAttr( "SuperType",superTypeName ))
		return;

	CObjectClassDesc *superType = FindClass( superTypeName );
	if (!superType)
		return;

	CString category,fileSpec,initialName;
	templ->getAttr( "Category",category );
	templ->getAttr( "File",fileSpec );
	templ->getAttr( "Name",initialName );

	CXMLObjectClassDesc *classDesc = new CXMLObjectClassDesc;
	classDesc->superType = superType;
	classDesc->type = typeName;
	classDesc->category = category;
	classDesc->fileSpec = fileSpec;
	CoCreateGuid( &classDesc->guid );
	//classDesc->properties = templ->findChild( "Properties" );

	CClassFactory::Instance()->RegisterClass( classDesc );
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::LoadClassTemplates( const CString &path )
{
	XmlParser parser;

	CString dir = Path::AddBackslash(path);

	std::vector<CFileUtil::FileDesc> files;
	CFileUtil::ScanDirectory( dir,"*.xml",files,false );

	for (int k = 0; k < files.size(); k++)
	{

		// Construct the full filepath of the current file
		XmlNodeRef node = parser.parse( dir + files[k].filename );
		if (node != 0 && node->isTag("ObjectTemplates"))
		{
			CString name;
			for (int i = 0; i < node->getChildCount(); i++)
			{
				RegisterClassTemplate( node->getChild(i) );
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::Serialize( XmlNodeRef &xmlNode,bool bLoading,int flags )
{
	if (!xmlNode)
		return;

	bool bIgnoreExternal = flags & SERIALIZE_IGNORE_EXTERNAL;

	if (bLoading)
	{
		m_loadedObjects = 0;

		if (flags == SERIALIZE_ONLY_NOTSHARED)
			DeleteNotSharedObjects();
		else if (flags == SERIALIZE_ONLY_SHARED)
			DeleteSharedObjects();
		else
			DeleteAllObjects();


		XmlNodeRef root = xmlNode->findChild( "Objects" );
		
		int totalObjects = 0;
		if (root)
			root->getAttr( "NumObjects",totalObjects );

		StartObjectsLoading( totalObjects );

		// Load layers.
		CObjectArchive ar( this,xmlNode,true );
	
		// Load layers.
		m_pLayerManager->Serialize( ar,bIgnoreExternal );

		// Loading.
		if (root)
		{
			ar.node = root;
			LoadObjects( ar,false );
		}
		EndObjectsLoading();
	}
	else
	{
		// Saving.
		XmlNodeRef root = xmlNode->newChild( "Objects" );

		CObjectArchive ar( this,root,false );

		int totalObjects = m_objects.size();
		root->setAttr( "NumObjects",totalObjects );

		// Save all objects to XML.
		for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
		{
			CBaseObject *obj = it->second;

			// Not save objects in prefabs or groups.
			if (obj->GetGroup() || obj->CheckFlags(OBJFLAG_PREFAB))
				continue;

			if ((flags == SERIALIZE_ONLY_SHARED) && !obj->CheckFlags(OBJFLAG_SHARED))
				continue;
			else if ((flags == SERIALIZE_ONLY_NOTSHARED) && obj->CheckFlags(OBJFLAG_SHARED))
				continue;

			CObjectLayer *pLayer = obj->GetLayer();
			if (pLayer->IsExternal() || pLayer->IsParentExternal())
				continue;
			
			XmlNodeRef objNode = root->newChild( "Object" );
			ar.node = objNode;
			obj->Serialize( ar );
		}

		// Save layers.
		ar.node = xmlNode;
		m_pLayerManager->Serialize( ar,bIgnoreExternal );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::LoadObjects( CObjectArchive &objectArchive,bool bSelect )
{
	XmlNodeRef objectsNode = objectArchive.node;
	int numObjects = objectsNode->getChildCount();
	for (int i = 0; i < numObjects; i++)
	{
		objectArchive.node = objectsNode->getChild(i);
		CBaseObject *obj = objectArchive.LoadObject( objectsNode->getChild(i) );
		if (obj && bSelect)
			SelectObject( obj );
	}
	EndObjectsLoading(); // End progress bar, here, Resolve objects have his own.
	objectArchive.ResolveObjects();

	InvalidateVisibleList();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::Export( const CString &levelPath,XmlNodeRef &rootNode,bool onlyShared )
{
	// Clear export files.
	DeleteFile( levelPath+"TagPoints.ini" );
	DeleteFile( levelPath+"Volumes.ini" );
	
	// Save all objects to XML.
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		CObjectLayer *pLayer = obj->GetLayer();
		if (!pLayer->IsExportable())
			continue;
		// Export Only shared objects.
		if ((obj->CheckFlags(OBJFLAG_SHARED) && onlyShared) ||
				(!obj->CheckFlags(OBJFLAG_SHARED) && !onlyShared))
		{
			obj->Export( levelPath,rootNode );
		}
	}
}

void CObjectManager::DeleteNotSharedObjects()
{
	std::vector<CBaseObjectPtr> objects;
	GetAllObjects( objects );
	for (int i = 0; i < objects.size(); i++)
	{
		CBaseObject *obj = objects[i];
		if (!obj->CheckFlags(OBJFLAG_SHARED))
		{
			DeleteObject( obj );
		}
	}
}

void CObjectManager::DeleteSharedObjects()
{
	std::vector<CBaseObjectPtr> objects;
	GetAllObjects( objects );
	for (int i = 0; i < objects.size(); i++)
	{
		CBaseObject *obj = objects[i];
		if (obj->CheckFlags(OBJFLAG_SHARED))
		{
			DeleteObject( obj );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
IObjectSelectCallback* CObjectManager::SetSelectCallback( IObjectSelectCallback* callback )
{
	IObjectSelectCallback* prev = m_selectCallback;
	m_selectCallback = callback;
	return prev;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::InvalidateVisibleList()
{
	m_bVisibleObjectValid = false;
	m_visibleObjects.clear();
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::UpdateVisibilityList()
{
	m_visibleObjects.clear();
	for (Objects::iterator it = m_objects.begin(); it != m_objects.end(); ++it)
	{
		CBaseObject *obj = it->second;
		bool visible = obj->IsPotentiallyVisible();
    obj->UpdateVisibility( visible );
		if (visible)
		{
			// Prefabs are not added into visible list.
			if (!obj->CheckFlags(OBJFLAG_PREFAB))
				m_visibleObjects.push_back( obj );
		}
	}
	m_bVisibleObjectValid = true;
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::FindAnimNodeOwner( IAnimNode* node ) const
{
	if (!node)
		return 0;
	IAnimNodeOwner *pOwner = node->GetNodeOwner();
	if (pOwner)
	{
		CEntity *pEntity = static_cast<CEntity*>(pOwner);
		return pEntity;
	}

	// Backward compatability.
	int nodeType = node->GetType();
	if (nodeType == ANODE_ENTITY || nodeType == ANODE_CAMERA)
	{
		uint32 animNodeId = node->GetId();
		for (Objects::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
		{
			CBaseObject *obj = it->second;
			if (obj->GetId().Data1 == animNodeId)
			{
				return obj;
			}
		}
	}

	return 0;
}

//////////////////////////////////////////////////////////////////////////
bool CObjectManager::ConvertToType( CBaseObject *object,ObjectType objectType )
{
	if (objectType == OBJTYPE_BRUSH)
	{
		if (object->IsKindOf(RUNTIME_CLASS(CEntity)))
		{
			CUndo undo( "Convert To Brush" );
			// Can convert Entity to brush.
			CBaseObjectPtr brushObject = GetIEditor()->NewObject( "Brush" );
			if (!brushObject)
				return false;

			if (!brushObject->ConvertFromObject( object ))
			{
				// delete this object.
				DeleteObject( brushObject );
				return false;
			}
			DeleteObject( object );
			return true;
		}
	}
	else if (objectType == OBJTYPE_ENTITY)
	{
		if (object->IsKindOf(RUNTIME_CLASS(CBrushObject)) || object->IsKindOf(RUNTIME_CLASS(CEntity))  )
		{
			CUndo undo( "Convert To GeomEntity" );
			// Can convert Entity to brush.
			CBaseObjectPtr ent = GetIEditor()->NewObject( "GeomEntity" );
			if (!ent)
				return false;

			if (!ent->ConvertFromObject( object ))
			{
				// delete this object.
				DeleteObject( ent );
				return false;
			}
			DeleteObject( object );
			return true;
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::SetObjectSelected( CBaseObject* pObject,bool bSelect )
{
	// Only select/unselect once.
	if ((pObject->IsSelected() && bSelect) || (!pObject->IsSelected() && !bSelect))
		return;

	// Store selection undo.
	if (CUndo::IsRecording())
		CUndo::Record( new CUndoBaseObjectSelect(pObject) );

	pObject->SetSelected(bSelect);
	m_bSelectionChanged = true;


	if (bSelect && !GetIEditor()->GetTransformManipulator())
	{
		if (CAxisGizmo::GetGlobalAxisGizmoCount() < gSettings.gizmo.axisGizmoMaxCount)
		{
			// Create axis gizmo for this object.
			m_gizmoManager->AddGizmo( new CAxisGizmo(pObject) );
		}
	}

	if (bSelect)
		NotifyObjectListeners( pObject,CBaseObject::ON_SELECT );
	else
		NotifyObjectListeners( pObject,CBaseObject::ON_UNSELECT );
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::HideTransformManipulators()
{
	m_gizmoManager->DeleteAllTransformManipulators();
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void CObjectManager::AddObjectEventListener( const EventCallback &cb )
{
	stl::push_back_unique( m_objectEventListeners,cb );
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::RemoveObjectEventListener( const EventCallback &cb )
{
	stl::find_and_erase( m_objectEventListeners,cb );
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::NotifyObjectListeners( CBaseObject *pObject,CBaseObject::EObjectListenerEvent event )
{
	std::list<EventCallback>::iterator next;
	for (std::list<EventCallback>::iterator it = m_objectEventListeners.begin(); it != m_objectEventListeners.end(); it = next)
	{
		next = it;
		++next;
		// Call listener callback.
		(*it)( pObject,event );
	}
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::StartObjectsLoading( int numObjects )
{
	if (m_pLoadProgress)
		return;
	m_pLoadProgress = new CWaitProgress( _T("Loading Objects") );
	m_totalObjectsToLoad = numObjects;
	m_loadedObjects = 0;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::EndObjectsLoading()
{
	if (m_pLoadProgress)
		delete m_pLoadProgress;
	m_pLoadProgress = 0;
}

//////////////////////////////////////////////////////////////////////////
void CObjectManager::GatherUsedResources( CUsedResources &resources )
{
	CBaseObjectsArray objects;
	GetIEditor()->GetObjectManager()->GetObjects( objects );

	for (int i = 0; i < objects.size(); i++)
	{
		CBaseObject *pObject = objects[i];
		pObject->GatherUsedResources( resources );
	}
}

//////////////////////////////////////////////////////////////////////////
IGizmoManager* CObjectManager::GetGizmoManager()
{
	return m_gizmoManager;
}

//////////////////////////////////////////////////////////////////////////
IStatObj* CObjectManager::GetGeometryFromObject( CBaseObject *pObject )
{
	assert( pObject );

	if (pObject->IsKindOf(RUNTIME_CLASS(CBrushObject)))
	{
		CBrushObject *pBrushObj = (CBrushObject*)pObject;
		return pBrushObj->GetIStatObj();
	}
	if (pObject->IsKindOf(RUNTIME_CLASS(CEntity)))
	{
		CEntity *pEntityObj = (CEntity*)pObject;
		if (pEntityObj->GetIEntity())
		{
			IEntity *pGameEntity = pEntityObj->GetIEntity();
			for (int i = 0; pGameEntity != NULL && i < pGameEntity->GetSlotCount(); i++)
			{
				if (pGameEntity->GetStatObj(i))
					return pGameEntity->GetStatObj(i);
			}
		}
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
ICharacterInstance* CObjectManager::GetCharacterFromObject( CBaseObject *pObject )
{
	assert( pObject );
	if (pObject->IsKindOf(RUNTIME_CLASS(CEntity)))
	{
		CEntity *pEntityObj = (CEntity*)pObject;
		if (pEntityObj->GetIEntity())
		{
			IEntity *pGameEntity = pEntityObj->GetIEntity();
			for (int i = 0; pGameEntity != NULL && i < pGameEntity->GetSlotCount(); i++)
			{
				if (pGameEntity->GetCharacter(i))
					return pGameEntity->GetCharacter(i);
			}
		}
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CObjectManager::FindPhysicalObjectOwner( IPhysicalEntity *pPhysicalEntity )
{
	if (!pPhysicalEntity)
		return 0;

	int itype = pPhysicalEntity->GetiForeignData();
	switch (itype)
	{
	case PHYS_FOREIGN_ID_ROPE:
		{
			IRopeRenderNode *pRenderNode = (IRopeRenderNode*)pPhysicalEntity->GetForeignData(itype);
			if (pRenderNode)
			{
				EntityId id = (EntityId)pRenderNode->GetEntityOwner();
				CEntity *pEntity = CEntity::FindFromEntityId(id);
				return pEntity;
			}
		}
		break;
	case PHYS_FOREIGN_ID_ENTITY:
		{
			IEntity *pIEntity = gEnv->pEntitySystem->GetEntityFromPhysics(pPhysicalEntity);
			if (pIEntity)
			{
				return CEntity::FindFromEntityId(pIEntity->GetId());
			}
		}
		break;
	case PHYS_FOREIGN_ID_STATIC:
		{
			IRopeRenderNode *pRenderNode = (IRopeRenderNode*)pPhysicalEntity->GetForeignData(itype);
			if (pRenderNode)
			{
				// Find brush who created this render node.

			}
		}
	}
	return 0;
}
