//
// Asura_Entity_System.h
//
// Created by KOR - 22/03/2006
// 
// Contains Asura_Entity_System, which previously resided in Asura_Entity.cpp,
// but was split off for clarity.
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_ENTITY_SYSTEM_H__
#define __ASURA_ENTITY_SYSTEM_H__

#include "Core/Asura_Combined_List.h"
#include "Core/Asura_DynamicBVH.h"
#include "Entity/Asura_Entity_PeriodicClientUpdateList.h"
#include "Network/Asura_Guid.h"
#include "Network/Asura_Network.h"

class Asura_Message;
class Asura_Entity;
class Asura_Entity_Client;
class Asura_Entity_Renderable;
class Asura_Entity_Server;
template<class T> class Asura_DynamicBVH;

typedef bool ProjectDeliverMessageToServerEntityFunction(const Asura_Message* pxMessage);

#ifdef ASURA_TOOLS
//Enumeration for identifying the ranges from which tools created guids can be assigned
enum Tools_GuidRange
{
	TOOLS_GUIDRANGE_ENTITY = 0, //For guids assigned by the main entity file
	TOOLS_GUIDRANGE_CUTSCENE = 1,//and the cutscene file
	TOOLS_GUIDRANGE_SOUND = 2,//the sound file (for sound controllers)
	TOOLS_GUIDRANGE_MISSIONSOUND = 3,//the mission sound file (if doing mission file stuff)

	TOOLS_NUM_GUIDRANGE,
};
#endif

#if defined(ASURA_USE_PROFILING_SYSTEM) && defined(ASURA_DEBUG_VARIABLE_SYSTEM)

#if !defined(ASURA_MAX_ENTITY_PROFILE_CLASSIFICATIONS)
#define ASURA_MAX_ENTITY_PROFILE_CLASSIFICATIONS	256
#endif	//!defined(ASURA_MAX_ENTITY_PROFILE_CLASSIFICATIONS)


////////////////////////////////////////////////////////////////////////////////
// Class        : Asura_Entity_System_ProfileData
// Author       : Andy Tate
// Date         : 23-Mar-2009
//
// Description  : Class used to store the profile data used for both client
//				  and server-data.
////////////////////////////////////////////////////////////////////////////////

class Asura_Entity_System_ProfileData
{
	// Friend classes...
	friend class Asura_Entity_System;

public:
	// Public functions...
	Asura_Entity_System_ProfileData(void)	{}
	//~Asura_Entity_System_ProfileData();

	template <class T> void	CountEntities(Asura_Classified_List<T>& xEntityList);
	template <class T> void	UpdateEntities(Asura_Classified_List<T>& xEntityList);
	template <class T> void	UpdateDVS(const Asura_UnicodeChar* wszParentDVSEntry, T* pxValues);

protected:
	// Protected variables...

	// Protected functions...


private:
	// Private variables...
	u_int	m_uNumEntities;
	u_int	m_uNumEntitiesUpdating;
	u_int	m_uNumClassificationsFound;
	float	m_fEntitiesUpdateTime;
	bool	m_bProfileEntityUpdatesThisFrame;

	u_short	m_ausEntityClassifications[ASURA_MAX_ENTITY_PROFILE_CLASSIFICATIONS];
	u_int	m_auNumEntitiesPerClassification[ASURA_MAX_ENTITY_PROFILE_CLASSIFICATIONS];
	u_int	m_auNumEntitiesUpdatingPerClassification[ASURA_MAX_ENTITY_PROFILE_CLASSIFICATIONS];
	float	m_afTimeUpdatingEntitiesPerClassification[ASURA_MAX_ENTITY_PROFILE_CLASSIFICATIONS];

	// Private functions...
	Asura_Entity_System_ProfileData(const Asura_Entity_System_ProfileData& xOther);

	Asura_Entity_System_ProfileData&	operator=(const Asura_Entity_System_ProfileData& xOther);

};   // End of class 'Asura_Entity_System_ProfileData'.

#endif	//defined(ASURA_USE_PROFILING_SYSTEM) && defined(ASURA_DEBUG_VARIABLE_SYSTEM)


/// Kevin Lea - 27/11/2001 16:10:51 - entity system; this has the
/// responsibility to registers entities (giving them a guid) and adds 
/// them to a hash table so they can be quickly found by their guid.
class Asura_Entity_System
{
public:
	static void Initialise(void);
	static void Reset(void);
#if defined(ASURA_TOOLS)
	static void ResetNextToolsGuid();
#endif	// defined(ASURA_TOOLS)
	static void ResetRecentlyDestroyedSystem();
	static void Shutdown(void);

