
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_Human_Focus.cpp
// Author : Chris Wallace
// Date   : 08/08/08
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

// This file's header...
#include "Actor/Vertebrates/Human/AVP_Human_Focus.h"

// Asura independent header-files...
#include "Attractors/Asura_Attractor_System.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "Camera/Asura_Camera.h"
#if !defined(ASURA_NO_DEBUG_OUTPUT)
#include "Debugging/Asura_DebugLineRender.h"
#endif	// !defined(ASURA_NO_DEBUG_OUTPUT)
#include "GUIMenu/Asura_GUIMenu_Root.h"
#include "GUIMenu/Asura_GUIMenu_Element_Image.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"
#include "Physics/Asura_Physics.h"
#include "ProjectSettings/Asura_PhysicsConfiguration.h"
#include "Rendering/Asura_Font_System.h"
#include "Rendering/Asura_Viewport.h"
#include "SpecialFX/PFX/Asura_PFX_Entity_Effect.h"
#include "SpecialFX/PFX/Asura_PFX_System.h"

// Game independent header-files...
#include "Actor/AVP_Actor_AnimationHandler.h"
#include "Actor/Vertebrates/AVP_Vertebrate_Description.h"
#include "Actor/Vertebrates/Human/AVP_Human_InputHandler.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/AVP_ClientActor_Human.h"
#include "Inventory/AVP_Inventory_HashIDs.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Inventory/Items/AVP_InventoryItem_VisionMode.h"
#include "Inventory/Items/AVP_InventoryItem_Weapon.h"
#include "Inventory/Items/Weapons/AVP_InventoryWeapon_SmartGun.h"
#include "GUIMenu/AVP_GUIMenu_System.h"

// Game axon
#include "Core/AVP_Navigation.h"
#include "Tactical/AVP_Tactical_System.h"

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

DEBUGVAR float dbg_fScreenSpaceMeleeLockFromCentreLimit = 0.25f;

static const Asura_Vector_3 xUP(0.0f, -1.0f, 0.0f);
static const Asura_Vector_3 xDOWN(0.0f, 1.0f, 0.0f);
static const Asura_Vector_3 xFORWARD(0.0f, 0.0f, 1.0f);
static const Asura_Vector_3 xSCREEN_CENTRE(0.5f, 0.5f, 0.0f);

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


////////////////////////////////////////////////////////////////////////////////
// Private functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Protected functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Human_Focus::AVP_Human_Focus
// Author       : Anthony Gregson
// Date         : 31/01/08
////////////////////////////////////////////////////////////////////////////////

AVP_Human_Focus::AVP_Human_Focus(AVP_ClientActor_Human& xOwner)
: PARENT(xOwner)
//, m_uFlags(0)
, m_fMaxLOSRange(20.0f)
{

}

/// End of function 'AVP_Human_Focus::AVP_Human_Focus'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Human_Focus::~AVP_Human_Focus
// Author       : Anthony Gregson
// Date         : 31/01/08
////////////////////////////////////////////////////////////////////////////////

AVP_Human_Focus::~AVP_Human_Focus()
{

}

/// End of function 'AVP_Human_Focus::~AVP_Human_Focus'. ///


////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Human_Focus::Update
// Author       : Anthony Gregson
// Date         : 31/01/08
////////////////////////////////////////////////////////////////////////////////

void AVP_Human_Focus::Update()
{
	// Call the parent function...
	PARENT::Update();

	const AVP_Inventory_System* pxInventory = m_xOwner.GetInventorySystem();
	const AVP_InventoryItem_Weapon* pxInvWeapon = pxInventory != NULL ? static_cast< const AVP_InventoryItem_Weapon* >( pxInventory->GetCurrentItem( uAVP_INVENTORY_GROUPHASH_WEAPON ) ) : NULL;
	if(pxInvWeapon)
	{
		if(pxInvWeapon->GetItemType() == AVP_INVENTORYITEMTYPE_SMARTGUN)
		{
			const AVP_InventoryWeapon_SmartGun* pxSmartgun = static_cast<const AVP_InventoryWeapon_SmartGun*>(pxInvWeapon);
			if ( pxSmartgun->GetTrackingMode() )
			{
				Activate();
			}
			else
			{
				Deactivate();
			}
		}
		else
		{
			Deactivate();
		}
	}

	// Are there any enemies or jump targets?
	UpdateTargets();
}

/// End of function 'AVP_Human_Focus::Update'. ///

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_Human_Focus::UpdateTargets
// Author       : Anthony Gregson
// Date         : 31/01/08
////////////////////////////////////////////////////////////////////////////////

