//////////////////////////////////////////////////////////////////////////////
//
// AVP_ClientWeapon_Sentrygun.cpp
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

// This file's header.
#include "Entity/PhysicalObject/Actor/Weapons/Sentrygun/AVP_ClientWeapon_Sentrygun.h"

// Engine independent.
#include "Animation/Asura_Animation.h"
#include "Animation/Asura_AnimationAttachment.h"
#include "Core/Asura_Timers.h"
#include "Entity/Asura_PosAndOriInterpolator.h"
#ifdef ASURA_USE_CONTAINER_SYSTEM
#include "Entity/Container/Asura_ContainerState.h"
#endif //ASURA_USE_CONTAINER_SYSTEM
#include "Entity/Ragdoll/Asura_ClientEntity_Ragdoll.h"
#include "Interface/Asura_CommandConsole.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"
#include "Network/Asura_Player.h"
#include "Physics/Asura_Physics.h"
#include "Rendering/Asura_Render.h"
#include "Shapes/Asura_Shapes.h"
#include "Sound/Asura_Sound_Event_System.h"
#include "SpecialFX/PFX/Asura_PFX_System.h"
#include "SpecialFX/PFX/Asura_PFX_Entity_Effect.h"
#include "Core/AVP_Game_Core.h"

// Project independent.
#include "Actor/Weapons/Sentrygun/AVP_Sentrygun_AIAnimStateData.h"
#include "Actor/Weapons/Sentrygun/AVP_Sentrygun_AnimationHandler.h"
#include "Actor/AVP_Actor_CameraHandler.h"
#include "Actor/AVP_Actor_HUDHandler.h"
#include "Actor/AVP_Actor_InputHandler.h"

#include "Animation/AVP_AnimFlags.h"
#include "Core/AVP_Settings.h"
#include "Entity/Triggers/AVP_ClientEntity_InteractionTrigger.h"
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/AVP_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_Actor_Messages.h"
#include "Entity/PhysicalObject/Actor/Weapons/Sentrygun/AVP_Sentrygun_Messages.h"
#include "Entity/PhysicalObject/Actor/Weapons/Sentrygun/AVP_ServerWeapon_Sentrygun.h"
#include "Inventory/AVP_Inventory_HashIDs.h"
#include "Inventory/AVP_Inventory_Item.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Moves/AVP_Movement_System.h"
#include "Moves/AVP_Moves.h"


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

static const Asura_Hash_ID g_auSoundBlueprintParamHash[ AVP_ClientWeapon_Sentrygun::END_SOUND ] = 
{
	0x38fba2dc,	// Investigating
	0x71d5a669,	// LockedOn
	0xc6a04db5,	// LostTarget
	0xf5779b82,	// ResumingSweep
	0x7350AE61	// DryFire
};

Asura_Hashed_List<AVP_ClientWeapon_Sentrygun> AVP_ClientWeapon_Sentrygun::s_xList;

AVP_ClientWeapon_Sentrygun::AVP_ClientWeapon_Sentrygun()
: PARENT( 0 )
, m_pxContainerAttachment( 0 )
, m_uDefaultBlueprint( 0xfd5d8ead ) // Asura_GetHashID( "SentryGun SingleBarrel" )
{
	Classification = AVP_ENTITYCLASS_SENTRYGUN;
	s_xList.AddToList( this );

}


AVP_ClientWeapon_Sentrygun::~AVP_ClientWeapon_Sentrygun( void ) 
{
	for( u_int uCount = 0; uCount < END_SOUND; uCount ++ )
	{
		if( m_aiSoundEventHandles[uCount]!= ASURA_INVALID_HANDLE ) 
		{
			// if we have an old handle for this sound stop it (which releases it as well) and reset the handle value here. 
			Asura_Sound_Event_System::Stop( m_aiSoundEventHandles[uCount] );
			m_aiSoundEventHandles[uCount] = ASURA_INVALID_HANDLE;
		}
	}

	s_xList.RemoveFromList(this);

	DestroyContainer();
}

void AVP_ClientWeapon_Sentrygun::InitialiseFromBlueprint( const Asura_Blueprint& xBlueprint )
{
	PARENT::InitialiseFromBlueprint( xBlueprint );

	for ( u_int uItr = 0; uItr < END_SOUND; ++uItr )
	{
		m_apxSoundEvent[uItr] = GetSoundEventFromBlueprints( g_auSoundBlueprintParamHash[ uItr ] );
		m_aiSoundEventHandles[uItr] = ASURA_INVALID_HANDLE;
	}

	Asura_Value xValue;
	if ( m_pxPhysicsObjectHandler && xBlueprint.GetParameterValueByHashID( 0x7d643002, xValue ) && xValue.GetBoolValue() == true )	// NoCollisions
	{
		delete m_pxPhysicsObjectHandler;
	}
}

