
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_Actor_InteractionHandler.cpp
// Author : Andy Tate
// Date   : 4-Sep-2007
//
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header...
#include "Actor/AVP_Actor_InteractionHandler.h"

// Asura independent header-files...
#include "Debugging/Asura_DebugLineRender.h"
#include "Interface/Asura_CommandConsole.h"
#include "Network/Asura_Network.h"

// Project independent header-files...
#include "Actor/AVP_Actor_InputHandler.h"
#include "Actor/Vertebrates/Alien/AVP_Alien_InteractionHandler.h"
#include "Actor/Vertebrates/Human/AVP_Human_InteractionHandler.h"
#include "Actor/Vertebrates/Predator/AVP_Predator_InteractionHandler.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Core/AVP_Game_Core.h"
#include "Core/AVP_Maths.h"
#include "Entity/AVP_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_Actor_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Entity/PhysicalObject/Interact/AVP_ClientEntity_InteractObject.h"
#include "Entity/Triggers/AVP_ClientEntity_InteractionTrigger.h"
#include "Inventory/AVP_Inventory_Item.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Moves/AVP_Movement_System.h"
#include "Moves/AVP_Moves.h"
#include "Moves/AVP_Move_Interaction.h"

////////////////////////////////////////////////////////////////////////////////
// Constant declarations...
////////////////////////////////////////////////////////////////////////////////

bool AVP_Actor_InteractionHandler::s_bRenderInteractionPrompt = false;

#ifndef ASURA_NO_DEBUG_OUTPUT
DVSVAR bool dvs_bRenderDebugInfo = false;
#endif

////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////

AVP_Actor_InteractionHandler::AVP_Actor_InteractionHandler(AVP_ClientEntity_Actor& xOwner)
: m_xOwner(xOwner)
, m_uInteractionGuid(ASURA_GUID_UNREGISTERED)
{
}

AVP_Actor_InteractionHandler::~AVP_Actor_InteractionHandler()
{
}

