/*
	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.
*/

#ifndef GAME_OBJECT_MANAGER_H
#define GAME_OBJECT_MANAGER_H

//=====================================================================================================================
//=====================================================================================================================

namespace Game
{
	//=================================================================================================================
	//=================================================================================================================
	
	class GameObjectBase;
	class Camera2D;

	//=================================================================================================================
	/// @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.
	//=================================================================================================================

	class GameObjectManager : public ObjectBase
	{
		public:
		
			//=========================================================================================================
			// Friends
			//=========================================================================================================
			
				/// GameObjectBase is a friend of this class
				
				friend class GameObjectBase;
				
			//=========================================================================================================
			// Enums
			//=========================================================================================================
			
				/// Types of global object messages that can be sent
			
				enum GlobalObjectMessage
				{
					/// Sent when all objects have been added to the level
				
					GOM_LEVEL_READY
				};
		
			//=========================================================================================================
			// Constants
			//=========================================================================================================
			
				/// If a game object has this ID then it is invalid and means it has not been added into this manager
			
				static const int INVALID_ID = 0;
		
			//=========================================================================================================
			// Functions
			//=========================================================================================================
			
				/// Creates the game object manager
			
				GameObjectManager();
				
				/// Destroys the game object manager
				
				~GameObjectManager();
				
				/// Clears all of the objects in the manager:
								
				void Clear();
				
				/// Updates all objects by calling their OnUpdate() function
				
				void Update( float time_delta );

				/// Draws all objects by calling their OnDraw() function

				void Draw( Camera2D * c );
				
				/// Adds an object into the game
				
				void AddObject( GameObjectBase * object );
				
				/// Adds an object into the game 
				
				inline void AddObject( GameObjectBase & object ){ AddObject(&object); }
				
				/// Removes an object from the game
				
				void RemoveObject( GameObjectBase * object );
				
				/// Removes an object from the game 
				
				inline void RemoveObject( GameObjectBase & object ){ RemoveObject(&object); }
				
				/// Sends an event with the given id to all objects
				
				void SendGlobalEvent( unsigned event_id , long event_data );
				
				/// Tells if the manager contains the given object
				
				bool ContainsObject( GameObjectBase * object );
				
				/// Tells if the manager contains the given object
				
				inline bool ContainsObject( GameObjectBase & object ){ return ContainsObject(&object); }

				/// Returns a list of all the objects in the manager

				void GetObjectList( std::vector<GameObjectBase*> & objects );
				
				/// Searches for a game object by it's name and returns the first object found:
				
				GameObjectBase * FindByName( const char * name );
				
				/// Searches for a game object by it's name and returns all objects found with the same name
				
				void FindByName( const char * name , std::vector<GameObjectBase*> & objects );
				
				/// Searches for a game object by it's ID and returns the object found
				
				GameObjectBase * FindById( unsigned id );
				
				/// Tells the object to announce it's type name: this is the type name without the Game:: namespace

				virtual const char * GetTypeName() const;				
				
				//=====================================================================================================
				/// @brief		Searches for a game object by it's type name and returns the first object found:
				/// @param		type - name of the type to try and find
				/// @returns	A pointer to the object, or null if not found
				/// @remarks	The template parameter marks the concrete c++ type that we are looking for. Type
				///				checking is performed in debug to make sure the correct type is returned.
				/// @pre		The object found must cast to the type specified in the template parameter
				//=====================================================================================================
				
				template<class T> __forceinline T * FindByType( const char * type )
				{
					// Do a search
					
					GameObjectBase * obj = FindByTypeImpl(type);
					
					// See if found:
					
					if ( obj )
					{
						#ifdef DEBUG
						
							// Safety check:
							
							ENSURE( dynamic_cast<T*>(obj) != 0 , "All objects found must cast to the type specified in the template parameter" )														
						
						#endif
						
						// Return the object
						
						return (T*) obj;
					}
					else
					{
						// Just return null: nothing found
						
						return 0;
					}
				}
				
				//=====================================================================================================				
				/// @brief		Searches for a game object by it's type name and returns all objects found with the 
				///				same type.
				/// @param		type - name of the type to try and find
				/// @param		objects - list to save all of the objects found
				/// @returns	A pointer to the object, or null if not found
				/// @remarks	The template parameter marks the concrete c++ type that we are looking for. Type
				///				checking is performed in debug to make sure the correct type is returned.
				/// @pre		All objects found must cast to the type specified in the template parameter
				//=====================================================================================================
				
				template<class T> __forceinline void FindByType( const char * type , std::vector<GameObjectBase*> & objects )
				{
					// Do the search:
					
					FindByTypeImpl(type,objects);
					
					// In debug ensure all of the objects are ok:
					
					#ifdef DEBUG
					
						for ( int i = 0 ; i < objects.size() ; i++ )
						{
							ENSURE( dynamic_cast<T*>( objects[i] ) != 0 , "All objects found must cast to the type specified in the template parameter" )
						}
					
					#endif
				}
				
		private:
		
			//=========================================================================================================
			// Variables
			//=========================================================================================================
			
				/// A map identifying all game object's by their ID uniquely
				
				std::map<unsigned,GameObjectBase*> m_objects;
				
				/// A map identifying all game object's by their name: name does not have to be unique. Names are stored in lowercase.
				
				std::map< std::string, std::map< unsigned,GameObjectBase*> > m_names;
				
				/// A map identifying all game object's by their type: type does not have to be unique. Types are stored given case.
				
				std::map< std::string, std::map<unsigned,GameObjectBase*> > m_types;
				
				/// A list containing recently released object ID's. These are reused where possible:
				
				std::list<unsigned> m_free_ids;
				
				/// The next highest free id: if there are no free id's in the list then this one is used
				
				unsigned m_next_id;
				
			//=========================================================================================================
			// Functions
			//=========================================================================================================
			
				/// Adds the given object to the map of all objects (identified by id)
				
				void AddToObjectMap( GameObjectBase * object );

				/// Adds the given object to the map of all types (identified by type name)
				
				void AddToTypeMap( GameObjectBase * object );
				
				/// Adds the given object to the map of all names (identified by name)
				
				void AddToNameMap( GameObjectBase * object );
				
				/// Removes the given object from the map of all objects (identified by id)
				
				void RemoveFromObjectMap( GameObjectBase * object );

				/// Removes the given object from the map of all types (identified by type name)
				
				void RemoveFromTypeMap( GameObjectBase * object );
				
				/// Removes the given object from the map of all names (identified by name)
				
				void RemoveFromNameMap( GameObjectBase * object );
				
				/// Searches for a game object by it's type name and returns the first object found: this is the implementation of this function
				
				GameObjectBase * FindByTypeImpl( const char * type );
				
				/// Searches for a game object by it's type name and returns all objects found with the same type: this is the implementation of this function
				
				void FindByTypeImpl( const char * type , std::vector<GameObjectBase*> & objects );
	};
}

#endif
