//////////////////////////////////////////////////////////////////////////////
//
// AVP_Sense_Hearing.cpp
//
// Created by NJD - 06 December 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

// Core
#include "Core/Asura.h"

// This files header
#include "Knowledge/SenseTypes/AVP_Sense_Hearing_Actor.h"

// Asura
#include "Blueprint/Asura_Blueprint.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"

// Axon
#include "Core/BrainTypes/Axon_Project_Brain.h"
#include "Knowledge/AVP_Knowledge_System.h"
#include "Tactical/AVP_Tactical_System.h"
#include "Knowledge/Axon_Knowledge.h"
#include "Knowledge/SenseTypes/AVP_Sense_Vision_Actor.h"

// AVP
#include "Entity/PhysicalObject/Actor/Vertebrates/Human/AVP_ServerActor_Human.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/Predator/AVP_ServerActor_Predator.h"
#include "Entity/AVP_Entity_Classification.h"
#include "Core/BrainTypes/Axon_Project_Brain.h"

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Sense_Hearing
//
//////////////////////////////////////////////////////////////////////////////

//---------------------------------------------------------------------------
// AVP_Sense_Hearing :: Constructor
//---------------------------------------------------------------------------
AVP_Sense_Hearing::AVP_Sense_Hearing( AVP_Knowledge_System_Actor* pxAVPKnowledgeSystem ) 
: PARENT( pxAVPKnowledgeSystem )
, m_pxActor( AVP_ServerEntity_Actor::FindEntity( GetAVPKnowledgeSystem()->GetBrain()->GetOwnerGuid() ) )
{
    m_fSensitivity = 10.0f;
}

//---------------------------------------------------------------------------
// AVP_Sense_Hearing :: Destructor
//---------------------------------------------------------------------------
AVP_Sense_Hearing::~AVP_Sense_Hearing() 
{
	DestroyAllKnowledge();
}

//---------------------------------------------------------------------------
// AVP_Sense_Hearing :: InitialiseFromBlueprint
//---------------------------------------------------------------------------
void AVP_Sense_Hearing::InitialiseFromBlueprint( const Asura_Blueprint& xBlueprint )
{
	PARENT::InitialiseFromBlueprint( xBlueprint );

	// We also need to initialise from our AI settings blueprint currently.
	InitFromSettingsBlueprint();
}

//---------------------------------------------------------------------------
// AVP_Sense_Hearing :: ReadFromChunkStream
//---------------------------------------------------------------------------
void AVP_Sense_Hearing::ReadFromChunkStream( Asura_Chunk_Stream& xStream ) 
{
    int iVersion;
    xStream >> iVersion;

    PARENT::ReadFromChunkStream( xStream );

    Asura_Assertf(iVersion == CURRENT_CHUNK_VERSION, ("AVP_Sense_Hearing::ReadFromChunkStream - unhandled chunk version (%d)", iVersion));
}

//---------------------------------------------------------------------------
// AVP_Sense_Hearing :: WriteToChunkStream
//---------------------------------------------------------------------------
void AVP_Sense_Hearing::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
    xStream << CURRENT_CHUNK_VERSION;

    PARENT::WriteToChunkStream(xStream);
}