bool AVP_ClientWeapon_Sentrygun::HandleMessage( const Asura_Message* pxMessage )
{
	switch ( pxMessage->GetMsgID() )
	{
		case ASURA_MSG_CLIENTUPDATE:
		{
			const AVP_Message_SentryGun_ClientUpdate* pxData = static_cast<const AVP_Message_SentryGun_ClientUpdate*>( pxMessage->GetData() );

			// If we are inactive and now receiving a client update this means we are becoming active.
			// We should go visible and teleport to this first pos/ori so we don't zip across the level.
			if( !IsEntityActive() )
			{
				Asura_Matrix_3x3 xOrientMatrix;
				pxData->m_xOrientation.ToMatrix( xOrientMatrix );
				TeleportTo( pxData->m_xPosition, xOrientMatrix );
			}

			ProcessClientUpdate( pxData, pxMessage->GetSentTime() );
			
			//return PARENT::HandleMessage( pxMessage );
			return true;
		}

		case ASURA_MSG_AXON_GS2_ANIM_SEQUENCE_DATA:
		{
			const Axon_Gamescene_AnimSequenceData* pxData = static_cast< const Axon_Gamescene_AnimSequenceData* >( pxMessage->GetData() );
			if ( m_pxAnimationHandler ) { m_pxAnimationHandler->HandleGamesceneAnimSequenceData( *pxData ); }
			return true;
		}

		case AVP_MSGID_REQUEST_ANIMATION:
		{
			const AVP_Message_Actor_RequestAnimation* pxData = static_cast< const AVP_Message_Actor_RequestAnimation* >( pxMessage->GetData() );
			if ( m_pxAnimationHandler ) { m_pxAnimationHandler->HandleAnimationRequest( *pxData ); }
			return true;
		}


		case AVP_MSGID_SENTRYGUN_ENABLE:
		{
			EnableContainer( true );
			return true;
		}

		case AVP_MSGID_SENTRYGUN_DISABLE:
		case AVP_MSGID_SENTRYGUN_DESTROY:
		{
			EnableContainer( false );
			return true;
		}

		case AVP_MSGID_SENTRYGUN_PLAYSOUND:
		{
			const AVP_Message_Sentrygun_PlaySound* pxData = static_cast< const AVP_Message_Sentrygun_PlaySound* >( pxMessage->GetData() );
			PlaySound( pxData->GetSound() );
			return true;
		}

		default:
		{
			return PARENT::HandleMessage( pxMessage );
		}
	}
}

void AVP_ClientWeapon_Sentrygun::PostCreateInitialise(void)
{
	if ( GetBlueprintHashID() != ASURA_HASH_ID_UNSET )
	{
		//If we do not have an animation handler yet, create it so we can set the skin.
		if(!m_pxAnimationHandler)
		{
			CreateAnimationHandler();
		}
	}

	// if we havent already create the container that has the sentry gun light. 
	if ( ! m_pxContainerAttachment )
	{
		CreateContainer();
	}

	// turn the light off if the server life state is not alive. 
	AVP_ServerWeapon_Sentrygun* pxServerGun = AVP_ServerWeapon_Sentrygun::FindEntity( GetGuid() );
	if( pxServerGun && ( pxServerGun->GetLifeState() != AVP_ServerWeapon_Sentrygun::ALIVE ) )
	{
		EnableContainer( false );
	}

	PARENT::PostCreateInitialise();
}

void AVP_ClientWeapon_Sentrygun::CreateAnimationHandler()
{
	Asura_Assert( m_pxAnimationHandler == NULL, "Already got an animation handler" );
	m_pxAnimationHandler = new AVP_Sentrygun_AnimationHandler(*this);
	m_pxAnimationHandler->Initialise();
}

