//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================

#include "cbase.h"
#include "tf_weapon_invis.h"
#include "in_buttons.h"

#if !defined( CLIENT_DLL )
	#include "vguiscreen.h"
	#include "tf_player.h"
#else
	#include "c_tf_player.h"
#endif

extern ConVar tf_spy_invis_unstealth_time;
extern ConVar tf_spy_cloak_consume_rate;
extern ConVar tf_spy_cloak_regen_rate;

//=============================================================================
//
// TFWeaponBase Melee tables.
//
IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponInvis, DT_TFWeaponInvis )

BEGIN_NETWORK_TABLE( CTFWeaponInvis, DT_TFWeaponInvis )
END_NETWORK_TABLE()

BEGIN_PREDICTION_DATA( CTFWeaponInvis )
END_PREDICTION_DATA()

LINK_ENTITY_TO_CLASS( tf_weapon_invis, CTFWeaponInvis );
PRECACHE_WEAPON_REGISTER( tf_weapon_invis );

// Server specific.
#if !defined( CLIENT_DLL ) 
	BEGIN_DATADESC( CTFWeaponInvis )
	END_DATADESC()
#endif

//-----------------------------------------------------------------------------
// Purpose: Use the offhand view model
//-----------------------------------------------------------------------------
void CTFWeaponInvis::Spawn( void )
{
	BaseClass::Spawn();

	SetViewModelIndex( 1 );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFWeaponInvis::OnActiveStateChanged( int iOldState )
{
	BaseClass::OnActiveStateChanged( iOldState );

	// If we are being removed, we need to remove all stealth effects from our owner
	if ( m_iState == WEAPON_NOT_CARRIED && iOldState != WEAPON_NOT_CARRIED )
	{
		CleanupInvisibilityWatch();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Clear out the view model when we hide
//-----------------------------------------------------------------------------
void CTFWeaponInvis::HideThink( void )
{ 
	SetWeaponVisible( false );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
const char *CTFWeaponInvis::GetViewModel( int viewmodelindex  ) const
{
	// Watch uses the player model as its viewmodel, because it's never seen being carried by the player
	const CEconItemView *pItem = GetAttributeContainer()->GetItem();
	if ( pItem->IsValid() )
	{
		int iClass = 0;
		int iTeam = 0;
		CTFPlayer *pTFPlayer = ToTFPlayer( GetOwnerEntity() );
		if ( pTFPlayer )
		{
			iClass = pTFPlayer->GetPlayerClass()->GetClassIndex();
			iTeam = pTFPlayer->GetTeamNumber();
		}

		return pItem->GetPlayerDisplayModel( iClass, iTeam );
	}

	return BaseClass::GetViewModel( viewmodelindex );
}

//-----------------------------------------------------------------------------
// Purpose: Show/hide weapon and corresponding view model if any
// Input  : visible - 
//-----------------------------------------------------------------------------
void CTFWeaponInvis::SetWeaponVisible( bool visible )
{
	CBaseViewModel *vm = NULL;

	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	if ( pOwner )
	{
		vm = pOwner->GetViewModel( m_nViewModelIndex );
	}

	if ( visible )
	{
		RemoveEffects( EF_NODRAW );
		if ( vm )
		{
			vm->RemoveEffects( EF_NODRAW );
		}
	}
	else
	{
		AddEffects( EF_NODRAW );
		if ( vm )
		{
			vm->AddEffects( EF_NODRAW );
		}
	}
}

//-----------------------------------------------------------------------------
bool CTFWeaponInvis::Deploy( void )
{
	bool b = BaseClass::Deploy();

	SetWeaponIdleTime( gpGlobals->curtime + 1.5 );

	return b;
}

//-----------------------------------------------------------------------------
bool CTFWeaponInvis::Holster( CBaseCombatWeapon *pSwitchingTo )
{ 
	bool bHolster = BaseClass::Holster( pSwitchingTo );

	// far in the future
	SetWeaponIdleTime( gpGlobals->curtime + 10 );

	return bHolster;
}

//-----------------------------------------------------------------------------
void CTFWeaponInvis::PrimaryAttack( void )
{
	// do nothing
}

//-----------------------------------------------------------------------------
void CTFWeaponInvis::SecondaryAttack( void )
{
	// do nothing
}

//-----------------------------------------------------------------------------
void CTFWeaponInvis::ItemBusyFrame( void )
{
	// do nothing
}

//-----------------------------------------------------------------------------
// Purpose: the player alt-fired or otherwise activated the functionality of
//			the invisibility watch
//-----------------------------------------------------------------------------
bool CTFWeaponInvis::ActivateInvisibilityWatch( void )
{
	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	if ( !pOwner )
		return false;

	SetCloakRates();

	bool bDoSkill = false;
	// If we're in TF_COND_STEALTHED - which means we gave it ourselves - always remove it
	// If we're in TF_COND_STEALTHED_USER_BUFF and we have Dead Ringer, allow it to be toggled
	// since we're allowed to fire from stealth
	if ( pOwner->m_Shared.InCond( TF_COND_STEALTHED ) )
	{
		// De-cloak.
		float flDecloakRate = 0.0f;
		CALL_ATTRIB_HOOK_FLOAT( flDecloakRate, mult_decloak_rate );
		if ( flDecloakRate <= 0.0f )
			flDecloakRate = 1.0f;

		pOwner->m_Shared.FadeInvis( 1.0f );
	}
	else
	{
		if ( HasFeignDeath() )
		{
			if ( pOwner->m_Shared.IsFeignDeathReady() )
			{
				// Turn it off...
				SetFeignDeathState( false );
			}
			else if ( pOwner->m_Shared.GetSpyCloakMeter() == 100.f )
			{
				// Turn it on...
				SetFeignDeathState( true );
			}
		}
		else if ( pOwner->CanGoInvisible() && ( pOwner->m_Shared.GetSpyCloakMeter() > 8.0f ) )	// must have over 10% cloak to start
		{
			// Do standard cloak.
			pOwner->m_Shared.AddCond( TF_COND_STEALTHED, -1.f, pOwner );

#ifdef STAGING_ONLY
			if ( pOwner->m_Shared.HasPhaseCloakAbility() )
			{
				pOwner->m_Shared.AddCond( TF_COND_STEALTHED_PHASE, -1.f, pOwner );
			}
#endif // STAGING_ONLY

			bDoSkill = true;
		}
	}

	if ( bDoSkill )
	{
		pOwner->m_Shared.SetNextStealthTime( gpGlobals->curtime + 0.5 );
	}
	else
	{
		pOwner->m_Shared.SetNextStealthTime( gpGlobals->curtime + 0.1 );
	}

	return bDoSkill;
}

//-----------------------------------------------------------------------------
// Purpose: the player has changed loadouts or done something else that causes
//			us to clean up any side effects of our watch
//-----------------------------------------------------------------------------
void CTFWeaponInvis::CleanupInvisibilityWatch( void )
{
	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	if ( !pOwner )
		return;

	pOwner->m_Shared.SetFeignDeathReady( false );

	if ( pOwner->m_Shared.IsStealthed() )
	{
		// De-cloak.
		pOwner->m_Shared.FadeInvis( 1.0f );
	}

	pOwner->HolsterOffHandWeapon();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFWeaponInvis::SetFeignDeathState( bool bEnabled )
{
	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
	if ( !pOwner )
		return;

	if ( pOwner->m_Shared.InCond( TF_COND_GRAPPLINGHOOK ) )
		return;

	if ( bEnabled )
	{
		pOwner->m_Shared.SetFeignDeathReady( true );
		pOwner->SetOffHandWeapon( this );
		pOwner->m_Shared.SetNextStealthTime( gpGlobals->curtime + 0.5 );
	}
	else
	{
		pOwner->m_Shared.SetFeignDeathReady( false );
		if ( !pOwner->m_Shared.InCond( TF_COND_STEALTHED ) )
		{
			pOwner->HolsterOffHandWeapon();
			if ( pOwner->GetActiveWeapon() )
			{
				pOwner->GetActiveWeapon()->m_flNextPrimaryAttack = gpGlobals->curtime + 0.1f;
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Set the correct cloak consume & regen rates for this item.
//-----------------------------------------------------------------------------
void CTFWeaponInvis::SetCloakRates( void )
{
	CTFPlayer *pOwner = ToTFPlayer( GetOwner() );

	float fCloakConsumeRate = tf_spy_cloak_consume_rate.GetFloat();
	float fCloakConsumeFactor = 1.0f;
	CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pOwner, fCloakConsumeFactor, mult_cloak_meter_consume_rate );	// Ask the owner since this attr may come from another weapon
	
	// This value is a inverse scale and does not match expectations on description
	// so we subtract and invert to make it align
	// ie 25% (0.75% in schema) makes 10seconds go to 13.3 when we want 12.5
	// 2 - 0.75 = 1.25... 1 / 1.25 = 0.8.. Consume rate "8". 10s / 0.8 = 12.5s
	if ( fCloakConsumeFactor < 1.0f )
	{
		fCloakConsumeFactor = 1.0f / (2.0f - fCloakConsumeFactor);
	}
	
	pOwner->m_Shared.SetCloakConsumeRate( fCloakConsumeRate * fCloakConsumeFactor );

	float fCloakRegenRate = tf_spy_cloak_regen_rate.GetFloat();
	float fCloakRegenFactor = 1.0f;
	CALL_ATTRIB_HOOK_FLOAT( fCloakRegenFactor, mult_cloak_meter_regen_rate );
	pOwner->m_Shared.SetCloakRegenRate( fCloakRegenRate * fCloakRegenFactor );
}

#ifndef CLIENT_DLL

//-----------------------------------------------------------------------------
// Purpose: Return the right pda panel for the watch model we're using
//-----------------------------------------------------------------------------
void CTFWeaponInvis::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
{
	const char *pszViewModel = GetViewModel(0);
	if ( Q_stristr( pszViewModel, "pocket" ) )
	{
		pPanelName = "pda_panel_spy_invis_pocket";
	}
	else if ( Q_stristr( pszViewModel, "ttg_watch_spy" ) )
	{
		pPanelName = "pda_panel_spy_invis_pocket_ttg";
	}
	else if ( Q_stristr( pszViewModel, "hm_watch" ) )
	{
		pPanelName = "pda_panel_spy_invis_pocket_hm";
	}
	else
	{
		pPanelName = "pda_panel_spy_invis";
	}
}

#endif