//---------------------------------------------------------------------------
// AVP_Sense_Hearing :: Update
//---------------------------------------------------------------------------
void AVP_Sense_Hearing::Update(const float fTimeslice)
{
	m_xPosition    = m_pxActor->GetPosition();
	m_xDirection.x = m_pxActor->GetOrientation().e13;
	m_xDirection.y = m_pxActor->GetOrientation().e23;
	m_xDirection.z = m_pxActor->GetOrientation().e33;

	PARENT::Update(fTimeslice);

	// Go through and only keep the most important sound from each guid
	Asura_List_It<Axon_Knowledge> xHearingIter(&m_xSoundList);
	while (!xHearingIter.Done())
	{
		Axon_Knowledge* pxKnowledge = xHearingIter.GetCurrent();
		const AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity(pxKnowledge->GetGuid());

		if ( !pxActor )
		{
			xHearingIter.DeleteCurrent();
			m_pxKnowledgeSystem->DestroyKnowledge(pxKnowledge);
			continue;
		}

		if ( !pxActor->IsEntityActive() )
		{
			xHearingIter.DeleteCurrent();
			m_pxKnowledgeSystem->DestroyKnowledge(pxKnowledge);
			continue;
		}		

		GetAVPKnowledgeSystem()->ResetAlertTimer();

		xHearingIter.Next();
	}
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision :: AddToVisionList
//---------------------------------------------------------------------------
void AVP_Sense_Hearing::AddToHearingList(Axon_Knowledge* pxNewKnowledge)
{
	if (pxNewKnowledge)
	{
		m_xSoundList.AddToList(pxNewKnowledge);
	}
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision :: AddToVisionList
//---------------------------------------------------------------------------
void AVP_Sense_Hearing::DeleteFromHearingList(const Axon_Knowledge* pxKnowledgeToRemove)
{
	Asura_List_It<Axon_Knowledge> xHearingIter(&m_xSoundList);
	while (!xHearingIter.Done())
	{
		Axon_Knowledge* pxKnowledge = xHearingIter.GetCurrent();
		
		if (pxKnowledge->GetGuid() == pxKnowledgeToRemove->GetGuid())
		{
			m_xSoundList.RemoveFromList(pxKnowledge);
			m_pxKnowledgeSystem->DestroyKnowledge(pxKnowledge);
			break;
		}

		xHearingIter.Next();
	}
}

//---------------------------------------------------------------------------
// AVP_Sense_Hearing :: GetAVPKnowledgeSystem
//---------------------------------------------------------------------------
AVP_Knowledge_System_Actor* AVP_Sense_Hearing::GetAVPKnowledgeSystem()
{
    return static_cast<AVP_Knowledge_System_Actor*>(m_pxKnowledgeSystem);
}

//---------------------------------------------------------------------------
// AVP_Sense_Hearing :: ShouldCullOnSound
//---------------------------------------------------------------------------
bool AVP_Sense_Hearing::ShouldCullSound( const Axon_Sound_Registry::SoundEntry* pxEntry ) 
{
	if ( PARENT::ShouldCullSound( pxEntry ) )
	{
		return true;
	}

	AVP_ServerEntity_Actor* pxOurActor = m_pxActor;
	Asura_Vector_3			xOurPos = pxOurActor->GetPosition();

	float fDistanceSq		= ( xOurPos - pxEntry->m_xPosition ).MagnitudeSquared();
	float fSoundDistanceSq	= pxEntry->m_fRange * pxEntry->m_fRange;

	if ( fDistanceSq > fSoundDistanceSq )
	{
		return true;
	}

	if (IsFriendlySoundOfTypeToIgnore( pxEntry ) )
	{
		return true;
	}

	if ( ShouldCullSoundTypeFromAlertLevel( pxEntry, pxEntry->m_uSourceGuid ) )
	{
		return true;
	}


	if ( pxEntry->m_uSourceGuid != ASURA_GUID_UNREGISTERED )
	{
		if ( GetAVPKnowledgeSystem()->GetTargetPriority( pxEntry->m_uSourceGuid ) == AVP_AI_TargetPriorityData::IGNORE_P )
		{
			return true;
		}
	}

	if ( pxEntry->m_uClassification != pxOurActor->GetClassification() && pxEntry->m_uSoundType != AXON_SOUND_TYPE_GUNFIRE && 
		pxEntry->m_uSoundType != AXON_SOUND_TYPE_RICOCHET && pxEntry->m_uSoundType != AXON_SOUND_TYPE_EXPLOSION)
	{
		if ( GetAVPKnowledgeSystem()->GetAlertLevel() == AVP_Knowledge_System_Actor::PASSIVE_ALERTNESS )
		{
			GetAVPKnowledgeSystem()->SetAlertLevel( AVP_Knowledge_System_Actor::ACTIVE_ALERTNESS );
		}

		GetAVPKnowledgeSystem()->ResetAlertTimer();
	}

	return false;
}

//---------------------------------------------------------------------------
// AVP_Sense_Hearing :: RefreshBlueprintData
//---------------------------------------------------------------------------
void AVP_Sense_Hearing::InitFromSettingsBlueprint()
{
	AVP_ServerEntity_Actor*	pxOurActor				= AVP_ServerEntity_Actor::FindEntity(GetAVPKnowledgeSystem()->GetBrain()->GetOwnerGuid());
	Asura_Hash_ID			uBlueprintTypeHashID	= 0x7AD7BD1B; //Asura_GetHashID("AI Settings");
	Asura_Hash_ID			uBlueprintHashID		= ASURA_HASH_ID_UNSET;

	if ( !pxOurActor )
	{
		Asura_Assertf( false, ( "AVP_Sense_Hearing::InitFromSettingsBlueprint - this shouldn't be possible. GUID = %u", GetAVPKnowledgeSystem()->GetBrain()->GetOwnerGuid() ) );
		return;
	}

	switch (pxOurActor->GetClassification())
	{
		case AVP_ENTITYCLASS_ANDROID:			uBlueprintHashID = 0xb66dfeef; break; // "AndroidKnowledge"
		case AVP_ENTITYCLASS_CIVILIAN:			uBlueprintHashID = 0x4b5ad85b; break; // "CivilianKnowledge"
		case AVP_ENTITYCLASS_MARINE:			uBlueprintHashID = 0xed4b34fc; break; // "MarineKnowledge"
		case AVP_ENTITYCLASS_PREDATOR:			uBlueprintHashID = 0xfbdd4b67; break; // "PredatorKnowledge"
		case AVP_ENTITYCLASS_SENTRYGUN:			uBlueprintHashID = 0x8e47c09d; break; // "SentryGunKnowledge"
		default: Asura_Assert(false, "AVP_Sense_Hearing :: InitFromSettingsBlueprint - Invalid Actor Type using default values for knowledge settings");
	}

	Asura_Blueprint*		pxBlueprint				= Asura_Blueprint_System::GetBlueprintByHashID(uBlueprintTypeHashID, uBlueprintHashID);
	if (pxBlueprint)
	{
		Asura_Value xValue;

		if (pxBlueprint->GetParameterValueByHashID(0x1daea193, xValue))	// 0x1daea193; //"HearingKnowledgeLifeTime"
			m_fKowledgeLifeTime = (xValue.GetFloatValue());
	}

	// Forgot to initialise this.
	m_fSensitivity = 10.0f;
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision :: IsFriendlyFootsetpSound
//---------------------------------------------------------------------------
bool AVP_Sense_Hearing::IsFriendlyFootstepSound(const Axon_Sound_Registry::SoundEntry* pxEntry)
{
	// If this sound is coming from a friend
	const Asura_Entity_Server* pxFrom = Asura_Entity_Server::FindEntity( pxEntry->m_uSourceGuid );

	if (pxFrom && AVP_Tactical_System::GetAllegiance(*m_pxActor, *pxFrom) == AVP_ALLEGIANCE_FRIEND)
	{
		// If its footsteps, cull it. Other sounds such as screaming and shooting we want to hear
		if (pxEntry->m_uSoundType == AXON_SOUND_TYPE_FOOTSTEP)
		{
			return true;
		}
	}

	return false;
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision :: IsFriendlySoundOfTypeToIgnore
//---------------------------------------------------------------------------
bool AVP_Sense_Hearing::IsFriendlySoundOfTypeToIgnore(const Axon_Sound_Registry::SoundEntry* pxEntry)
{
	// If this sound is coming from a friend
	const Asura_Entity_Server* pxFrom = Asura_Entity_Server::FindEntity( pxEntry->m_uSourceGuid );

	if (pxFrom && AVP_Tactical_System::GetAllegiance(*m_pxActor, *pxFrom) == AVP_ALLEGIANCE_FRIEND)
	{
		// If its footsteps, cull it. Other sounds such as screaming and shooting we want to hear
		switch ( pxEntry->m_uSoundType )
		{
			case AVP_SOUND_TYPE_PRED_ROAR:
			case AVP_SOUND_TYPE_ALIEN_ROAR:
			case AVP_SOUND_TYPE_HUMAN_HURT:
			case AVP_SOUND_TYPE_MELEE_ATTACK:
			{
				return false;
			}

			case AXON_SOUND_TYPE_NO_TYPE:
			case AXON_SOUND_TYPE_FOOTSTEP:
			case AXON_SOUND_TYPE_GUNFIRE:
			case AXON_SOUND_TYPE_RICOCHET:
			case AXON_SOUND_TYPE_EXPLOSION:
			case AVP_SOUND_TYPE_JUMP_LANDING:
			default:
			{
				return true;
			}
		}
	}

	return false;
}

//---------------------------------------------------------------------------
// AVP_Sense_Vision :: ShouldCullSoundTypeFromAlertLevel
//---------------------------------------------------------------------------
bool AVP_Sense_Hearing::ShouldCullSoundTypeFromAlertLevel(const Axon_Sound_Registry::SoundEntry* pxEntry, const Asura_Guid uSourceGuid)
{
	const AVP_ServerEntity_Actor* pxActor = AVP_ServerEntity_Actor::FindEntity(uSourceGuid);

	if (!pxActor || pxActor->IsDead() || !pxActor->IsEntityActive() )
	{
		// If this cannot be found then it is a projectile
		return false;
	}

//	if ( pxActor->IsPlayer() && pxActor->GetClassification() == AVP_ENTITYCLASS_MARINE && pxEntry->m_uSoundType == AVP_SOUND_TYPE_HUMAN_HURT )
//	{
//		return true;
//	}

	if ( pxEntry->m_uSoundType != AXON_SOUND_TYPE_FOOTSTEP )
	{
		return false;
	}

	switch ( GetAVPKnowledgeSystem()->GetAlertLevel() )
	{
		case AVP_Knowledge_System_Actor::AGGRESSIVE_ALERTNESS:
		{
			return true;
		}

		case AVP_Knowledge_System_Actor::ACTIVE_ALERTNESS:
		case AVP_Knowledge_System_Actor::PASSIVE_ALERTNESS:
		default:
		{
			if ( pxEntry->m_uSoundType == AXON_SOUND_TYPE_FOOTSTEP )
			{
				return ( pxActor->IsMovingSlow() );
			}
			else
			{
				return false;
			}
		}
	}
}