void AVP_Human_Focus::UpdateTargets()
{
	Asura_Guid uPreviousTarget = m_uTargetEnemyGuid;
	m_uTargetEnemyGuid = ASURA_GUID_UNREGISTERED;
	m_xTargetList.Clear(); // empty old list

	Asura_Vector_3 xCameraPosition;
	Asura_Matrix_3x3 xCameraOrientation;

	// Always perform the LOS-test from the camera...
	Asura_Hash_ID uCameraHashID = AVP_Vertebrate_Description::GetMarkerPointHashID( AVP_Vertebrate_Description::MPID_EYES ); 

	if( m_xOwner.GetWorldSpaceMarkerPoint( uCameraHashID, &xCameraPosition, &xCameraOrientation) ) // Camera
	{
		const Asura_Vector_3 xCameraDirection(xCameraOrientation.e13, xCameraOrientation.e23, xCameraOrientation.e33);

		// focus lock only works with weapon with lock on.
		const AVP_Inventory_System* pxInventory = m_xOwner.GetInventorySystem();
		const AVP_InventoryItem_Weapon* pxInvWeapon = pxInventory != NULL ? static_cast< const AVP_InventoryItem_Weapon* >( pxInventory->GetCurrentItem( uAVP_INVENTORY_GROUPHASH_WEAPON ) ) : NULL;
	
		if(pxInvWeapon && pxInvWeapon->GetItemType() == AVP_INVENTORYITEMTYPE_SMARTGUN)
		{
			const AVP_InventoryWeapon_SmartGun* pxSmartgun = static_cast<const AVP_InventoryWeapon_SmartGun*>(pxInvWeapon);
			if ( pxSmartgun->GetTrackingMode() )
			{
				// Get the maximum distances from the vision mode
				Asura_Vector_2 xBestScreenPos(1.0f, 1.0f); // Given we recentre the positions, this is twice as far from the centre than any screen corner.

				// First check if our current target is still acceptable. If it is, there's no need to go through everyone.
				float fFocusRange = 1000.0f;
//				float fBestFocusTime = 1.0f;
//				float fBestFocusRange = 1000.0f;
				float fBestPriority = 0.0f;
				/*const AVP_ClientEntity_Actor* pxPrevTargetEntity = AVP_ClientEntity_Actor::FindEntity( uPreviousTarget );

				if ( pxPrevTargetEntity && !pxPrevTargetEntity->IsDead() && !pxPrevTargetEntity->IsDestroyed() && !pxPrevTargetEntity->IsInvisible() )
				{
					float fVisionMult = pxVision != NULL ? pxVision->GetVisionModeMultiplier( static_cast< AVP_ENTITY_CLASSIFICATION >( pxPrevTargetEntity->GetClassification() ) ) : 1.0f;

					fFocusRange = fVisionMult * pxInvWeapon->GetLockOnRange();
					fFocusTime = pxInvWeapon->GetLockOnTime() / fVisionMult;

					if (fFocusRange > 0.0f)
					{
						if ( GetActive() )
						{
							Asura_Vector_2 xDummy;
							if (TestEntityAgainstScreenArea(*pxPrevTargetEntity, fFocusRange, xCameraPosition, xBestScreenPos, xDummy))
							{
								fBestFocusRange = fFocusRange;
								fBestFocusTime = fFocusTime;
								m_uTargetEnemyGuid = uPreviousTarget;
								bUsePrevious = true;
							}
						}
					}
				}*/

				if ( GetActive() )
				{
					const AVP_ClientEntity_Actor* pxEntity = NULL;
					// The only real way to do this right now is to cycle through all actors.
					for (Asura_Hashed_List_ConstIt<AVP_ClientEntity_Actor> xIt(AVP_ClientEntity_Actor::GetList()); !xIt.Done(); xIt.Next())
					{
						pxEntity = xIt.GetCurrent();

						bool bIsEnemy = (AVP_Tactical_System::GetAllegiance(m_xOwner, *pxEntity) == AVP_ALLEGIANCE_ENEMY);

						if ( pxEntity && !pxEntity->IsDead() && !pxEntity->IsDestroyed() && !pxEntity->IsInvisible() && (pxEntity->GetGuid() != m_xOwner.GetGuid() ) && bIsEnemy)
						{
							fFocusRange = pxInvWeapon->GetLockOnRange();

							if (fFocusRange > 0.0f)
							{
								Asura_Vector_2 xScreenPos;
								Asura_Vector_3 xWorldspacePos;
								if (!TestEntityAgainstScreenArea(*pxEntity, fFocusRange, xCameraPosition, xBestScreenPos, xScreenPos)) { continue; }

								Focus_Target xNewTarget;

								// Do this before switching to pixel co-ords
								xNewTarget.m_fPriority = Focus_Target::CalculatePriority(xScreenPos, fFocusRange, dbg_fScreenSpaceMeleeLockFromCentreLimit, pxEntity->GetGuid(), uPreviousTarget);

								{
									xScreenPos.x += xSCREEN_CENTRE.x; // put this back...
									xScreenPos.y += xSCREEN_CENTRE.y;

									const Asura_Vector_2&	xSubPixelOffset	= Asura_Render::GetSubPixelOffset();

									xScreenPos.x	 = ((xScreenPos.x*Asura_Camera::GetViewingArea()->m_fWidth)+Asura_Camera::GetViewingArea()->m_fXOffset);
									xScreenPos.x	*= Asura_Viewport::GetWidth();
									xScreenPos.x	+= xSubPixelOffset.x;

									xScreenPos.y	 = ((xScreenPos.y*Asura_Camera::GetViewingArea()->m_fHeight)+Asura_Camera::GetViewingArea()->m_fYOffset);
									xScreenPos.y	*= Asura_Viewport::GetHeight();
									xScreenPos.y	+= xSubPixelOffset.y;
								}
								
								// Add this actor to the list
								xNewTarget.m_uGuid =  pxEntity->GetGuid();
								xNewTarget.m_xScreenPos = xScreenPos;
																
								if(xNewTarget.m_fPriority > fBestPriority)
								{
									fBestPriority = xNewTarget.m_fPriority;
							
									// New best.
//									fBestFocusTime = pxInvWeapon->GetLockOnTime();
//									fBestFocusRange = fFocusRange;
									xBestScreenPos = xScreenPos;
									m_uTargetEnemyGuid = pxEntity->GetGuid();
								}
								
								m_xTargetList.Push_Back(xNewTarget);
							}
						}
					}
				}

				m_bTargetIsLockOn = true;
			}
		}
	}
	else
	{
		Asura_Assert(false, "Cannot find camera to use when checking for targets.");
	}
}

