//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//

#include "cbase.h"
#include "particles_simple.h"
#include "particles_localspace.h"
#include "c_te_effect_dispatch.h"
#include "clienteffectprecachesystem.h"
#include "tier0/vprof.h"
#include "fx.h"
#include "r_efx.h"
#include "tier1/KeyValues.h"
#include "dlight.h"
#include "tf_shareddefs.h"
#include "tf_fx_muzzleflash.h"
#include "toolframework/itoolframework.h"
#include "IEffects.h"
#include "fx_sparks.h"
#include "iefx.h"
#include "fx_quad.h"
#include "fx.h"
#include "toolframework_client.h"

// Precache our effects
CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffect_TF_MuzzleFlash )
	CLIENTEFFECT_MATERIAL( "effects/muzzleflash1" )
	CLIENTEFFECT_MATERIAL( "effects/muzzleflash2" )
	CLIENTEFFECT_MATERIAL( "effects/muzzleflash3" )
	CLIENTEFFECT_MATERIAL( "effects/muzzleflash4" )
CLIENTEFFECT_REGISTER_END()

ConVar cl_muzzleflash_dlight_1st( "cl_muzzleflash_dlight_1st", "1" );

void TE_DynamicLight( IRecipientFilter& filter, float delay,
	const Vector* org, int r, int g, int b, int exponent, float radius, float time, float decay, int nLightIndex = LIGHT_INDEX_TE_DYNAMIC );

extern PMaterialHandle g_Material_Spark;

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void TF_3rdPersonMuzzleFlashCallback( const CEffectData &data )
{
	float scale = data.m_flMagnitude;
	int attachmentIndex = data.m_nAttachmentIndex;
	
	CSmartPtr<CLocalSpaceEmitter> pSimple = CLocalSpaceEmitter::Create( "MuzzleFlash", data.m_hEntity, attachmentIndex, 0 );
	
	SimpleParticle *pParticle;
	Vector			forward(1,0,0), offset, right(0,1,0);

	//
	// Flash
	//

	if ( data.m_nHitBox > 0 )	// >0 is a mg flash in script based muzzleflashes ..
	{
		offset = vec3_origin;

		// small inner cone
		scale *= 4;
		float flScale = random->RandomFloat( scale-0.1f, scale+0.1f );

		int i;
		for ( i = 1; i < 9; i++ )
		{
			offset = (forward * (i*2.0f*scale));

			pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/muzzleflash%d", random->RandomInt(1,4) ) ), offset );
				
			if ( pParticle == NULL )
				return;

			pParticle->m_flLifetime		= 0.0f;
			pParticle->m_flDieTime		= 0.1f;

			pParticle->m_vecVelocity.Init();

			pParticle->m_uchColor[0]	= 255;
			pParticle->m_uchColor[1]	= 255;
			pParticle->m_uchColor[2]	= 255;

			pParticle->m_uchStartAlpha	= 255;
			pParticle->m_uchEndAlpha	= 128;

			pParticle->m_uchStartSize	= (random->RandomFloat( 6.0f, 9.0f ) * (12-(i))/9) * flScale;
			pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
			pParticle->m_flRoll			= random->RandomInt( 0, 360 );
			pParticle->m_flRollDelta	= 0.0f;
		}
	}
	else
	{
		scale *= 4;
		float flScale = random->RandomFloat( scale-0.1f, scale+0.1f );
		
		int i;
		for ( i = 1; i < 9; i++ )
		{
			offset = (forward * (i*2.0f*scale));

			pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/muzzleflash%d", random->RandomInt(1,4) ) ), offset );
				
			if ( pParticle == NULL )
				return;

			pParticle->m_flLifetime		= 0.0f;
			pParticle->m_flDieTime		= 0.1f;

			pParticle->m_vecVelocity.Init();

			pParticle->m_uchColor[0]	= 255;
			pParticle->m_uchColor[1]	= 255;
			pParticle->m_uchColor[2]	= 255;

			pParticle->m_uchStartAlpha	= 128;
			pParticle->m_uchEndAlpha	= 64;

			pParticle->m_uchStartSize	= (random->RandomFloat( 6.0f, 9.0f ) * (12-(i))/9) * flScale;
			pParticle->m_uchEndSize		= pParticle->m_uchStartSize;
			pParticle->m_flRoll			= random->RandomInt( 0, 360 );
			pParticle->m_flRollDelta	= 0.0f;
		}
	}

	//Smoke
	int i;
	offset = vec3_origin;
	for( i=0;i<3;i++ )
	{
		SimpleParticle *pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Mat_DustPuff[0], offset );
			
		if ( pParticle )
		{
			pParticle->m_flLifetime		= 0.0f;
			pParticle->m_flDieTime		= 0.3f;

			pParticle->m_vecVelocity.Init();
			pParticle->m_vecVelocity = forward * ( random->RandomFloat( 80.0f, 100.0f ) + (2-i)*15 );

			int color = random->RandomInt( 200, 255 );
			pParticle->m_uchColor[0]	= color;
			pParticle->m_uchColor[1]	= color;
			pParticle->m_uchColor[2]	= color;

			pParticle->m_uchStartAlpha	= 32;
			pParticle->m_uchEndAlpha	= 0;

			pParticle->m_uchStartSize	= ( 4.0 + 3.0*(2-i) ) * data.m_flMagnitude;
			pParticle->m_uchEndSize		= pParticle->m_uchStartSize * 13.0f;
			pParticle->m_flRoll			= random->RandomInt( 0, 360 );
			pParticle->m_flRollDelta	= random->RandomFloat( -0.5f, 0.5f );
		}
	}
}

