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

#include "cbase.h"
#include "weapon_csbase.h"
#include "fx_cs_shared.h"


#if defined( CLIENT_DLL )

	#define CWeaponGlock C_WeaponGlock
	#include "c_cs_player.h"

#else

	#include "cs_player.h"

#endif


class CWeaponGlock : public CWeaponCSBase
{
public:
	DECLARE_CLASS( CWeaponGlock, CWeaponCSBase );
	DECLARE_NETWORKCLASS(); 
	DECLARE_PREDICTABLE();
	
	CWeaponGlock();

	virtual void Spawn();

	virtual void PrimaryAttack();
	virtual void SecondaryAttack();
	virtual bool Deploy();

	virtual void ItemPostFrame();

	void GlockFire( float fSpread, bool bFireBurst );
	void FireRemaining( float fSpread );
	
	virtual bool Reload();

	virtual void WeaponIdle();

 	virtual float GetInaccuracy() const;

	virtual CSWeaponID GetWeaponID( void ) const		{ return WEAPON_GLOCK; }

private:
	
	CWeaponGlock( const CWeaponGlock & );

	CNetworkVar( bool, m_bBurstMode );
	CNetworkVar( int, m_iBurstShotsRemaining );	// used to keep track of the shots fired during the Glock18 burst fire mode.
	float	m_fNextBurstShot;					// time to shoot the next bullet in burst fire mode
	float	m_flLastFire;
};

IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGlock, DT_WeaponGlock )

BEGIN_NETWORK_TABLE( CWeaponGlock, DT_WeaponGlock )
	#ifdef CLIENT_DLL
		RecvPropBool( RECVINFO( m_bBurstMode ) ),
		RecvPropInt( RECVINFO( m_iBurstShotsRemaining ) ),
	#else
		SendPropBool( SENDINFO( m_bBurstMode ) ),
		SendPropInt( SENDINFO( m_iBurstShotsRemaining ) ),
	#endif
END_NETWORK_TABLE()

