/*
	Trig Wars.
	Copyright (c) 2008, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//=====================================================================================================================
/// @brief		This class is responsible for holding and managing all of the object's in the game. Add objects 
///				to this class to allow for OnUpdate() events to be called and use this class to search for objects
///				by certain criteria.
//=====================================================================================================================

#include "Precompiled.h"
#include "Game\GameObjectManager.h"
#include "GameObjects\GameObjectBase.h"

using namespace Game;
using namespace std;

//=====================================================================================================================
/// @brief		Creates the game object manager.
//=====================================================================================================================

GameObjectManager::GameObjectManager()
{
	// Set the next free id counter
	
	m_next_id = 1;
}

//=====================================================================================================================
/// @brief		Destroys the game object manager.
//=====================================================================================================================

GameObjectManager::~GameObjectManager()
{
	// Clear the object list:
	
	Clear();
}

//=====================================================================================================================
/// @brief		Annouces the type name for this object.
/// @returns	Type name of this object
//=====================================================================================================================

const char * GameObjectManager::GetTypeName() const
{
	return "GameObjectManager";
}

//=====================================================================================================================
/// @brief		Clears all of the objects in the manager
/// @post		All object lists should be cleared 
//=====================================================================================================================

void GameObjectManager::Clear()
{	
	// Build a list of objects to be removed:
	
	list<GameObjectBase*> remove_objects;
	
		map<unsigned,GameObjectBase*>::iterator i = m_objects.begin();
		map<unsigned,GameObjectBase*>::iterator e = m_objects.end();
		
		// Make the list
		
		while ( i != e )
		{
			remove_objects.push_back((*i).second); ++i;
		}
		
	// Now remove everything:
	
	while ( remove_objects.size() > 0 )
	{
		RemoveObject( remove_objects.front() ); remove_objects.pop_front();
	}

	// Clear the list of free Id's
	
	m_free_ids.clear();

	// Set the next free id back to 1:

	m_next_id = 1;
	
	// Make sure all lists are empty:
	
	ENSURE( m_objects.size()	== 0 , "All object lists should be cleared" );
	ENSURE( m_types.size()		== 0 , "All object lists should be cleared" );
	ENSURE( m_names.size()		== 0 , "All object lists should be cleared" );
}

//=====================================================================================================================
/// @brief		Adds the given object into the game. The object will receive update events and be searchable
///				through this class.
/// @brief		object - object to add into the game
/// @pre		Given object cannot be null
/// @pre		Given object must not be in the manager already, or in another manager
/// @post		An id overflow cannot occur
/// @post		Chosen object ID must be valid
//=====================================================================================================================

void GameObjectManager::AddObject( GameObjectBase * object )
{
	// Make sure the object is ok:
	
	ENSURE( object != 0 , "Given object cannot be null" );
	
	// Make sure we are not out of id's
	
	ENSURE( ( m_free_ids.size() > 0 ) || ( m_next_id != (unsigned)(-1) ) , "An id overflow cannot occur" );
	
	// Make sure the object is not already in the manager:
	
	ENSURE( object->GetID() == INVALID_ID && object->GetManager() == 0 , "Given object must not be in the manager already, or in another manager" );
	
	// See if we have a freed ids:
	
	if ( m_free_ids.size() > 0 )
	{
		// Good: get the first one available
		
		int id = m_free_ids.front(); m_free_ids.pop_front();
		
		// Now assign the id:
		
		object->m_id = id;
		
		// Make sure it is not invalid:
		
		ENSURE( object->GetID() != INVALID_ID , "Chosen object ID must be valid" );
	}
	else
	{
		// Make sure it is not invalid:
		
		ENSURE( m_next_id != INVALID_ID , "Chosen object ID must be valid" );
		
		// No free id's - use the next free one:
		
		object->m_id = m_next_id;
		
		// Need to use a higher id next time
		
		m_next_id++;
	}
	
	// Add the object into all the maps:
	
	AddToObjectMap(object);
	AddToTypeMap(object);
	AddToNameMap(object);
	
	// Add a reference to the object:
	
	object->AddReference();
	
	// Reference ourselves on behalf of the object: so we are not deleted on it
	
	this->AddReference();
	
	// Set the manager on the object:
	
	object->m_manager = this;
	
	// Call the OnGameAddded() function
	
	object->OnGameAdded();	
}

//=====================================================================================================================
/// @brief		Removes the given object from the game. The object will not receive update events and will not 
///				be searchable thereafter.
/// @brief		object - object to remove from the game
/// @pre		Given object cannot be null
/// @pre		Given object must be present in the object manager
//=====================================================================================================================

void GameObjectManager::RemoveObject( GameObjectBase * object )
{
	// Make sure the object is ok:
	
	ENSURE( object != 0 , "Given object cannot be null" );
	
	// Make sure the object is not already in the manager:
	
	ENSURE( object->GetID() != INVALID_ID && object->GetManager() != 0 , "Given object must be present in the object manager" );
	
	// Call the OnGameRemove() function
	
	object->OnGameRemoved();	
	
	// Remove the object from all maps:
	
	RemoveFromObjectMap(object);
	RemoveFromTypeMap(object);
	RemoveFromNameMap(object);
	
	// Add the object's id to the list of free ids:
	
	m_free_ids.push_back( object->m_id );
		
	// Clear the object's id and manager:
	
	object->m_id = INVALID_ID; object->m_manager = 0;
	
	// Remove reference to the object
	
	object->RemoveReference();
	
	// Remove the previously added reference to ourselves on behalf of the object
	
	this->RemoveReference();
}

//=====================================================================================================================
/// @brief		Draws all objects by calling their OnDraw() function
/// @param		c - camera to draw from
/// @pre		Given camera must be valid
/// @pre		There must be no null objects in the manager
//=====================================================================================================================

void GameObjectManager::Draw( Camera2D * c )
{
	// Make sure ok:

	ENSURE(c,"Given camera must be valid");

	// Run through the map:
	
	map<unsigned,GameObjectBase*>::iterator i = m_objects.begin();
	map<unsigned,GameObjectBase*>::iterator e = m_objects.end();
	
	while ( i != e )
	{
		// Get the object and move on first in case the object is deleted:
		
		GameObjectBase * obj = (*i).second; ++i;
		
		// Make sure ok:
		
		ENSURE( obj != 0 , "There must be no null objects in the manager" );
	
		// Draw:
	
		obj->OnDraw(c);
	}	
}

//=====================================================================================================================
/// @brief		Updates all objects by calling their OnUpdate() function
/// @param		time_delta - amount of time passed since this frame and the last one
/// @pre		Time delta must be >= 0
/// @pre		There must be no null objects in the manager
//=====================================================================================================================

void GameObjectManager::Update( float time_delta )
{
	// Make sure ok:
	
	ENSURE( time_delta >= 0 , "Time delta must be >= 0" );

	// Run through the map:
	
	map<unsigned,GameObjectBase*>::iterator i = m_objects.begin();
	map<unsigned,GameObjectBase*>::iterator e = m_objects.end();
	
	while ( i != e )
	{
		// Get the object and move on first in case the object is deleted:
		
		GameObjectBase * obj = (*i).second; ++i;
		
		// Make sure ok:
		
		ENSURE( obj != 0 , "There must be no null objects in the manager" );		
	
		// Update:
	
		obj->OnUpdate(time_delta);
	}
}

//=====================================================================================================================
/// @brief		Sends an event with the given id to all objects
/// @param		event_id - identifier identifying the event
/// @param		event_data - optional data particular to the event
/// @pre		There must be no null objects in the manager
//=====================================================================================================================

void GameObjectManager::SendGlobalEvent( unsigned event_id , long event_data )
{
	// Run through all the objects:

	map< unsigned , GameObjectBase * >::iterator i = m_objects.begin();
	map< unsigned , GameObjectBase * >::iterator e = m_objects.end();

	// Send the event to all of them:

	while ( i != e )
	{
		// Get the object and move on first in case the object is deleted:
		
		GameObjectBase * obj = (*i).second; ++i;
		
		// Make sure ok:
		
		ENSURE( obj != 0 , "There must be no null objects in the manager" );		
	
		// Send event:
	
		obj->OnGlobalEvent(event_id,event_data);
	}
}

//=====================================================================================================================
/// @brief		Returns a list of all the objects in the manager
/// @param		objects - vector to add all the objects to
/// @pre		The given vector should be empty before calling
//=====================================================================================================================

void GameObjectManager::GetObjectList( vector<GameObjectBase*> & objects )
{
	// Make sure ok:

	ENSURE( objects.size() == 0 , "The given vector should be empty before calling" );

	// Make the vector big enough:

	objects.resize( m_objects.size() );

	// Run through all the objects:

	map< unsigned , GameObjectBase * >::iterator i = m_objects.begin();
	map< unsigned , GameObjectBase * >::iterator e = m_objects.end();

	unsigned object_num = 0;

	// Do it:

	while ( i != e )
	{
		objects[ object_num ] = (*i).second; ++i; object_num++;
	}
}

//=====================================================================================================================
/// @brief		Adds the object into the overall objects map, where it is indentified by id.
/// @param		object - object to add
/// @pre		Object must be valid
/// @pre		Object cannot already be present in the global objects map
//=====================================================================================================================

void GameObjectManager::AddToObjectMap( GameObjectBase * object )
{
	// Make sure the object is ok:
	
	ENSURE( object != 0 , "Given object cannot be null" );
	
	// Make sure that this id isn't already in the map:
	
	ENSURE( m_objects.find(object->GetID()) == m_objects.end() , "Object cannot already be present in the global objects map" );
	
	// Add it in:
	
	m_objects[object->GetID()] = object;
}

//=====================================================================================================================
/// @brief		Adds the given object to the to map of all types (identified by type name)
/// @param		object - object to add
/// @pre		Object must be valid
/// @pre		Object cannot already be present in the internal container of objects for this type
/// @pre		Object type name must be valid
//=====================================================================================================================

void GameObjectManager::AddToTypeMap( GameObjectBase * object )
{
	// Make sure the object is ok:
	
	ENSURE( object != 0 , "Given object cannot be null" );
	
	// Make sure type name is ok:
	
	ENSURE( object->GetTypeName() != 0 , "Object type name must be valid" ); 
	
	// Make sure type name is ok:
	
	ENSURE( strlen(object->GetTypeName()) > 0 , "Object type name must be valid" );
	
	// Get the type name:
	
	string type_name = object->GetTypeName();
	
	// Try and get the map for this type name:
	
	map< string, map<unsigned,GameObjectBase*> >::iterator i = m_types.find(type_name);
	
	// See if we have a map for this type name:
	
	if ( i != m_types.end() )
	{
		// Get the map for this type name:
		
		map<unsigned,GameObjectBase*> & type_map = (*i).second;
		
		// This should never be empty if present:
		
		ENSURE( type_map.size() > 0 , "Individual type lists should not exist if empty" ); 
		
		// Make sure it isn't in it already:
		
		ENSURE( type_map.find(object->GetID()) == type_map.end() , "Object cannot already be present in the internal container of objects for this type" ); 
		
		// Good: now add the object in
		
		type_map[object->GetID()] = object;
	}
	else
	{
		// Don't have a map for this type: make it
		
		map<unsigned,GameObjectBase*> & type_map = m_types[type_name];
		
		// Add the object into this map:
		
		type_map[object->GetID()] = object;
	}
}

//=====================================================================================================================
/// @brief		Adds the given object to the map of all object names
/// @param		object - object to include in the name dictionary
/// @pre		Object must be valid
/// @pre		Object cannot already be present in the internal container of objects for this type
//=====================================================================================================================

void GameObjectManager::AddToNameMap( GameObjectBase * object )
{
	// Make sure the object is ok:
	
	ENSURE( object != 0 , "Given object cannot be null" );
	
	// Make a lowercase copy of the object's name:
	
	string name_lower = object->GetName(); String::ToLower(name_lower);
	
	// Try and get the map for this name:
	
	map< string , map<unsigned,GameObjectBase*> >::iterator i = m_names.find(name_lower);
	
	// See if we have a map for this name:
	
	if ( i != m_names.end() )
	{
		// Get the map for this name:
		
		map<unsigned,GameObjectBase*> & name_map = (*i).second;
		
		// This should never be empty if present:
		
		ENSURE( name_map.size() > 0 , "Individual name lists should not exist if empty" ); 
		
		// Make sure it isn't in it already:
		
		ENSURE( name_map.find(object->GetID()) == name_map.end() , "Object cannot already be present in the internal container of objects for this name" ); 
		
		// Good: now add the object in
		
		name_map[object->GetID()] = object;
	}
	else
	{
		// Don't have a map for this name: make it
		
		map<unsigned,GameObjectBase*> & name_map = m_names[name_lower];
		
		// Add the object into this map:
		
		name_map[object->GetID()] = object;
	}
}

//=====================================================================================================================
/// @brief		Removes the given object from the map of all objects (identified by id)
/// @param		object - object to be removed
/// @pre		Object must be valid
/// @pre		Object must be present in the overall object dictionary
//=====================================================================================================================

void GameObjectManager::RemoveFromObjectMap( GameObjectBase * object )
{
	// Make sure the object is ok:
	
	ENSURE( object != 0 , "Given object cannot be null" );
	
	// Find the object in the map:
	
	map<unsigned,GameObjectBase*>::iterator i = m_objects.find( object->GetID() );
	
	// Make sure it's present:
	
	ENSURE( i != m_objects.end() , "Object must be present in the overall object dictionary" );
	
	// Remove it:
	
	m_objects.erase(i);
}

//=====================================================================================================================
/// @brief		Removes the given object from the map of all types (identified by type name)
/// @param		object - object to be removed
/// @pre		Object must be valid
/// @pre		There must exist a dicitionary internally for the object's type name
/// @pre		Object must be present in the dictionary for it's type name
//=====================================================================================================================

void GameObjectManager::RemoveFromTypeMap( GameObjectBase * object )
{
	// Make sure the object is ok:
	
	ENSURE( object != 0 , "Given object cannot be null" );
	
	// Get the object's type name:
	
	string type_name = object->GetTypeName();
	
	// Find the object's type name map:
	
	map< string , map<unsigned,GameObjectBase*> >::iterator i = m_types.find( type_name );
	
	// Make sure it's present:
	
	ENSURE( i != m_types.end() , "There must exist a dicitionary internally for the object's type name" );
	
	// Good: now get the dictionary for this type name
	
	map<unsigned,GameObjectBase*> & type_map = (*i).second;
	
	// Get an iterator to the object in it's type name dictionary:
	
	map<unsigned,GameObjectBase*>::iterator j = type_map.find( object->GetID() );
	
	// Make sure the object is in this dictionary
	
	ENSURE( j != type_map.end() , "Object must be present in the dictionary for it's type name" );
	
	// Remove it from it's own type name dictionary:
	
	type_map.erase(j);
	
	// If the type map is now empty then remove it from the list of type names:
	
	if ( type_map.size() == 0 ) m_types.erase(i);
}

//=====================================================================================================================
/// @brief		Removes the given object from the map of all names (identified by name)
/// @param		object - object to be removed
/// @pre		Object must be valid
/// @pre		There must exist a dicitionary internally for the object's name
/// @pre		Object must be present in the dictionary for it's name
//=====================================================================================================================

void GameObjectManager::RemoveFromNameMap( GameObjectBase * object )
{
	// Make sure the object is ok:
	
	ENSURE( object != 0 , "Given object cannot be null" );
	
	// Get the object's name and make it lowercase:
	
	string name = object->GetName(); String::ToLower(name);
	
	// Find the object's name map:
	
	map< string , map<unsigned,GameObjectBase*> >::iterator i = m_names.find( name );
	
	// Make sure it's present:
	
	ENSURE( i != m_names.end() , "There must exist a dicitionary internally for the object's name" );
	
	// Good: now get the dictionary for this name
	
	map<unsigned,GameObjectBase*> & name_map = (*i).second;
	
	// Get an iterator to the object in it's name dictionary:
	
	map<unsigned,GameObjectBase*>::iterator j = name_map.find( object->GetID() );
	
	// Make sure the object is in this dictionary
	
	ENSURE( j != name_map.end() , "Object must be present in the dictionary for it's name" );
	
	// Remove it from it's own name dictionary:
	
	name_map.erase(j);
	
	// If the name map is now empty then remove it from the list of names:
	
	if ( name_map.size() == 0 ) m_names.erase(i);
}

//=====================================================================================================================
/// @brief		Searches for a game object by it's name and returns the first object found:
/// @param		name - name of the object to find
/// @remarks	The given name is NOT case sensitive
/// @pre		Given name cannot be null
/// @post		Object given cannot not be null if present
/// @post		Object returned must have a valid id
/// @post		The id of the object must not contradict the id in the manager
//=====================================================================================================================

GameObjectBase * GameObjectManager::FindByName( const char * name )
{
	// Make sure the name is ok:
	
	ENSURE( name != 0 , "Given name cannot be null" );
	
	// Make a lowercase version of the name
	
	string name_lower = name; String::ToLower(name_lower);
	
	// Do a lookup:
	
	map< string, map<unsigned,GameObjectBase*> >::iterator i = m_names.find(name_lower);
	
	// See if we got anything:
	
	if ( i != m_names.end() )
	{
		// Get this map:
	
		map<unsigned,GameObjectBase*> & name_map = (*i).second;
		
		// Should never be empty:
		
		ENSURE( name_map.size() > 0 , "Dictionary for objects of this name should never exist if empty" );
		
		// Should never be null:
		
		ENSURE( (*(name_map.begin())).second != 0 , "Object given cannot not be null if present" );

		// Should be valid:
		
		ENSURE( (*(name_map.begin())).second->GetID() != INVALID_ID , "Object returned must have a valid id" );
		
		// Make sure id is ok:
		
		ENSURE( (*(name_map.begin())).second->GetID() == (*(name_map.begin())).first , "The id of the object must not contradict the id in the manager" );
		
		// Return the object:
		
		return (*name_map.begin()).second;
	}
	else
	{
		// Can't find:
		
		return 0;
	}
}

//=====================================================================================================================
/// @brief		Searches for a game object by it's name and returns all of the objects found:
/// @param		name - name of the object to find
/// @param		objects - vector to save results to
/// @remarks	The given name is NOT case sensitive
/// @pre		Given name cannot be null
/// @post		Objects given cannot not be null if present
/// @post		Objects returned must have a valid id
/// @post		The id of the object must not contradict the id in the manager
//=====================================================================================================================

void GameObjectManager::FindByName( const char * name , vector<GameObjectBase*> & objects )
{
	// Make sure the name is ok:
	
	ENSURE( name != 0 , "Given name cannot be null" );
	
	// Make a lowercase version of the name
	
	string name_lower = name; String::ToLower(name_lower);	
	
	// Do a lookup:
	
	map< string, map<unsigned,GameObjectBase*> >::iterator i = m_names.find(name_lower);
	
	// See if we got anything:
	
	if ( i != m_names.end() )
	{
		// Get this map:
	
		map<unsigned,GameObjectBase*> & name_map = (*i).second;
		
		// Should never be empty:
		
		ENSURE( name_map.size() > 0 , "Dictionary for objects of this name should never exist if empty" );
		
		// Make room in the vector:
		
		objects.resize(name_map.size());
		
		// Fill it in:
		
		map<unsigned,GameObjectBase*>::iterator j = name_map.begin();
		map<unsigned,GameObjectBase*>::iterator e = name_map.end();
		
		unsigned object_number = 0;
		
		while ( j != e )
		{
			// Should never be null:
			
			ENSURE( (*j).second != 0 , "Objects given cannot not be null if present" );
			
			// Should be valid:
			
			ENSURE( (*j).second->GetID() != INVALID_ID , "Objects returned must have a valid id" );
			
			// Make sure id is ok:
			
			ENSURE( (*j).second->GetID() == (*j).first , "The id of the object must not contradict the id in the manager" );					
		
			// Save
		
			objects[object_number] = (*j).second;
			
			// Next object
			
			object_number++; j++;
		}
	}
	else
	{
		// Nothing found:
		
		objects.resize(0);
	}
}

//=====================================================================================================================
/// @brief		Searches for a game object by it's type name and returns the first object found. This is the internal
///				implementation of this function that is not type checked in debug.
/// @param		type - type name of the object to find
/// @returns	First object of this type, if any.
/// @remarks	Type name IS case sensitive
/// @pre		Given type name cannot be null
/// @post		Object given cannot not be null if present
/// @post		Object returned must have a valid id
/// @post		The id of the object must not contradict the id in the manager
//=====================================================================================================================

GameObjectBase * GameObjectManager::FindByTypeImpl( const char * type )
{
	// Make sure the name is ok:
	
	ENSURE( type != 0 , "Given type name cannot be null" );
	
	// Do a lookup:
	
	map< string, map<unsigned,GameObjectBase*> >::iterator i = m_types.find(type);
	
	// See if we got anything:
	
	if ( i != m_types.end() )
	{
		// Get this map:
	
		map<unsigned,GameObjectBase*> & type_map = (*i).second;
		
		// Should never be empty:
		
		ENSURE( type_map.size() > 0 , "Dictionary for objects of this type should never exist if empty" );
		
		// Should never be null:
		
		ENSURE( (*(type_map.begin())).second != 0 , "Object given cannot not be null if present" );
		
		// Should be valid:
		
		ENSURE( (*(type_map.begin())).second->GetID() != INVALID_ID , "Object returned must have a valid id" );
		
		// Make sure id is ok:
		
		ENSURE( (*(type_map.begin())).second->GetID() == (*(type_map.begin())).first , "The id of the object must not contradict the id in the manager" );
		
		// Return the object:
		
		return (*type_map.begin()).second;
	}
	else
	{
		// Can't find:
		
		return 0;
	}	
}

//=====================================================================================================================
/// @brief		Searches for a game object by it's type name and returns the first object found. This is the internal
///				implementation of this function that is not type checked in debug.
/// @param		type - type name of the object to find
/// @param		objects - vector to save results to
/// @returns	First object of this type, if any.
/// @remarks	Type name IS case sensitive
/// @pre		Given type name cannot be null
/// @post		Objects given cannot not be null if present
/// @post		Objects returned must have a valid id
/// @post		The id of the object must not contradict the id in the manager
//=====================================================================================================================

void GameObjectManager::FindByTypeImpl( const char * type , vector<GameObjectBase*> & objects )
{
	// Make sure the name is ok:
	
	ENSURE( type != 0 , "Given type name cannot be null" );
	
	// Do a lookup:
	
	map< string, map<unsigned,GameObjectBase*> >::iterator i = m_types.find(type);
	
	// See if we got anything:
	
	if ( i != m_names.end() )
	{
		// Get this map:
	
		map<unsigned,GameObjectBase*> & type_map = (*i).second;
		
		// Should never be empty:
		
		ENSURE( type_map.size() > 0 , "Dictionary for objects of this type should never exist if empty" );
		
		// Make room in the vector:
		
		objects.resize(type_map.size());
		
		// Fill it in:
		
		map<unsigned,GameObjectBase*>::iterator j = type_map.begin();
		map<unsigned,GameObjectBase*>::iterator e = type_map.end();
		
		unsigned object_number = 0;
		
		while ( j != e )
		{
			// Should never be null:
			
			ENSURE( (*j).second != 0 , "Objects given cannot not be null if present" );
			
			// Should be valid:
			
			ENSURE( (*j).second->GetID() != INVALID_ID , "Objects returned must have a valid id" );
			
			// Make sure id is ok:
			
			ENSURE( (*j).second->GetID() == (*j).first , "The id of the object must not contradict the id in the manager" );
		
			// Save
		
			objects[object_number] = (*j).second;
			
			// Next object
			
			object_number++; j++;
		}
	}
	else
	{
		// Nothing found:
		
		objects.resize(0);
	}	
}

//=====================================================================================================================
/// @brief		Searches for a game object by it's ID and returns the object found
/// @param		id - id of the object to get
/// @remarks	An invalid id will simply return nothing
/// @post		An invalid id object will never be returned
/// @post		The id of the object must not contradict the id in the manager
//=====================================================================================================================

GameObjectBase * GameObjectManager::FindById( unsigned id )
{
	// Search:
	
	map< unsigned , GameObjectBase * >::iterator i = m_objects.find(id);
	
	// See if found:
	
	if ( i != m_objects.end() )
	{	
		// Make sure we are not saving invalid id's:
		
		ENSURE( id != INVALID_ID , "There must be no objects with invalid id's saved" );
		
		// Make sure id is ok:
		
		ENSURE( (*i).second->GetID() == (*i).first , "The id of the object must not contradict the id in the manager" );
		
		// Return the object:
		
		return (*i).second;
	}
	else
	{
		return 0;
	}
}


//=====================================================================================================================
/// @brief		Tells if the manager contains the given object
/// @param		object - object to check
/// @returns	true if the object is held by the manager
/// @pre		The given object must not be null
//=====================================================================================================================

bool GameObjectManager::ContainsObject( GameObjectBase * object )
{
	// Make sure not null

	ENSURE( object != 0 ,   "The given object must not be null" );

	// If it has an id then it is in the manager:
	
	return object->GetID() != INVALID_ID;
}
