//////////////////////////////////////////////////////////////////////////////
//
// Asura_Project_Entity.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// Engine independent.
#include "Entity/Asura_Entity.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"

// Project independent.
#include "Core/AVP_Game_Core.h"
#include "Core/AVP_Settings.h"
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/AVP_Messages.h"
#include "Entity/Damage/AVP_ClientEntity_AcidDamage.h"
#include "Entity/ObjectiveSystem/PlayerObjective/AVP_ClientEntity_PlayerObjective.h"
#include "Entity/PhysicalObject/Actor/Vehicles/Dropship/AVP_ClientVehicle_Dropship.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Alien/AVP_ClientActor_Alien.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AlienQueen/AVP_ClientActor_AlienQueen.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/FaceHugger/AVP_ClientActor_FaceHugger.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/Android/AVP_ClientActor_Android.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/Civilian/AVP_ClientActor_Civilian.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/AVP_ClientActor_Human.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/Marine/AVP_ClientActor_Marine.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Alien/AVP_Messages_Alien.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/AVP_Messages_Human.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ClientActor_Predator.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_Messages_Predator.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predalien/AVP_ClientActor_Predalien.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predalien/AVP_Messages_Predalien.h"
#include "Entity/PhysicalObject/Actor/Weapons/Sentrygun/AVP_ClientWeapon_Sentrygun.h"
#include "Entity/PhysicalObject/CommandPost/AVP_ClientEntity_CommandPost.h"
#include "Entity/PhysicalObject/CommandPost/AVP_ServerEntity_CommandPost.h"
#include "Entity/PhysicalObject/Gib/AVP_ClientEntity_Gib.h"
#include "Entity/PhysicalObject/Interact/AVP_ClientEntity_InteractObject.h"
#include "Entity/PhysicalObject/Interact/InvisiSwitch/AVP_ClientEntity_InteractInvisiSwitch.h"
#include "Entity/PhysicalObject/Interact/ToolReceptacle/BatteryReceptacle/AVP_ClientEntity_InteractBatteryReceptacle.h"
#include "Entity/PhysicalObject/Interact/ToolReceptacle/HackingPoint/AVP_ClientEntity_InteractHackingPoint.h"
#include "Entity/PhysicalObject/Interact/ToolReceptacle/RetinalScanner/AVP_ClientEntity_InteractRetinalScanner.h"
#include "Entity/PhysicalObject/Interact/PredatorLock/AVP_ClientEntity_InteractPredatorLock.h"
#include "Entity/PhysicalObject/Lift/AVP_ClientEntity_Lift.h"
#include "Entity/PhysicalObject/PhysicsObject/DamageableObject/AVP_ClientEntity_DamageableObject.h"
#include "Entity/PhysicalObject/PhysicsObject/DamageableObject/AVP_DamageableObject_Messages.h"
#include "Entity/PhysicalObject/PhysicsObject/AVP_ClientEntity_PhysicsObject.h"
#include "Entity/PhysicalObject/PhysicsObject/AVP_PhysicsObject_Messages.h"
#include "Entity/PhysicalObject/Pickup/AVP_ClientEntity_Pickup.h"
#include "Entity/PhysicalObject/Pickup/AVP_Pickup_Messages.h"
#include "Entity/PhysicalObject/Projectile/AVP_ClientProjectile_AcidSpit.h"
#include "Entity/PhysicalObject/Projectile/AVP_ClientProjectile_Disc.h"
#include "Entity/PhysicalObject/Projectile/AVP_ClientProjectile_Mine.h"
#include "Entity/PhysicalObject/Projectile/AVP_ClientProjectile_DropshipMissile.h"
#include "Entity/PhysicalObject/Projectile/AVP_ClientProjectile_GenericProjectile.h"
#include "Entity/PhysicalObject/Projectile/AVP_ClientProjectile_HumanFlare.h"
#include "Entity/PhysicalObject/Projectile/AVP_ClientProjectile_Flamethrower.h"
#include "Entity/PhysicalObject/Projectile/AVP_Projectile_Messages.h"
#include "Entity/PlayerInfo/AVP_ClientEntity_PlayerInfo.h"
#include "Entity/PlayerInfo/AVP_PlayerInfo_Messages.h"
#include "Entity/Triggers/AVP_ClientEntity_InteractionTrigger.h"
#include "Entity/Vent/AVP_ClientEntity_Vent.h"
#include "Network/AVP_Player.h"

// P R O T O T Y P E   A N D   I N L I N E   F U N C T I O N S ///////////////