void AVP_ClientWeapon_Sentrygun::ProcessClientUpdate( const AVP_Message_SentryGun_ClientUpdate *pxData, float fServerTime, bool bProcessPosAndOri /*= true*/ )
{
	if ( ! m_pxContainerAttachment )
	{
		CreateContainer();
	}

	if( m_pxContainerAttachment )
	{
		if( pxData->GetLightOn() && ( m_pxContainerAttachment->GetInputState() == Asura_Container_InputState::Off() ) )
		{
			// if the light should be on and it is off, turn it on
			m_pxContainerAttachment->ChangeState( Asura_Container_InputState::On() );
		}
		
		if( ! pxData->GetLightOn() && ( m_pxContainerAttachment->GetInputState() == Asura_Container_InputState::On() ) )
		{
			// if light should be off and its on, Off it goes. 
			m_pxContainerAttachment->ChangeState( Asura_Container_InputState::Off() );
		}
	}

	PARENT::ProcessClientUpdate( pxData, fServerTime, bProcessPosAndOri );
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientWeapon_Sentrygun::Create
// Author       : Andy Tate
// Date         : 3-Oct-2006
////////////////////////////////////////////////////////////////////////////////

AVP_ClientWeapon_Sentrygun* AVP_ClientWeapon_Sentrygun::Create (const Asura_Message* pxMessage)
{
	AVP_ClientWeapon_Sentrygun*	pxClient	= new AVP_ClientWeapon_Sentrygun;

	// Initialise the entity...
	pxClient->HandleMessage(pxMessage);

	// Return the entity...
	return(pxClient);
}

/// End of function 'AVP_ClientActor_Alien::Create'. ///

void AVP_ClientWeapon_Sentrygun::OnDeath(const AVP_EntityDamage_ApplyToEntity& xDamage)
{
	PARENT::OnDeath(xDamage);

	// TODO Go Bang
	EnableContainer( false );

	if ( !m_pxAnimationHandler || !static_cast< AVP_Sentrygun_AnimationHandler* >( m_pxAnimationHandler )->SetDamagedSkin() )
	{
		SetAsDestroyed();
	}
}

void AVP_ClientWeapon_Sentrygun::PlaySound( Sound eSound )
{
	if ( m_apxSoundEvent[ eSound ] )
	{
		if( m_aiSoundEventHandles[eSound]!= ASURA_INVALID_HANDLE ) 
		{
			// if we have an old handle for this sound stop it (which releases it as well) and reset the handle value here. 
			Asura_Sound_Event_System::Stop( m_aiSoundEventHandles[eSound] );
			m_aiSoundEventHandles[eSound] = ASURA_INVALID_HANDLE;
		}

		Asura_Sound_Event_System::PlayEvent( m_apxSoundEvent[eSound], &m_aiSoundEventHandles[eSound], &GetCurrentPosition(), 1.0f, GetGuid() );
	}
}

const AVP_Actor_AnimStateData* AVP_ClientWeapon_Sentrygun::GetStateDataFromMesssageData( const u_int uState, const AVP_Message_Actor_ClientUpdate* pxActorData )
{
	if ( GetInventorySystem() )
	{
		const AVP_Inventory_Item* pxItem = GetInventorySystem()->GetCurrentItem( uAVP_INVENTORY_GROUPHASH_WEAPON );
		if ( pxItem )
		{
			return AVP_Sentrygun_AIAnimStateData::GetAnimStateData( GetAnimStateBlueprintType(), pxItem->GetBlueprintHash(), uState );
		}
	}
	//else
	return NULL;
}

const AVP_Actor_AnimStateData* AVP_ClientWeapon_Sentrygun::GetStateData( const u_int uState, const u_int uGroundType, const u_int uLimbDamage, const bool bIsAiming, const u_int uCoverType )
{
	if ( GetInventorySystem() )
	{
		const AVP_Inventory_Item* pxItem = GetInventorySystem()->GetCurrentItem( uAVP_INVENTORY_GROUPHASH_WEAPON );
		if ( pxItem )
		{
			return AVP_Sentrygun_AIAnimStateData::GetAnimStateData( GetAnimStateBlueprintType(), pxItem->GetBlueprintHash(), uState );
		}
	}
	//else
	return NULL;
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientWeapon_Sentrygun::CreateContainer
// Author       : Callum Hope CAH
// Date         : 18 Feb 2009
// Scope		: Protected
//
// Desc			: Creates the containers used with the sentry gun, be sure to call this only after the gun updates are happening
//				  Otherwise there will be no animations active to attach the containers to. 
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientWeapon_Sentrygun::CreateContainer()
{
	Asura_Animation* pxAnimation = ( m_pxAnimationHandler == NULL ) ? NULL : m_pxAnimationHandler->GetAnimation();

	if(pxAnimation == NULL)
	{
		Asura_Assert(false, "Unable to get animation to attach the torch to.");
	}
	else
	{
		// get the sentry gun blueprint (hash value defined on initialisation of the AVP_CLientWeapon_SentryGun
		const Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_ACTORS, m_uDefaultBlueprint );

		if(pxBlueprint == NULL)
		{
			Asura_Assert(false, "Unable to find the Sentry Gun Blurprint.");
		}
		else
		{
			Asura_Hash_ID uContainerHash = ASURA_HASH_ID_UNSET;
			Asura_Value	xValue;

			if( pxBlueprint->GetParameterValueByHashID( 0x24D7901F /* "SentryGun.Beam.Red" */, xValue ) )	
			{
				//if we found the blueprint variable for the container get the hash value of it
				uContainerHash	= xValue.GetHashIDValue();
			}

			if( uContainerHash == ASURA_HASH_ID_UNSET )
			{
				Asura_Assertf( false, ( "Failed to find container to use for a Sentury Gun ( GUID %d ).", GetGuid() ) );
			}
			else
			{
				// If we found the hash for the container Create the attachment for it. 
				m_pxContainerAttachment = new Asura_AnimationAttachment_ClientContainerInstance( uContainerHash, Asura_Container_InputState::On(), GetGuid() );
				
				// then resize it depending on the individual sentry gun FOV size and attach it. 
				SetContainerRange();
				if( ! m_pxContainerAttachment->LinkAttachment( pxAnimation, 0x06233516 /* "light" */, false ) )
				{
					Asura_Assert( false, "AVP_ClientWeapon_Sentrygun::CreateContainer\n the \"light\" Marker piont can not be found,\n container will not be shown. " );;	
				}
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientWeapon_Sentrygun::DestroyContainer
// Author       : Callum Hope CAH
// Date         : 18 Feb 2009
// Scope		: Protected
//
// Desc			: Destroys all containers used byu the sentury gun. Deinialisation as opposed to an ingame turn off. 
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientWeapon_Sentrygun::DestroyContainer()
{
	if ( m_pxContainerAttachment != NULL )
	{
		if ( m_pxContainerAttachment->IsLinked() )
		{
			m_pxContainerAttachment->UnlinkAttachment( true );
		}
		else
		{
			delete m_pxContainerAttachment;
		}
		m_pxContainerAttachment = NULL;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientWeapon_Sentrygun::EnableContainer
// Author       : Callum Hope CAH
// Date         : 18 Feb 2009
// Scope		: Protected
//
// Desc			: Takes a bool and makes sure the light is on or off accordingly. 
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientWeapon_Sentrygun::EnableContainer( bool bEnabled )
{
	if ( m_pxContainerAttachment != NULL )
	{
		if ( m_pxContainerAttachment->IsLinked() )
		{
			// if we have an existing linked container attachment then turn it on off accordingly. 
			if( bEnabled )
			{
				m_pxContainerAttachment->ChangeState( Asura_Container_InputState::On() );
			}
			else
			{
				m_pxContainerAttachment->ChangeState( Asura_Container_InputState::Off() );
			}
		}
		else
		{
			Asura_Assert( false, "AVP_ClientWeapon_Sentrygun::EnableContainer\n Containers are being interacted with without being linked to an animation" );
		}
	}
	else
	{
		Asura_Assert( false, "AVP_ClientWeapon_Sentrygun::EnableContainer\n There are no containers but we are trying to interact with them. " );
	}
}

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ClientWeapon_Sentrygun::SetContainerRange
// Author       : Callum Hope CAH
// Date         : 18 Feb 2009
// Scope		: Private
//
// Desc			: 
////////////////////////////////////////////////////////////////////////////////

void AVP_ClientWeapon_Sentrygun::SetContainerRange()
{
/*	Asura_ClientContainerInstance *pxClient = m_pxContainerAttachment->GetClientContainerInstance();

	if( pxClient->GetClassification() == AsuraContainerClass_Collection )
	{
		Asura_ClientContainerInstance_Collection * pxCollection = static_cast<Asura_ClientContainerInstance_Collection*>( pxClient );
		AVP_FindSentryBeamFunctor xFunctor;

		pxCollection->Traverse( xFunctor );

		if( xFunctor.GetProjector() )
		{
			//fMaxRange = xFunctor.GetProjector()->GetParam().m_fMaxRange;
			
			//m_pxAnimation = const_cast<Asura_Animation*>( xFunctor.GetProjector()->GetAnimation() );

			//m_pxModifier = new AVP_BoneDataModifier_AnimationFlame( m_pxAnimation, pxOwnerAnim, Asura_GetHashID( "muz" ));
		}

		if( xFunctor.GetLightShaft() )
		{
			//xFunctor.GetLightShaft()->GetParam().m_afDistance[1] += 0.01f;
		}
	}
*/
}
/// End of file. ///
