//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Client's CObjectSentrygun
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "c_baseobject.h"
#include "c_tf_player.h"
#include "vgui/ILocalize.h"
#include "c_obj_dispenser.h"

// NVNT haptics system interface
#include "c_tf_haptics.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

using namespace vgui;

//-----------------------------------------------------------------------------
// Purpose: RecvProxy that converts the Team's player UtlVector to entindexes
//-----------------------------------------------------------------------------
void RecvProxy_HealingList(  const CRecvProxyData *pData, void *pStruct, void *pOut )
{
	C_ObjectDispenser *pDispenser = (C_ObjectDispenser*)pStruct;

	CBaseHandle *pHandle = (CBaseHandle*)(&(pDispenser->m_hHealingTargets[pData->m_iElement])); 
	RecvProxy_IntToEHandle( pData, pStruct, pHandle );

	// update the heal beams
	pDispenser->m_bUpdateHealingTargets = true;
}

void RecvProxyArrayLength_HealingArray( void *pStruct, int objectID, int currentArrayLength )
{
	C_ObjectDispenser *pDispenser = (C_ObjectDispenser*)pStruct;

	if ( pDispenser->m_hHealingTargets.Size() != currentArrayLength )
		pDispenser->m_hHealingTargets.SetSize( currentArrayLength );

	// update the heal beams
	pDispenser->m_bUpdateHealingTargets = true;
}

//-----------------------------------------------------------------------------
// Purpose: Dispenser object
//-----------------------------------------------------------------------------

IMPLEMENT_CLIENTCLASS_DT(C_ObjectDispenser, DT_ObjectDispenser, CObjectDispenser)
	RecvPropInt( RECVINFO( m_iState ) ),
	RecvPropInt( RECVINFO( m_iAmmoMetal ) ),
	RecvPropInt( RECVINFO( m_iMiniBombCounter ) ),

	RecvPropArray2( 
		RecvProxyArrayLength_HealingArray,
		RecvPropInt( "healing_array_element", 0, SIZEOF_IGNORE, 0, RecvProxy_HealingList ), 
		MAX_PLAYERS, 
		0, 
		"healing_array"
		)