static bool HandleClientEntityCreationFromUpdate( const Asura_Message* pxMsg );
static bool HandleClientEntityCreateMessage( const Asura_Message* pxMsg );
static void SendRequestForCreateMsg( const Asura_Message* pxMsg );

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

//
// Asura_Entity_System
//

bool Asura_Entity_System::Project_ClientCreateEntity( const Asura_Message* pxMsg )
{
    switch( pxMsg->GetMsgID() )
    {
        case ASURA_MSG_ACTIVATE:
	    case ASURA_MSG_DEACTIVATE:
		{
            // This is a deactivate message for something we didn't know
            // existed. There's not a lot we can do so ignore the message and
            // wait for an update.
            return(true);
		}

		case ASURA_MSG_CONTAINER_CLIENTUPDATE_EXTENDED:
		case ASURA_MSG_CONTAINER_CLIENTUPDATE_EXTENDED1:
        case ASURA_MSG_CLIENTUPDATE:
		{
            return(HandleClientEntityCreationFromUpdate(pxMsg));
		}

		case ASURA_MSG_CLIENTCREATE:
		{
			return HandleClientEntityCreateMessage(pxMsg);
		}

		// Messages which can be sent without the target being created yet, but which are safe to ignore.
		case AVP_MSGID_SETPUPPETGUID:
		case AVP_MSGID_ITEM_BEGIN_USING:
		case AVP_MSGID_ITEM_BEGIN_IDLING:
		case AVP_MSGID_DISABLE_IK_SOLVER:
		case AVP_MSGID_ENABLE_IK_SOLVER:
		case AVP_MSGID_PLAY_DIALOGUE_EVENT:
		case AVP_MSGID_ENTITYDAMAGE_APPLY_TO_ENTITY:
		case ASURA_MSGID_TELEPORT:
		case ASURA_MSG_LINK_TO_PARENT:	// I *think* it's safe to ignore this message as the parent entity guid and markerpoint are passed down each frame in the client update message. But it might cause problems if we start using attach with physics.
		{
			return true;
		}

        default:
		{
            return(false);
		}
    }
}

#ifdef ASURA_SUPPORT_NETMSG_SYSTEM

bool Asura_Entity_System::Project_RegisterUpdateMessage( u_short usMsgID, u_short usEntityClassification )
{
	switch (usMsgID)
	{
	case ASURA_MSG_CLIENTUPDATE:
		switch (usEntityClassification)
		{
			case AsuraEntityClass_PhysicsObject:
				AVP_Message_PhysicsObject_ClientUpdate::RegisterFields( usMsgID, usEntityClassification );
				return true;

			case AVP_ENTITYCLASS_ALIEN:
				AVP_Message_AlienClientUpdate::RegisterFields(usMsgID, usEntityClassification);
				return true;

			case AVP_ENTITYCLASS_HUMAN:
			case AVP_ENTITYCLASS_CIVILIAN:	// Type of human, uses Human client update
			case AVP_ENTITYCLASS_MARINE:	// Type of human, uses Human client update
			case AVP_ENTITYCLASS_ANDROID:	// Type of human, uses Human client update
				AVP_Message_HumanClientUpdate::RegisterFields(usMsgID, usEntityClassification);
				return true;

			case AVP_ENTITYCLASS_PREDATOR:
				AVP_Message_PredatorClientUpdate::RegisterFields(usMsgID, usEntityClassification);
				return true;

			case AVP_ENTITYCLASS_PREDALIEN:
				AVP_Message_PredalienClientUpdate::RegisterFields(usMsgID, usEntityClassification);
				return true;

			case AVP_ENTITYCLASS_PLAYERINFO:
				AVP_Message_PlayerInfo_ClientUpdate::RegisterFields(usMsgID, usEntityClassification);
				return true;

			case AVP_ENTITYCLASS_DISC:
				AVP_Message_DiscClientUpdate::RegisterFields(usMsgID, usEntityClassification);
				return true;

			case AVP_ENTITYCLASS_HUMANFLARE:
			case AVP_ENTITYCLASS_GENERICPROJECTILE:
			case AVP_ENTITYCLASS_PROJECTILE_ACIDSPIT:
				AVP_Message_GenericProjectileClientUpdate::RegisterFields(usMsgID, usEntityClassification);
				return true;

			case AVP_ENTITYCLASS_PICKUP:
				AVP_Message_Pickup_ClientUpdate::RegisterFields(usMsgID, usEntityClassification);
				return true;

			case AVP_ENTITYCLASS_DAMAGEABLEOBJECT:
				AVP_Message_DamageableObject_ClientUpdate::RegisterFields(usMsgID, usEntityClassification);
				return true;

			// JG - 25 September 2008 - No need to create acid damage through updates anymore
			case AVP_ENTITYCLASS_ACID_DAMAGE:
				return false;

			case AVP_ENTITYCLASS_COMMANDPOST:
				AVP_Message_CommandPost_ClientUpdate::RegisterFields(usMsgID, usEntityClassification);
				return true;

			default:
				break;
		}
		break;

	default:
		break;
	}

    return false;
}