    static void InitialiseServerEntitiesFromBlueprints();
    static void PostLoadInitialise(void);
    static void PostCreateInitialise(void); // Client equivalent to above. At this time, I can't see a good way to hook this up automatically, so it will be left to projects to call.

	// UpdateInactiveClientEntitiesAtEndOfFrame()
	// This function causes MakeClientUpdateMessage() to be called on all server entities which 
	// are inactive but will send client updates when they're activated. The client entities are
	// deactivated immediately. This helps all the client entities to be created at the start of 
	// a level or after a load. It's only really going to work for non-multiplayer games. Therefore
	// it asserts if external network is active.
	static void UpdateInactiveClientEntitiesAtEndOfFrame();

	static void BeginUpdate(void);
	static void Update(Asura_Classified_List<Asura_Entity_Client>& xClientList, Asura_Classified_List<Asura_Entity_Server>& xServerList);
	static void EndUpdate(void);
	static void Update(void);
	static void BeginRender(void);
	static void Render(Asura_Classified_List<Asura_Entity_Renderable>& xList);
	static void EndRender(void);
	static void Render(void);
    static void PruneEntityLists(void);

	// (MH - 29.03.2004) added parameter to say whether generated messages will be sent
	// out over the network this frame. If they are, we need to make sure that all entities
	// needing an update have made one, otherwise we can just do some work in the meantime.
	static void GetClientUpdates(bool bSendingThisFrame);

	static void RegisterClientEntity(Asura_Entity* entity);
	static void RegisterServerEntity(Asura_Entity* entity, bool createdByTools = false);
	
	static Asura_Guid GetCreatedByGameGuid();

#if defined(ASURA_TOOLS)
	//assigns next tools guid from entity or cutscene range depending on current setting
	static Asura_Guid GetCreatedByToolsGuid(void);
	//assigns next tools guid from entity file range
	static Asura_Guid GetCreatedByToolsGuid_EntityFile(void){return GetCreatedByToolsGuid(TOOLS_GUIDRANGE_ENTITY);}
	//assigns next tools guid from cutscene file range
	static Asura_Guid GetCreatedByToolsGuid_CutsceneFile(void) {return GetCreatedByToolsGuid(TOOLS_GUIDRANGE_CUTSCENE);}
	//assigns next tools guid from the specified range
	static Asura_Guid GetCreatedByToolsGuid(Tools_GuidRange eRange);
	//gets the next guid to be assigned from entity or cutscene range depending on current setting
	static Asura_Guid GetNextToolsCreatedGuid();
	static Asura_Guid GetNextToolsCreatedGuid_EntityFile(){return GetNextToolsCreatedGuid(TOOLS_GUIDRANGE_ENTITY);}
	static Asura_Guid GetNextToolsCreatedGuid_CutsceneFile(){return GetNextToolsCreatedGuid(TOOLS_GUIDRANGE_CUTSCENE);}
	static Asura_Guid GetNextToolsCreatedGuid(Tools_GuidRange eRange);

	//Used by the loaders to set the next guid to allocate. Ignored if the next guid is already higher
	static void SetNextToolsCreatedGuid(Tools_GuidRange eRange, Asura_Guid uNextGuid);
#endif	// defined(ASURA_TOOLS)

	//determines whether guids should be assigned from cutscene file or entity file range
	static void SetAssignCutsceneFileGuids(bool bAssign){s_bAssignGuidsFromCutsceneRange = bAssign;}
	static bool GetAssignCutsceneFileGuids(bool bAssign){return s_bAssignGuidsFromCutsceneRange;}

#if defined(ASURA_TOOLS)
	static void RegisterToolsGuid(Asura_Guid guid);//used by loaders to make sure NextToolsCreatedGuid is higher than the guids of loaded entities
#endif	// defined(ASURA_TOOLS)
	static void RegisterGameCreatedGuid(Asura_Guid guid);//used by loaders to make sure NextGameCreatedGuid is higher than the guids of loaded entities

#ifdef ASURA_SUPPORT_NETMSG_SYSTEM
	// (MH - 28.04.2003) for the system which reduces the amount of update data
	// being sent over the network: this provides a means for entity types to 
	// define the fields of their update message. This is analogous to ClientCreateEntity.
	// GetUpdateHeartbeat() allows for various types to use different rates for full updates.
	// e.g. a player entity might send a full update every 2 seconds, a static object every 5 seconds.
	static bool RegisterUpdateMessage(u_short usMsgID, u_short usEntityClassification);
	static float GetUpdateHeartbeat(u_short usMsgID, u_short usEntityClassification);
#endif // #ifdef ASURA_SUPPORT_NETMSG_SYSTEM