/// End of function 'AVP_Alien_Focus::CheckForJump'. ///

bool AVP_Human_Focus::TestEntityAgainstScreenArea(const AVP_ClientEntity_Actor& xEntity, float& fLockRange, const Asura_Vector_3& xCameraPosition, const Asura_Vector_2& xPreviousBestScreenPos, Asura_Vector_2& xEntityScreenPos) const
{
	Asura_Vector_3 xChestPosition;
	Asura_Vector_3 xHeadPosition;
	xEntity.GetChestPosition(xChestPosition);
	xEntity.GetHeadPosition(xHeadPosition);
	Asura_Vector_3 xChestCameraSpacePos = xChestPosition;
	Asura_Vector_3 xHeadCameraSpacePos = xHeadPosition;
	// TODO - make sure this is the correct camera.
	Asura_Camera::ProjectWorldCoordsToScreenSpace(xChestCameraSpacePos);
	Asura_Camera::ProjectWorldCoordsToScreenSpace(xHeadCameraSpacePos);

	if (xChestCameraSpacePos.z <= 0.0f && xHeadCameraSpacePos.z < 0.0f) { return false; } // Behind the camera
	if (xChestCameraSpacePos.z > fLockRange && xHeadCameraSpacePos.z > fLockRange) { return false; } // Too far away from the camera. TODO - Possibly should replace this with a more traditional distance-from-camera-position check.

	xChestCameraSpacePos -= xSCREEN_CENTRE;
	xChestCameraSpacePos.z = 0.0f;
	xHeadCameraSpacePos -= xSCREEN_CENTRE;
	xHeadCameraSpacePos.z = 0.0f;

	// The positions may straddle the allowed area, so the test is a little more complex than normal.
	if (xChestCameraSpacePos.x < -dbg_fScreenSpaceMeleeLockFromCentreLimit && xHeadCameraSpacePos.x < -dbg_fScreenSpaceMeleeLockFromCentreLimit) { return false; }
	if (xChestCameraSpacePos.x > dbg_fScreenSpaceMeleeLockFromCentreLimit && xHeadCameraSpacePos.x > dbg_fScreenSpaceMeleeLockFromCentreLimit) { return false; }
	if (xChestCameraSpacePos.y < -dbg_fScreenSpaceMeleeLockFromCentreLimit && xHeadCameraSpacePos.y < -dbg_fScreenSpaceMeleeLockFromCentreLimit) { return false; }
	if (xChestCameraSpacePos.y > dbg_fScreenSpaceMeleeLockFromCentreLimit && xHeadCameraSpacePos.y > dbg_fScreenSpaceMeleeLockFromCentreLimit) { return false; }

	Asura_Vector_3 xWorldspacePos;
	if (xHeadCameraSpacePos.MagnitudeSquared() < xChestCameraSpacePos.MagnitudeSquared())
	{
		// Head is more central
		xEntityScreenPos.x = xHeadCameraSpacePos.x;
		xEntityScreenPos.y = xHeadCameraSpacePos.y;
		xWorldspacePos = xHeadPosition;
	}
	else
	{
		// Chest is more central
		xEntityScreenPos.x = xChestCameraSpacePos.x;
		xEntityScreenPos.y = xChestCameraSpacePos.y;
		xWorldspacePos = xChestPosition;
	}

	if (xEntityScreenPos.MagnitudeSquared() >= xPreviousBestScreenPos.MagnitudeSquared())
	{
		// Already have a more central target.
		return false;
	}

	// Finally, absolute last check, LOS test.
	Asura_Vector_3 xToEntity = xWorldspacePos - xCameraPosition;
	float fDist = xToEntity.Normalise();
	Asura_LOS_InputData xInput(xCameraPosition, xToEntity, m_xOwner.GetGuid(), fDist);
	const Asura_LOS_OutputData* pxOutput = Asura_LOS::TestAgainstEverything(&xInput);
	
	fLockRange = fDist;

	return(pxOutput->m_uHitEntity == xEntity.GetGuid());
}

void AVP_Human_Focus::PostCreateInitialise()
{

}

/// End of file. ///