#if defined(ASURA_SUPPORT_COMMAND_CONSOLE)
void AVP_Actor_InteractionHandler::CreateConsoleVars()
{
	Asura_CommandConsole::AddVar(L"AVP.Interaction.ShowPrompt", &s_bRenderInteractionPrompt, Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
	Asura_CommandConsole::MakeVarReadOnly(L"AVP.Interaction.ShowPrompt");
#ifndef ASURA_NO_DEBUG_OUTPUT
#ifdef ASURA_DEBUG_VARIABLE_SYSTEM
	Asura_CommandConsole::AddVar(L"AVP.Interaction.RenderDebugInfo", &dvs_bRenderDebugInfo, Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
#endif //ASURA_DEBUG_VARIABLE_SYSTEM
#endif
}
#endif	// defined(ASURA_SUPPORT_COMMAND_CONSOLE)

bool AVP_Actor_InteractionHandler::IsEntityInteracting(const Asura_Guid uGuid)
{
	const AVP_ClientEntity_Actor* pxActor = AVP_ClientEntity_Actor::FindEntity(uGuid);
	if (!pxActor) { return false; }

	const Asura_Movement_System* pxMovementSystem = pxActor->GetMovementSystem();
	const Asura_Move* pxCurrentMove = pxMovementSystem ? pxMovementSystem->GetCurrentMove() : NULL;
	if (!pxCurrentMove) { return false; }

	const Asura_Hash_ID uMoveHash = pxCurrentMove->GetHash();

	// Since we can't guarantee the existence of an interaction handler on this machine, use the static functions based on classification.
	switch (pxActor->GetClassification())
	{
	default:
		return false;

	case AVP_ENTITYCLASS_ALIEN:
		return AVP_Alien_InteractionHandler::GetIsAlienInteractionMove(uMoveHash);

	case AVP_ENTITYCLASS_ANDROID:
	case AVP_ENTITYCLASS_CIVILIAN:
	case AVP_ENTITYCLASS_MARINE:
		return AVP_Human_InteractionHandler::GetIsHumanInteractionMove(uMoveHash);

	case AVP_ENTITYCLASS_PREDATOR:
		return AVP_Predator_InteractionHandler::GetIsPredatorInteractionMove(uMoveHash);
	}
}

bool AVP_Actor_InteractionHandler::HandleInteraction(AVP_INTERACTION_TYPES eType, int iKeyType, float fKeyValue) const
{
	const Asura_Guid uGuidForInteraction = GetGuidForInteraction( eType );
	if ( uGuidForInteraction != ASURA_GUID_UNREGISTERED )
	{
		if ( uGuidForInteraction != ASURA_GUID_UNREGISTERED )
		{
			// This bit is common to all interactions, including those handled in derived classes		
			// Tell the server we want to interact... this will kick off the interaction on all the other client machines
			Asura_Message* pxMessage = Asura_Network::CreateMessage( AVP_MSGID_REQUEST_INTERACTION, sizeof( AVP_Message_Actor_RequestInteraction ) );
			if ( pxMessage != NULL )
			{
				AVP_Message_Actor_RequestInteraction* pxData = static_cast< AVP_Message_Actor_RequestInteraction* >( pxMessage->GetData() );
				AVP_ClientEntity_InteractObject* pxInteractObject = AVP_ClientEntity_InteractObject::FindEntity( uGuidForInteraction );

				// Fill-in the message...
				pxData->SetInteractionType( eType );
				pxData->SetActorGuid( m_xOwner.GetGuid() );
				pxData->SetIsOn( pxInteractObject ? pxInteractObject->IsOn() : false );
				pxData->SetInteractionMoveType(AVP_Message_Actor_RequestInteraction::INTERACTION_INTIATED);
				// Send the request...
				pxMessage->SetToGuid( uGuidForInteraction );
				pxMessage->SetFromGuid( m_xOwner.GetGuid() );

				Asura_Network::Send( pxMessage );
			}	
			return true;    // There was an entity to intract with, so return true
		}
	}
	return false;
}

void AVP_Actor_InteractionHandler::Update()
{

	// If player is already interacting, don't update until he ends the interaction
	if (IsInteracting())
	{
		if ( AVP_Game_Core::IsLocalPlayer( m_xOwner.GetGuid() ) )
		{
			s_bRenderInteractionPrompt = false;
		}
		return;
	}

	m_uInteractionGuid			= ASURA_GUID_UNREGISTERED;

	Asura_Hashed_List_It<AVP_ClientEntity_InteractObject> xIterator( AVP_ClientEntity_InteractObject::GetList() );

	// Check ALL of these entities...
	while(!xIterator.Done())
	{
		AVP_ClientEntity_InteractObject* pxInteractObject = xIterator.GetCurrent();
		xIterator.Next();

		if (    pxInteractObject
			 && pxInteractObject->IsEntityActive() 
			 && pxInteractObject->IsEnabled() 
			 && pxInteractObject->WillInteractWith( m_xOwner.GetClassification() )
			 && pxInteractObject->IsActorUsingRightInteractionItem(m_xOwner) )
		{
			pxInteractObject->UpdatePFXs();

			const Asura_OrientedVolume* pxActorVol = pxInteractObject->GetActorVolume();
			if ( pxActorVol == NULL || pxActorVol->Contains( m_xOwner.GetCurrentPosition() ) )
			{
#ifndef ASURA_NO_DEBUG_OUTPUT
				if ( pxActorVol && dvs_bRenderDebugInfo )
				{
					Asura_Vector_3 xCentre( pxActorVol->CalculateCentre() );
					Asura_Bounding_Box xBox( pxActorVol->m_xBox );
					xBox.Displace( -xCentre );
					Asura_DebugLineRender::AddBoundingBox( xBox, xCentre, pxActorVol->m_xOrient, 0xFF00FF00 );
				}
#endif
				const Asura_OBB* pxTargetVol = pxInteractObject->GetTargetVolume();
				Asura_Vector_3 xFwd;
				Asura_Camera::GetForwardVector( xFwd );
				if ( pxTargetVol == NULL || AVP_Maths::RayIntersectsOBB( Asura_AVector3( *Asura_Camera::GetPosition() ), Asura_AVector3( xFwd ), *pxTargetVol ) )
				{
#ifndef ASURA_NO_DEBUG_OUTPUT
					if ( pxTargetVol && dvs_bRenderDebugInfo )
					{
						Asura_Bounding_Box xBB;
						xBB.Reset();
						Asura_Vector_3 xCentre( pxTargetVol->GetCentre() );
						xBB.Set( Asura_Vector_3( pxTargetVol->GetHalfExtents() ) );
						xBB.Set( -Asura_Vector_3( pxTargetVol->GetHalfExtents() ) );
						Asura_Quat xOri;
						xOri.FromMatrix( pxTargetVol->GetOrientation() );
						Asura_DebugLineRender::AddBoundingBox( xBB, xCentre, xOri, 0xFF00FF00 );
					}
#endif	
					m_uInteractionGuid = pxInteractObject->GetGuid();
				}
#ifndef ASURA_NO_DEBUG_OUTPUT
				else if ( pxTargetVol && dvs_bRenderDebugInfo )
				{
					Asura_Bounding_Box xBB;
					xBB.Reset();
					Asura_Vector_3 xCentre( pxTargetVol->GetCentre() );
					xBB.Set( Asura_Vector_3( pxTargetVol->GetHalfExtents() ) );
					xBB.Set( -Asura_Vector_3( pxTargetVol->GetHalfExtents() ) );
					Asura_Quat xOri;
					xOri.FromMatrix( pxTargetVol->GetOrientation() );
					Asura_DebugLineRender::AddBoundingBox( xBB, xCentre, xOri, 0xFFFF0000 );
				}
#endif
			}
#ifndef ASURA_NO_DEBUG_OUTPUT
			else if ( pxActorVol && dvs_bRenderDebugInfo )
			{
				Asura_Vector_3 xCentre( pxActorVol->CalculateCentre() );
				Asura_Bounding_Box xBox( pxActorVol->m_xBox );
				xBox.Displace( -xCentre );
				Asura_DebugLineRender::AddBoundingBox( xBox, xCentre, pxActorVol->m_xOrient, 0xFFFF0000 );
			}
#endif
		}
	}

	if ( AVP_Game_Core::IsLocalPlayer( m_xOwner.GetGuid() ) )
	{
		s_bRenderInteractionPrompt = (m_uInteractionGuid != ASURA_GUID_UNREGISTERED);
	}
}

bool AVP_Actor_InteractionHandler::IsInteractionAllowed() const
{
	const AVP_Movement_System*	pxMovementSystem	= static_cast<const AVP_Movement_System*>(m_xOwner.GetMovementSystem());
	const AVP_Move*				pxCurrentMove		= (pxMovementSystem == NULL) ? NULL : static_cast<const AVP_Move*>(pxMovementSystem->GetCurrentMove());

	const AVP_Inventory_System* pxInvSys = m_xOwner.GetInventorySystem();
	const AVP_Inventory_Item* pxStim = pxInvSys ? pxInvSys->GetItem(uAVP_BLUEPRINT_PARAM_STIM) : NULL;
	const bool bStimIdling = pxStim ? pxStim->IsIdling(true) : true;

	return pxCurrentMove == NULL && bStimIdling;
}

bool AVP_Actor_InteractionHandler::SendInteractMessage(AVP_INTERACTION_TYPES eType, const Asura_Hash_ID uInteractionMoveHash,  const AVP_ClientEntity_InteractObject* pxInteractObject, int iLockInteractType) const
{
	if (uInteractionMoveHash != ASURA_HASH_ID_UNSET  && pxInteractObject)
	{
		Asura_Message* pxMessage = Asura_Network::CreateMessage( AVP_MSGID_REQUEST_INTERACTION, sizeof( AVP_Message_Actor_RequestInteraction ) );
		if ( pxMessage )
		{
			AVP_Message_Actor_RequestInteraction* pxData = static_cast< AVP_Message_Actor_RequestInteraction* >( pxMessage->GetData() );

			// Fill-in the message...
			pxData->SetIsOn( pxInteractObject->IsOn());
			pxData->SetInteractionType( eType );
			pxData->SetActorGuid( m_xOwner.GetGuid() );
			pxData->SetMoveHash( uInteractionMoveHash );
			pxData->SetInteractionMoveType(iLockInteractType);
			// Send the request...
			pxMessage->SetToGuid( pxInteractObject->GetGuid() );
			pxMessage->SetFromGuid( m_xOwner.GetGuid() );

			Asura_Network::Send( pxMessage );
		}	
		return true;    // There was an entity to interact with, so return true
	}
	else
	{
		Asura_Assert(false, "AVP_Predator_InteractionHandler: Unbeleivable!");
		return false;
	}
}

bool AVP_Actor_InteractionHandler::IsInIdleInteraction() const
{
	return true;
}

bool AVP_Actor_InteractionHandler::IsInteracting() const
{
	return false;
}
/// End of file. ///