	static bool DeliverMessageToClientEntity(const Asura_Message* pxMessage);
	static bool DeliverMessageToServerEntity(const Asura_Message* pxMessage);
	static bool DeliverMessageToPlayerEntity(const Asura_Message* pxMessage);
	static bool DeliverMessageToOtherPlayers(const Asura_Message* pxMessage);
	static bool DeliverMessageToAllPlayers(const Asura_Message* pxMessage);

	static void SetProjectDeliverMessageToServerEntityFunction(ProjectDeliverMessageToServerEntityFunction* const pfnProjectDeliverMessageToServerEntity) { s_pfnProjectDeliverMessageToServerEntity = pfnProjectDeliverMessageToServerEntity; }

	static void FindEntityInLOS(const class Asura_LOS_InputData* pxInputData, class Asura_LOS_OutputData* pxOutputData);
	template<class F> static void TestEntitiesWithLOSFunctor(const Asura_LOS_InputData* pxInputData, F& xFunctor );

	// MGH 26/9/02: This function is only to be used when creating client side objects
	// in tools or (in very rare cases) loading client side objects from chunkbuffers.
	// Usually client objects are created from messages, in which case this should not be called.
	static void SetNextClientGuid(Asura_Guid uGuid) { NextClientGuid=uGuid; }

	// These are guaranteed to return a valid pointer, as they reference static data
	// If this changes, any calls will have to ensure that they check the pointer before using it
#if !defined(ASURA_USE_STREAMING)
	static Asura_Entity_PeriodicClientUpdateList* GetPeriodicClientUpdateList() { return &s_xPeriodicClientUpdateList; }
	static Asura_Hashed_List<Asura_Entity_Server>* GetConstantClientUpdateList() { return &s_xConstantClientUpdateList; }
#endif //!defined(ASURA_USE_STREAMING)

	// Prints some statistics about entities to a log-file, for profiling purposes.
	static void LogEntities();

	// Functions for marking the time during which entity destruction should be performed...
	static bool IsDestroyingAllEntities( void )	{ return s_bCurrentlyDestroyingAllEntities; }
	static void BeginEntityDestruction( void );
	static void EndEntityDestruction( void );

	// Returns the number of server entities having ::Update() called on them per frame
	static int GetNumClientEntitiesUpdating();
	static int GetNumServerEntitiesUpdating();
	static int GetNumRenderableEntities();

#ifdef ASURA_TOOLS
	static bool LogUpdatingEntityReport( void );
#endif

	// Helper function for requesting client entity creation messages, as it's almost always the same message that needs creating.
	static void SendRequestCreateMessage(const Asura_Guid uEntityGuid, const u_short usEntityClassification);

private:

    // For access to AddToDestroyList().
    friend class Asura_Entity;

	// For access to GetLineOfSightBVH().
	friend class Asura_LOS_Penetration;

	static Asura_DynamicBVH<Asura_Entity_Renderable>& GetLineOfSightBVH();
	static void RefreshLineOfSightBVH();

	static bool ClientCreateEntity(const Asura_Message* pxMessage);
	static bool Project_ClientCreateEntity(const Asura_Message* pxMessage);

	// (MH - 01.09.2003) if a message arrives which would destroy an entity just
	// before a message which would create that entity, we do not want to ignore
	// the destruction message; it must be stored so that the creation does not 
	// happen. This might well happen if messages arrive out-of-order in an
	// internet game. Ideally, the only message which results in destruction 
	// would be ASURA_MSG_DESTRUCT and that message would be sent guaranteed.
	// Other messages which include destruction as a side-effect must remember
	// to follow this point.
	static void UnhandledDestroyMessage(Asura_Guid uGuid);

#ifdef ASURA_SUPPORT_NETMSG_SYSTEM
	static bool Project_RegisterUpdateMessage(u_short usMsgID, u_short usEntityClassification);
	static float Project_GetUpdateHeartbeat(u_short usMsgID, u_short usEntityClassification);
#endif

    static void AddToDestroyList( Asura_Entity* pxEntity );
	static void RemoveFromDestroyList(Asura_Entity* pxEntity);
    static void DestroyAllEntities( void );
    static void FlushDestroyList( void );

	// NY - 04 January 2008 - check if this entity should have a client created due to s_bUpdateInactiveClientEntitiesAtEndOfFrame
	static bool ShouldCreateInactiveClientEntity(const Asura_Entity* pxEntity);