float Asura_Entity_System::Project_GetUpdateHeartbeat( u_short usMsgID, u_short usEntityClassification )
{
    return 0.0f;
}

#endif // #ifdef ASURA_SUPPORT_NETMSG_SYSTEM

// F U N C T I O N   I M P L E M E N T A T I O N S ///////////////////////////

static bool HandleClientEntityCreationFromUpdate( const Asura_Message* pxMsg )
{
	const u_short usClassification = pxMsg->GetEntityClassification();
	const Asura_Guid uGuid = pxMsg->GetFromGuid();

	void (*pfnCreateFromUpdate)(const Asura_Message* pxMessage) = NULL;

	// What sort of entity are we creating?
    switch(usClassification)
    {
        // Some classes want a separate message for creation instead, to
        // minimise update msg size.
		case AsuraEntityClass_Container:
		case AsuraEntityClass_PhysicsObject:
		case AVP_ENTITYCLASS_DROPSHIP:
		case AVP_ENTITYCLASS_ALIEN:
		case AVP_ENTITYCLASS_ANDROID:
		case AVP_ENTITYCLASS_CIVILIAN:
		case AVP_ENTITYCLASS_MARINE:
		case AVP_ENTITYCLASS_PREDATOR:
		case AVP_ENTITYCLASS_SENTRYGUN:
		case AVP_ENTITYCLASS_ACID_DAMAGE:
		case AVP_ENTITYCLASS_CAPTUREPOINT:
		case AVP_ENTITYCLASS_INTERACTOBJECT:
		case AVP_ENTITYCLASS_PICKUP:
		case AVP_ENTITYCLASS_PLAYER_OBJECTIVE:
		case AVP_ENTITYCLASS_PLAYERINFO:
		case AVP_ENTITYCLASS_COMMANDPOST:
		{
            SendRequestForCreateMsg( pxMsg );
			return true;
		}

        // Basic create-from-update behaviour.
		case AsuraEntityClass_Lift:
		{
			pfnCreateFromUpdate = AVP_ClientEntity_Lift::Create;
			break;
		}

		case AVP_ENTITYCLASS_INTERACTIONTRIGGER:
		{
			pfnCreateFromUpdate = AVP_ClientEntity_InteractionTrigger::Create;
			break;
		}

		case AVP_ENTITYCLASS_DISC:
		{
			pfnCreateFromUpdate = AVP_ClientProjectile_Disc::Create;
			break;
		}

		case AVP_ENTITYCLASS_MINE:
		{
			pfnCreateFromUpdate = AVP_ClientProjectile_Mine::Create;
			break;
		}

		case AVP_ENTITYCLASS_DROPSHIP_MISSILE:
		{
			pfnCreateFromUpdate = AVP_ClientProjectile_DropshipMissile::Create;
			break;
		}

		case AVP_ENTITYCLASS_PROJECTILE_FLAMETHROWER:
		{
			pfnCreateFromUpdate = AVP_ClientProjectile_Flamethrower::Create;
			break;
		}

		case AVP_ENTITYCLASS_ALIENFACEHUGGER:
		{
			pfnCreateFromUpdate = AVP_ClientActor_FaceHugger::Create;
			break;
		}

		case AVP_ENTITYCLASS_ALIENQUEEN:
		{
			pfnCreateFromUpdate = AVP_ClientActor_AlienQueen::Create;
			break;
		}

		case AVP_ENTITYCLASS_HUMANFLARE:
		{
			pfnCreateFromUpdate = AVP_ClientProjectile_HumanFlare::Create;
			break;
		}

		case AVP_ENTITYCLASS_GENERICPROJECTILE:
		{
			pfnCreateFromUpdate = AVP_ClientProjectile_GenericProjectile::Create;
			break;
		}

		case AVP_ENTITYCLASS_PROJECTILE_ACIDSPIT:
		{
			pfnCreateFromUpdate = AVP_ClientProjectile_AcidSpit::Create;
			break;
		}

        default:
		{
            return(false);
		}
    }

	// If we have a create-from-update-message function we can only use it if this is actually an update message.
	if (ASURA_MSG_CLIENTUPDATE == pxMsg->GetMsgID() || ASURA_MSG_CONTAINER_CLIENTUPDATE_EXTENDED == pxMsg->GetMsgID() || ASURA_MSG_CONTAINER_CLIENTUPDATE_EXTENDED1 ==  pxMsg->GetMsgID())
	{
		pfnCreateFromUpdate(pxMsg);

		if (AVP_Game_Core::GetReceivedInitialiseCompleteMessage()) // The end of the initialise state will handle this for all client entities.
		{
			Asura_Entity_Client* pxClient = Asura_Entity_Client::FindEntity(uGuid);

			if (pxClient)
			{
				pxClient->PostCreateInitialise();
			}
		}
	}

	return(true);
}