END_RECV_TABLE()

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
C_ObjectDispenser::C_ObjectDispenser()
{
	m_bUpdateHealingTargets = false;
	m_bPlayingSound = false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
C_ObjectDispenser::~C_ObjectDispenser()
{
	StopSound( "Building_Dispenser.Heal" );
	// NVNT see if local player is in the list of targets
	// temp. fix if dispener is destroyed will stop all healers.
	if(m_bPlayingSound)
	{
		if(tfHaptics.healingDispenserCount>0) {
			tfHaptics.healingDispenserCount --;
			if(tfHaptics.healingDispenserCount==0 && !tfHaptics.wasBeingHealedMedic)
				tfHaptics.isBeingHealed = false;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : updateType - 
//-----------------------------------------------------------------------------
void C_ObjectDispenser::OnDataChanged( DataUpdateType_t updateType )
{
	BaseClass::OnDataChanged( updateType );

#ifdef STAGING_ONLY
	if ( updateType == DATA_UPDATE_CREATED )
	{
		SetNextClientThink( CLIENT_THINK_ALWAYS );
	}
#endif // STAGING_ONLY

	if ( m_bUpdateHealingTargets )
	{
		UpdateEffects();
		m_bUpdateHealingTargets = false;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void C_ObjectDispenser::ClientThink()
{
	BaseClass::ClientThink();

#ifdef STAGING_ONLY
	C_TFPlayer *pTFOwner = GetOwner();
	if ( pTFOwner && pTFOwner->m_Shared.IsEnteringOrExitingFullyInvisible() )
	{
		UpdateEffects();
	}
#endif // STAGING_ONLY
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void C_ObjectDispenser::SetInvisibilityLevel( float flValue )
{
	if ( IsEnteringOrExitingFullyInvisible( flValue ) )
	{
		UpdateEffects();
	}

	BaseClass::SetInvisibilityLevel( flValue );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_ObjectDispenser::UpdateEffects( void )
{
	C_TFPlayer *pOwner = GetOwner();

	if ( GetInvisibilityLevel() == 1.f || ( pOwner && pOwner->m_Shared.IsFullyInvisible() ) )
	{
		StopEffects( true );
		return;
	}

	StopEffects();

	// Now add any new targets
	for ( int i = 0; i < m_hHealingTargets.Count(); i++ )
	{
		C_BaseEntity *pTarget = m_hHealingTargets[i].Get();

		// Loops through the healing targets, and make sure we have an effect for each of them
		if ( pTarget )
		{
			// don't want to show this effect for stealthed spies
			C_TFPlayer *pPlayer = dynamic_cast< C_TFPlayer * >( pTarget );
			if ( pPlayer && ( pPlayer->m_Shared.IsStealthed() || pPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) ) )
				continue;

			bool bHaveEffect = false;
			for ( int targets = 0; targets < m_hHealingTargetEffects.Count(); targets++ )
			{
				if ( m_hHealingTargetEffects[targets].pTarget == pTarget )
				{
					bHaveEffect = true;
					break;
				}
			}

			if ( bHaveEffect )
				continue;
			// NVNT if the dispenser has started to heal the local player
			//   notify the haptics system
			if(pTarget==C_BasePlayer::GetLocalPlayer())
			{
				tfHaptics.healingDispenserCount++;
				if(!tfHaptics.wasBeingHealedMedic) {
					tfHaptics.isBeingHealed = true;
				}
			}

			const char *pszEffectName;
			if ( GetTeamNumber() == TF_TEAM_RED )
			{
				pszEffectName = "dispenser_heal_red";
			}
			else
			{
				pszEffectName = "dispenser_heal_blue";
			}

			CNewParticleEffect *pEffect;

			// if we don't have a model, attach at the origin, otherwise use attachment 'heal_origin'
			if ( FBitSet( GetObjectFlags(), OF_DOESNT_HAVE_A_MODEL ) )
			{
				// offset the origin to player's chest
				if ( FBitSet( GetObjectFlags(), OF_PLAYER_DESTRUCTION ) )
				{
					pEffect = ParticleProp()->Create( pszEffectName, PATTACH_ABSORIGIN_FOLLOW, NULL, Vector( 0, 0, 50 ) );
				}
				else
				{
					pEffect = ParticleProp()->Create( pszEffectName, PATTACH_ABSORIGIN_FOLLOW );
				}
			}
			else
			{
				pEffect = ParticleProp()->Create( pszEffectName, PATTACH_POINT_FOLLOW, "heal_origin" );
			}

			ParticleProp()->AddControlPoint( pEffect, 1, pTarget, PATTACH_ABSORIGIN_FOLLOW, NULL, Vector(0,0,50) );
			
			int iIndex = m_hHealingTargetEffects.AddToTail();
			m_hHealingTargetEffects[iIndex].pTarget = pTarget;
			m_hHealingTargetEffects[iIndex].pEffect = pEffect;

			// Start the sound over again every time we start a new beam
			StopSound( "Building_Dispenser.Heal" );

			CLocalPlayerFilter filter;
			EmitSound( filter, entindex(), "Building_Dispenser.Heal" );

			m_bPlayingSound = true;
		}
	}

	// Stop the sound if we're not healing anyone
	if ( m_bPlayingSound && m_hHealingTargets.Count() == 0 )
	{
		m_bPlayingSound = false;

		// stop the sound
		StopSound( "Building_Dispenser.Heal" );
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_ObjectDispenser::StopEffects( bool bRemoveAll /* = false */ )
{
	// Find all the targets we've stopped healing
	bool bStillHealing[MAX_DISPENSER_HEALING_TARGETS] = { 0 };
	for ( int i = 0; i < m_hHealingTargetEffects.Count(); i++ )
	{
		bStillHealing[i] = false;

		// Are we still healing this target?
		if ( !bRemoveAll )
		{
			for ( int target = 0; target < m_hHealingTargets.Count(); target++ )
			{
				if ( m_hHealingTargets[target] && m_hHealingTargets[target] == m_hHealingTargetEffects[i].pTarget )
				{
					bStillHealing[i] = true;
					break;
				}
			}
		}
	}

	// Now remove all the dead effects
	for ( int i = m_hHealingTargetEffects.Count()-1; i >= 0; i-- )
	{
		if ( !bStillHealing[i] )
		{

			// NVNT if the healing target of this dispenser is the local player.
			//   inform the haptics system interface we are no longer healing.
			if(m_hHealingTargetEffects[i].pTarget==C_BasePlayer::GetLocalPlayer())
			{
				if(tfHaptics.healingDispenserCount>0) {
					tfHaptics.healingDispenserCount --;
					if(tfHaptics.healingDispenserCount==0 && !tfHaptics.wasBeingHealedMedic)
						tfHaptics.isBeingHealed = false;
				}
			}

			ParticleProp()->StopEmission( m_hHealingTargetEffects[i].pEffect );
			m_hHealingTargetEffects.Remove(i);
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Damage level has changed, update our effects
//-----------------------------------------------------------------------------
void C_ObjectDispenser::UpdateDamageEffects( BuildingDamageLevel_t damageLevel )
{
	if ( m_hDamageEffects )
	{
		m_hDamageEffects->StopEmission( false, false );
		m_hDamageEffects = NULL;
	}

	const char *pszEffect = "";

	switch( damageLevel )
	{
	case BUILDING_DAMAGE_LEVEL_LIGHT:
		pszEffect = "dispenserdamage_1";
		break;
	case BUILDING_DAMAGE_LEVEL_MEDIUM:
		pszEffect = "dispenserdamage_2";
		break;
	case BUILDING_DAMAGE_LEVEL_HEAVY:
		pszEffect = "dispenserdamage_3";
		break;
	case BUILDING_DAMAGE_LEVEL_CRITICAL:
		pszEffect = "dispenserdamage_4";
		break;

	default:
		break;
	}

	if ( Q_strlen(pszEffect) > 0 )
	{
		m_hDamageEffects = ParticleProp()->Create( pszEffect, PATTACH_ABSORIGIN );
	}
}

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
int C_ObjectDispenser::GetMaxMetal( void )
{
	return DISPENSER_MAX_METAL_AMMO;
}

//-----------------------------------------------------------------------------
// Control screen 
//-----------------------------------------------------------------------------

DECLARE_VGUI_SCREEN_FACTORY( CDispenserControlPanel, "screen_obj_dispenser_blue" );
DECLARE_VGUI_SCREEN_FACTORY( CDispenserControlPanel_Red, "screen_obj_dispenser_red" );

//-----------------------------------------------------------------------------
// Constructor: 
//-----------------------------------------------------------------------------
CDispenserControlPanel::CDispenserControlPanel( vgui::Panel *parent, const char *panelName )
: BaseClass( parent, "CDispenserControlPanel" ) 
{
	m_pAmmoProgress = new RotatingProgressBar( this, "MeterArrow" );
}

//-----------------------------------------------------------------------------
// Deactivates buttons we can't afford
//-----------------------------------------------------------------------------
void CDispenserControlPanel::OnTickActive( C_BaseObject *pObj, C_TFPlayer *pLocalPlayer )
{
	BaseClass::OnTickActive( pObj, pLocalPlayer );

	Assert( dynamic_cast< C_ObjectDispenser* >( pObj ) );
	m_hDispenser = static_cast< C_ObjectDispenser* >( pObj );

	float flProgress = m_hDispenser ? m_hDispenser->GetMetalAmmoCount() / (float)m_hDispenser->GetMaxMetal() : 0.f;

	m_pAmmoProgress->SetProgress( flProgress );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CDispenserControlPanel::IsVisible( void )
{
	if ( m_hDispenser )
	{
#ifdef STAGING_ONLY
		if ( m_hDispenser->IsMiniBuilding() )
			return false; 
#endif // STAGING_ONLY

		if ( m_hDispenser->GetInvisibilityLevel() == 1.f )
			return false;
	}

	return BaseClass::IsVisible();
}

IMPLEMENT_CLIENTCLASS_DT(C_ObjectCartDispenser, DT_ObjectCartDispenser, CObjectCartDispenser)
END_RECV_TABLE()