	// Internal update functions...
	template <class T> static void	UpdateEntities(Asura_Classified_List<T>& xEntityList);

#if defined(ASURA_DEBUG_VARIABLE_SYSTEM)
	template <class T> static u_int DeactivateDistantEntities(const Asura_Vector_3& xCameraPos, const float fDistSquared, const Asura_Hashed_List<T>* pxList);
	static bool ConsoleCmd_DeactivateDistantEntities( bool bHelp );
#if defined(ASURA_TOOLS)
	static bool ConsoleCmd_PrintUpdatingEntityReport( bool bHelp );
#endif	//defined(ASURA_TOOLS)
#if defined(ASURA_USE_PROFILING_SYSTEM)
	static void LogProfileData(const Asura_Char* szTitle, const Asura_Entity_System_ProfileData& xProfileData);
	static bool ConsoleCmd_LogEntityProfiling( bool bHelp );
#endif	//defined(ASURA_USE_PROFILING_SYSTEM)
#endif	//defined(ASURA_DEBUG_VARIABLE_SYSTEM)

#if defined(ASURA_TOOLS)
	static Asura_Guid s_auNextToolsCreatedGuid[TOOLS_NUM_GUIDRANGE];
#endif	// defined(ASURA_TOOLS)
	static bool s_bAssignGuidsFromCutsceneRange;
	static Asura_Guid NextGameCreatedGuid;
	static Asura_Guid NextClientGuid;

#if !defined(ASURA_USE_STREAMING)
	static Asura_Entity_PeriodicClientUpdateList  s_xPeriodicClientUpdateList;
	static Asura_Hashed_List<Asura_Entity_Server> s_xConstantClientUpdateList;
#endif //!defined(ASURA_USE_STREAMING)

#if defined(ASURA_USE_PROFILING_SYSTEM) && defined(ASURA_DEBUG_VARIABLE_SYSTEM)
	static u_int							s_uNumRenderableEntities;
	static u_int							s_auNumRenderableEntitiesPerClassification[ASURA_MAX_ENTITY_PROFILE_CLASSIFICATIONS];

	static Asura_Entity_System_ProfileData	s_xClientEntityProfileData;
	static Asura_Entity_System_ProfileData	s_xServerEntityProfileData;
#endif	//defined(ASURA_USE_PROFILING_SYSTEM) && defined(ASURA_DEBUG_VARIABLE_SYSTEM)

	static bool s_bUpdateInactiveClientEntitiesAtEndOfFrame;
	static bool	s_bCurrentlyDestroyingAllEntities;

	static Asura_DynamicBVH<Asura_Entity_Renderable> s_xLineOfSightBVH;

	static ProjectDeliverMessageToServerEntityFunction* s_pfnProjectDeliverMessageToServerEntity;
};

template<class F> 
void Asura_Entity_System::TestEntitiesWithLOSFunctor(const Asura_LOS_InputData* pxInputData, F& xFunctor )
{
	if(!pxInputData)
	{
		Asura_Assert(false, "LOS, input data is null");
		return;
	}

	Asura_Assert( Asura_Network::IsClient(), "Only valid to test LOS with Functor on clients" );
	if ( Asura_Network::IsClient() )
	{
		RefreshLineOfSightBVH();

		s_xLineOfSightBVH.TestRay_Functor( *pxInputData, xFunctor );
	}
}

class Asura_RecentlyDestroyedEntity;

class Asura_RecentlyDestroyedEntity_System
{
	friend class Asura_RecentlyDestroyedEntity;

public:

	// static functions
	static void UpdateLists(void);
	static void Reset(void);
	static void Shutdown(void) { Reset(); }
	static bool HasRecentlyBeenDestroyed(Asura_Guid uGuid);
	static void AddDestroyedEntity(Asura_Guid uGuid);

private:

	static void EmptyList(u_int uListID);

	// static data
	static Asura_Hashed_List<Asura_RecentlyDestroyedEntity> s_xList[2];
	static float s_fTimer;
	static u_int s_uCurList;
};	

class Asura_RecentlyDestroyedEntity
{
public:

	Asura_RecentlyDestroyedEntity(Asura_Guid uGuid)
		: m_uGuid(uGuid)
	{
		Asura_RecentlyDestroyedEntity_System::s_xList[Asura_RecentlyDestroyedEntity_System::s_uCurList].AddToList(this);
	}

	~Asura_RecentlyDestroyedEntity()
	{
		Asura_RecentlyDestroyedEntity_System::s_xList[Asura_RecentlyDestroyedEntity_System::s_uCurList].RemoveFromList(this);
	}

	Asura_Guid GetGuid() { return m_uGuid; }
	bool IsDestroyed() { return false; }		// hmm, this is unused.

private:

	Asura_Guid m_uGuid;

};

#endif//__ASURA_ENTITY_SYSTEM_H__