static bool HandleClientEntityCreateMessage( const Asura_Message* pxMsg )
{
	const u_short usClassification = pxMsg->GetEntityClassification();
	const Asura_Guid uGuid = pxMsg->GetFromGuid();

	Asura_Assert(ASURA_MSG_CLIENTCREATE == pxMsg->GetMsgID(), "");

	// What sort of entity are we creating?
	switch (usClassification)
	{
		case AsuraEntityClass_PhysicsObject:
		{
			AVP_ClientEntity_PhysicsObject::Create(pxMsg);
			break;
		}
	
		case AVP_ENTITYCLASS_DAMAGEABLEOBJECT:
		{
			AVP_ClientEntity_DamageableObject::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_ALIEN:
		{
			AVP_ClientActor_Alien::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_ANDROID:
		{
			AVP_ClientActor_Android::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_CIVILIAN:
		{
			AVP_ClientActor_Civilian::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_DROPSHIP:
		{
			AVP_ClientVehicle_Dropship::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_MARINE:
		case AVP_ENTITYCLASS_HUMAN:
		{
			AVP_ClientActor_Marine::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_PREDATOR:
		{
			AVP_ClientActor_Predator::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_PREDALIEN:
		{
			AVP_ClientActor_Predalien::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_SENTRYGUN:
		{
			AVP_ClientWeapon_Sentrygun::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_ACID_DAMAGE:
		{
			AVP_ClientEntity_AcidDamage::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_VENT:
		{
			AVP_ClientEntity_Vent::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_INTERACTOBJECT:
		{
			AVP_ClientEntity_InteractObject::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_PICKUP:
		{
			AVP_ClientEntity_Pickup::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_PLAYER_OBJECTIVE:
		{
			AVP_ClientEntity_PlayerObjective::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_PLAYERINFO:
		{
			AVP_ClientEntity_PlayerInfo::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_INTERACT_PREDATOR_LOCK:
		{
			AVP_ClientEntity_InteractPredator_Lock::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_INTERACT_BATTERY_RECEPTACLE:
		{
			AVP_ClientEntity_InteractBatteryReceptacle::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_INTERACT_HACKING_POINT:
		{
			AVP_ClientEntity_InteractHackingPoint::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_INTERACT_SENTRYGUN_INVISISWITCH:
		{
			AVP_ClientEntity_InteractInvisiSwitch::Create( pxMsg );
			break;
		}

		case AVP_ENTITYCLASS_INTERACT_RETINAL_SCANNER:
		{
			AVP_ClientEntity_InteractRetinalScanner::Create(pxMsg);
			break;
		}

		case AVP_ENTITYCLASS_COMMANDPOST:
			AVP_ClientEntity_CommandPost::Create(pxMsg); 
			break; 

		default:
		{
			return(false);
		}
	}

	if (AVP_Game_Core::GetReceivedInitialiseCompleteMessage()) // The end of the initialise state will handle this for all client entities.
	{
		Asura_Entity_Client* pxClient = Asura_Entity_Client::FindEntity(uGuid);

		if (pxClient)
		{
			pxClient->PostCreateInitialise();
		}
	}

	return(true);
}


static void SendRequestForCreateMsg( const Asura_Message* pxMsg )
{
	// We're probably going to get client create messages from anything that would be using this anyway, don't bother for now.
	if (!AVP_Game_Core::GetReceivedInitialiseCompleteMessage()) { return; }

	Asura_Entity_System::SendRequestCreateMessage(pxMsg->GetFromGuid(), pxMsg->GetEntityClassification());
}

//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////