void TF_3rdPersonMuzzleFlashCallback_SentryGun( const CEffectData &data )
{
	int iMuzzleFlashAttachment = data.m_nAttachmentIndex;
	int iUpgradeLevel	= data.m_fFlags;

	C_BaseEntity *pEnt = data.GetEntity();
	if ( pEnt && !pEnt->IsDormant() )
	{
		// The created entity kills itself
		//C_MuzzleFlashModel::CreateMuzzleFlashModel( "models/effects/sentry1_muzzle/sentry1_muzzle.mdl", pEnt, iMuzzleFlashAttachment );

		char *pszMuzzleFlashParticleEffect = NULL;
		switch( iUpgradeLevel )
		{
		case 1:
		default:
			pszMuzzleFlashParticleEffect = "muzzle_sentry";
			break;
		case 2:
		case 3:
			pszMuzzleFlashParticleEffect = "muzzle_sentry2";
			break;
		}

		DispatchParticleEffect( pszMuzzleFlashParticleEffect, PATTACH_POINT_FOLLOW, pEnt, iMuzzleFlashAttachment );
	}
}

//TODO: Come back and make this guy a nice particle.
DECLARE_CLIENT_EFFECT( "TF_3rdPersonMuzzleFlash", TF_3rdPersonMuzzleFlashCallback );
DECLARE_CLIENT_EFFECT( "TF_3rdPersonMuzzleFlash_SentryGun", TF_3rdPersonMuzzleFlashCallback_SentryGun );


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pszModelName - 
//			vecOrigin - 
//			vecForceDir - 
//			vecAngularImp - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
C_MuzzleFlashModel *C_MuzzleFlashModel::CreateMuzzleFlashModel( const char *pszModelName, C_BaseEntity *pParent, int iAttachment, float flLifetime )
{
	C_MuzzleFlashModel *pFlash = new C_MuzzleFlashModel;
	if ( !pFlash )
		return NULL;

	if ( !pFlash->InitializeMuzzleFlash( pszModelName, pParent, iAttachment, flLifetime ) )
		return NULL;

	return pFlash;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool C_MuzzleFlashModel::InitializeMuzzleFlash( const char *pszModelName, C_BaseEntity *pParent, int iAttachment, float flLifetime )
{
	AddEffects( EF_NORECEIVESHADOW | EF_NOSHADOW );
	if ( InitializeAsClientEntity( pszModelName, RENDER_GROUP_OPAQUE_ENTITY ) == false )
	{
		Release();
		return false;
	}

	SetParent( pParent, iAttachment );
	SetLocalOrigin( vec3_origin );
	SetLocalAngles( vec3_angle );

	AddSolidFlags( FSOLID_NOT_SOLID );

	m_flRotateAt = gpGlobals->curtime + 0.2;
	SetLifetime( flLifetime );
	SetNextClientThink( CLIENT_THINK_ALWAYS );

	SetCycle( 0 );
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void C_MuzzleFlashModel::SetLifetime( float flLifetime )
{
	// Expire when the lifetime is up
	m_flExpiresAt = gpGlobals->curtime + flLifetime;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void C_MuzzleFlashModel::ClientThink( void )
{
	if ( !GetMoveParent() || gpGlobals->curtime > m_flExpiresAt )
	{
		Release();
		return;
	}

	if ( gpGlobals->curtime > m_flRotateAt )
	{
		// Pick a new anim frame
		float flDelta = RandomFloat(0.2,0.4) * (RandomInt(0,1) == 1 ? 1 : -1);
		float flCycle = clamp( GetCycle() + flDelta, 0, 1 );
		SetCycle( flCycle );

		SetLocalAngles( QAngle(0,0,RandomFloat(0,360)) );
		m_flRotateAt = gpGlobals->curtime + 0.075;
	}
}


//-----------------------------------------------------------------------------
// This is an incredibly brutal hack to get muzzle flashes positioned correctly for recording
//-----------------------------------------------------------------------------
void C_MuzzleFlashModel::SetIs3rdPersonFlash( bool bEnable )
{
	m_bIs3rdPersonFlash = bEnable;
}

					   
bool C_MuzzleFlashModel::SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
{
	// FIXME: This is an incredibly brutal hack to get muzzle flashes positioned correctly for recording
	// NOTE: The correct, long-term solution, is to make weapon models
	// always store the 3rd person model and to have view model entities
	// always store the 1st person model. I didn't have time to do that for Leipzig.
	int nModelIndex = 0;
	int nWorldModelIndex = 0;
	CBaseCombatWeapon *pParent = dynamic_cast<CBaseCombatWeapon*>( GetMoveParent() );
	if ( m_bIs3rdPersonFlash && pParent )
	{
		nModelIndex = pParent->GetModelIndex();
		nWorldModelIndex = pParent->GetWorldModelIndex();
		pParent->SetModelIndex( nWorldModelIndex );
	}
	
	bool bResult = BaseClass::SetupBones( pBoneToWorldOut, nMaxBones, boneMask, currentTime );

	if ( m_bIs3rdPersonFlash && pParent )
	{
		pParent->SetModelIndex( nModelIndex );
	}

	return bResult;
}


//-----------------------------------------------------------------------------
// Recording
//-----------------------------------------------------------------------------
void C_MuzzleFlashModel::GetToolRecordingState( KeyValues *msg )
{
	if ( !ToolsEnabled() )
		return;

	VPROF_BUDGET( "C_MuzzleFlashModel::GetToolRecordingState", VPROF_BUDGETGROUP_TOOLS );

	BaseClass::GetToolRecordingState( msg );

	C_BaseEntity *pParent = GetMoveParent();
	if ( pParent )
	{
		BaseEntityRecordingState_t *pBaseEntity = (BaseEntityRecordingState_t*)msg->GetPtr( "baseentity" );
		pBaseEntity->m_nOwner = pParent->entindex();

		// FIXME: This recording path is a giant hack in a cascading series of hacks
		C_BaseCombatWeapon *pParentWeapon = dynamic_cast<C_BaseCombatWeapon*>( pParent );
		if ( pParentWeapon )
		{
			if ( pParentWeapon->WeaponState() == WEAPON_NOT_CARRIED )
			{
				pBaseEntity->m_bVisible = false;
			}
		}
	}
}