#if defined(CLIENT_DLL)
BEGIN_PREDICTION_DATA( CWeaponGlock )
	DEFINE_FIELD( m_flLastFire, FIELD_FLOAT ),
	DEFINE_PRED_FIELD( m_iBurstShotsRemaining, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
 	DEFINE_PRED_FIELD( m_fNextBurstShot, FIELD_FLOAT, 0 ),
END_PREDICTION_DATA()
#endif

LINK_ENTITY_TO_CLASS( weapon_glock, CWeaponGlock );
PRECACHE_WEAPON_REGISTER( weapon_glock );

const float kGlockBurstCycleTime = 0.06f;

CWeaponGlock::CWeaponGlock()
{
	m_bBurstMode = false;
	m_flLastFire = gpGlobals->curtime;
	m_iBurstShotsRemaining = 0;
	m_fNextBurstShot = 0.0f;
}


void CWeaponGlock::Spawn( )
{
	BaseClass::Spawn();

	m_bBurstMode = false;
	m_iBurstShotsRemaining = 0;
	m_fNextBurstShot = 0.0f;
	m_flAccuracy = 0.9f;
}

bool CWeaponGlock::Deploy( )
{
	m_iBurstShotsRemaining = 0;
	m_fNextBurstShot = 0.0f;
	m_flAccuracy = 0.9f;

	return BaseClass::Deploy();
}

void CWeaponGlock::SecondaryAttack()
{
	CCSPlayer *pPlayer = GetPlayerOwner();
	if ( !pPlayer )
		return;

	if ( m_bBurstMode )
	{
		ClientPrint( pPlayer, HUD_PRINTCENTER, "#Switch_To_SemiAuto" );
		m_bBurstMode = false;
		m_weaponMode = Primary_Mode;
	}
	else
	{
		ClientPrint( pPlayer, HUD_PRINTCENTER, "#Switch_To_BurstFire" );
		m_bBurstMode = true;
		m_weaponMode = Secondary_Mode;
	}
	
	m_flNextSecondaryAttack = gpGlobals->curtime + 0.3;
}

float CWeaponGlock::GetInaccuracy() const
{
	if ( weapon_accuracy_model.GetInt() == 1 )
	{
		CCSPlayer *pPlayer = GetPlayerOwner();
		if ( !pPlayer )
			return 0.0f;

		if ( m_bBurstMode )
		{
			if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
				return 1.2f * (1 - m_flAccuracy);

			else if (pPlayer->GetAbsVelocity().Length2D() > 5)
				return 0.185f * (1 - m_flAccuracy);

			else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
				return 0.095f * (1 - m_flAccuracy);

			else
				return 0.3f * (1 - m_flAccuracy);
		}
		else
		{
			if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
				return 1.0f * (1 - m_flAccuracy);

			else if (pPlayer->GetAbsVelocity().Length2D() > 5)
				return 0.165f * (1 - m_flAccuracy);

			else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
				return 0.075f * (1 - m_flAccuracy);

			else
				return 0.1f * (1 - m_flAccuracy);
		}
	}
	else
		return BaseClass::GetInaccuracy();
}


void CWeaponGlock::PrimaryAttack()
{
	CCSPlayer *pPlayer = GetPlayerOwner();
	if ( !pPlayer )
		return;

	float flCycleTime = m_bBurstMode ? 0.5f : GetCSWpnData().m_flCycleTime;

	// Mark the time of this shot and determine the accuracy modifier based on the last shot fired...
	m_flAccuracy -= (0.275)*(0.325 - (gpGlobals->curtime - m_flLastFire));

	if (m_flAccuracy > 0.9)
		m_flAccuracy = 0.9;
	else if (m_flAccuracy < 0.6)
		m_flAccuracy = 0.6;

	m_flLastFire = gpGlobals->curtime;

	if (m_iClip1 <= 0)
	{
		if ( m_bFireOnEmpty )

		{
			PlayEmptySound();
			m_flNextPrimaryAttack = gpGlobals->curtime + 0.1f;
			m_bFireOnEmpty = false;
		}

		return;
	}

	pPlayer->m_iShotsFired++;

	m_iClip1--;

	pPlayer->DoMuzzleFlash();

	//SetPlayerShieldAnim();

	// player "shoot" animation
	pPlayer->SetAnimation( PLAYER_ATTACK1 );

	// non-silenced
	//pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME;
	//pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH;

	FX_FireBullets( 
		pPlayer->entindex(),
		pPlayer->Weapon_ShootPosition(), 
		pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(), 
		GetWeaponID(),
		Primary_Mode,
		CBaseEntity::GetPredictionRandomSeed() & 255, // wrap it for network traffic so it's the same between client and server
		GetInaccuracy(),
		GetSpread(),
		gpGlobals->curtime);

	m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + flCycleTime;

	if (!m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0)
	{
		// HEV suit - indicate out of ammo condition
		pPlayer->SetSuitUpdate("!HEV_AMO0", false, 0);
	}

	SetWeaponIdleTime( gpGlobals->curtime + 2.5f );

	if ( m_bBurstMode )
	{
		// Fire off the next two rounds
		m_fNextBurstShot = gpGlobals->curtime + kGlockBurstCycleTime;
		m_iBurstShotsRemaining = 2;

		SendWeaponAnim( ACT_VM_SECONDARYATTACK );
	}
	else
	{
		SendWeaponAnim( ACT_VM_PRIMARYATTACK );
	}

	// update accuracy
	m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[m_weaponMode];

	//ResetPlayerShieldAnim();
}


// GOOSEMAN : FireRemaining used by Glock18

void CWeaponGlock::FireRemaining( float fSpread )
{
	CCSPlayer *pPlayer = GetPlayerOwner();
	if ( !pPlayer )
		Error( "!pPlayer" );

	if ( m_iBurstShotsRemaining == 0 )
		return;

	if (m_iClip1 <= 0)
	{
		m_iClip1 = 0;
		m_iBurstShotsRemaining = 0;
		m_fNextBurstShot = 0.0f;
		return;
	}
	--m_iClip1;

	// TODO FIXME damage = 18, rangemode 0.9

	float fInaccuracy = GetInaccuracy();
	if ( weapon_accuracy_model.GetInt() == 1 )
		fInaccuracy = 0.05;
	
	FX_FireBullets( 
		pPlayer->entindex(),
		pPlayer->Weapon_ShootPosition(), 
		pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(), 
		GetWeaponID(),
		Secondary_Mode,
		CBaseEntity::GetPredictionRandomSeed() & 255, // wrap it for network traffic so it's the same between client and server
		fInaccuracy,
		GetSpread(),
		m_fNextBurstShot);
	
	pPlayer->SetAnimation( PLAYER_ATTACK1 );
	pPlayer->m_iShotsFired++;

	--m_iBurstShotsRemaining;

	if ( m_iBurstShotsRemaining > 0 )
		m_fNextBurstShot += kGlockBurstCycleTime;
	else
		m_fNextBurstShot = 0.0;

	// update accuracy
	m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[Secondary_Mode];
}


void CWeaponGlock::ItemPostFrame()
{
	while ( m_iBurstShotsRemaining > 0 && gpGlobals->curtime >= m_fNextBurstShot )
	{
		if ( weapon_accuracy_model.GetInt() == 1 )
			FireRemaining(0.05f);
		else
			FireRemaining(GetSpread());
	}

	BaseClass::ItemPostFrame();
}


bool CWeaponGlock::Reload()
{
	if ( m_iBurstShotsRemaining != 0 )
		return true;

	if ( !DefaultPistolReload() )
		return false;

	m_flAccuracy = 0.9;
	return true;
}

void CWeaponGlock::WeaponIdle()
{
	CCSPlayer *pPlayer = GetPlayerOwner();
	if ( !pPlayer )
		return;

	if (m_flTimeWeaponIdle > gpGlobals->curtime)
		return;

	if ( pPlayer->HasShield() )
	{
		SetWeaponIdleTime( gpGlobals->curtime + 20 );
				
		//MIKETODO: shields
		//if ( FBitSet(m_iWeaponState, WPNSTATE_SHIELD_DRAWN) )
		//	 SendWeaponAnim( GLOCK18_SHIELD_IDLE, UseDecrement() ? 1:0 );
	}
	else
	{
		// only idle if the slid isn't back
		if (m_iClip1 != 0)
		{
			SendWeaponAnim( ACT_VM_IDLE );
		}
	}
}