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

#ifndef BEAM_H
#define BEAM_H
#ifdef _WIN32
#pragma once
#endif

#include "baseentity_shared.h"
#include "baseplayer_shared.h"
#if !defined( CLIENT_DLL )
#include "entityoutput.h"
#endif

#include "beam_flags.h"

#define MAX_BEAM_WIDTH			102.3f
#define MAX_BEAM_SCROLLSPEED	100.0f
#define MAX_BEAM_NOISEAMPLITUDE		64

#define SF_BEAM_STARTON			0x0001
#define SF_BEAM_TOGGLE			0x0002
#define SF_BEAM_RANDOM			0x0004
#define SF_BEAM_RING			0x0008
#define SF_BEAM_SPARKSTART		0x0010
#define SF_BEAM_SPARKEND		0x0020
#define SF_BEAM_DECALS			0x0040
#define SF_BEAM_SHADEIN			0x0080
#define SF_BEAM_SHADEOUT		0x0100
#define	SF_BEAM_TAPEROUT		0x0200	// Tapers to zero
#define SF_BEAM_TEMPORARY		0x8000

#define ATTACHMENT_INDEX_BITS	5
#define ATTACHMENT_INDEX_MASK	((1 << ATTACHMENT_INDEX_BITS) - 1)

#if defined( CLIENT_DLL )
#define CBeam C_Beam
#include "c_pixel_visibility.h"
#endif

class CBeam : public CBaseEntity
{
	DECLARE_CLASS( CBeam, CBaseEntity );
public:
	DECLARE_NETWORKCLASS();
	DECLARE_PREDICTABLE();
#if !defined( CLIENT_DLL )
	DECLARE_DATADESC();
#endif

	CBeam();

	virtual void SetModel( const char *szModelName );

	void	Spawn( void );
	void	Precache( void );
#if !defined( CLIENT_DLL )
	int		ObjectCaps( void );
	void	SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways );
	int		UpdateTransmitState( void );
	int		ShouldTransmit( const CCheckTransmitInfo *pInfo );
#endif

	virtual int DrawDebugTextOverlays(void);

	// These functions are here to show the way beams are encoded as entities.
	// Encoding beams as entities simplifies their management in the client/server architecture
	void SetType( int type );
	void SetBeamFlags( int flags );
	void SetBeamFlag( int flag );
	
	// NOTE: Start + End Pos are specified in *relative* coordinates 
	void SetStartPos( const Vector &pos );
	void SetEndPos( const Vector &pos );

	// This will change things so the abs position matches the requested spot
	void SetAbsStartPos( const Vector &pos );
	void SetAbsEndPos( const Vector &pos );

	const Vector &GetAbsStartPos( void ) const;
	const Vector &GetAbsEndPos( void ) const;

	void SetStartEntity( CBaseEntity *pEntity );
	void SetEndEntity( CBaseEntity *pEntity );

	void SetStartAttachment( int attachment );
	void SetEndAttachment( int attachment );

	void SetTexture( int spriteIndex );
	void SetHaloTexture( int spriteIndex );
	void SetHaloScale( float haloScale );
	void SetWidth( float width );
	void SetEndWidth( float endWidth );
	void SetFadeLength( float fadeLength );
	void SetNoise( float amplitude );
	void SetColor( int r, int g, int b );
	void SetBrightness( int brightness );
	void SetFrame( float frame );
	void SetScrollRate( int speed );
	void SetFireTime( float flFireTime );
	void SetFrameRate( float flFrameRate ) { m_flFrameRate = flFrameRate; }

	void SetMinDXLevel( int nMinDXLevel ) { m_nMinDXLevel = nMinDXLevel; }

	void TurnOn( void );
	void TurnOff( void );

	int	GetType( void ) const;
	int	GetBeamFlags( void ) const;
	CBaseEntity* GetStartEntityPtr( void ) const;
	int	GetStartEntity( void ) const;
	CBaseEntity* GetEndEntityPtr( void ) const;
	int	GetEndEntity( void ) const;
	int GetStartAttachment() const;
	int GetEndAttachment() const;

	virtual const Vector &WorldSpaceCenter( void ) const;

	int GetTexture( void );
	float GetWidth( void ) const;
	float GetEndWidth( void ) const;
	float GetFadeLength( void ) const;
	float GetNoise( void ) const;
	int GetBrightness( void ) const;
	float GetFrame( void ) const;
	float GetScrollRate( void ) const;
	float GetHDRColorScale( void ) const;
	void SetHDRColorScale( float flScale ) { m_flHDRColorScale = flScale; }


	// Call after you change start/end positions
	void		RelinkBeam( void );

	void		DoSparks( const Vector &start, const Vector &end );
	CBaseEntity *RandomTargetname( const char *szName );
	void		BeamDamage( trace_t *ptr );
	// Init after BeamCreate()
	void		BeamInit( const char *pSpriteName, float width );
	void		PointsInit( const Vector &start, const Vector &end );
	void		PointEntInit( const Vector &start, CBaseEntity *pEndEntity );
	void		EntsInit( CBaseEntity *pStartEntity, CBaseEntity *pEndEntity );
	void		LaserInit( CBaseEntity *pStartEntity, CBaseEntity *pEndEntity );
	void		HoseInit( const Vector &start, const Vector &direction );
	void		SplineInit( int nNumEnts, CBaseEntity** pEntList, int *attachment  );

	// Input handlers

	static CBeam *BeamCreate( const char *pSpriteName, float width );
	static CBeam *BeamCreatePredictable( const char *module, int line, bool persist, const char *pSpriteName, float width, CBasePlayer *pOwner );

	void LiveForTime( float time );
	void BeamDamageInstant( trace_t *ptr, float damage );

// Only supported in TF2 right now
#if defined( INVASION_CLIENT_DLL )
	virtual bool	ShouldPredict( void )
	{
		return true;
	}
#endif

	virtual const char *GetDecalName( void ) { return "BigShot"; }

#if defined( CLIENT_DLL )
// IClientEntity overrides.
public:
	virtual int			DrawModel( int flags );
	virtual bool		IsTransparent( void );
	virtual bool		ShouldDraw();
	virtual bool		IgnoresZBuffer( void ) const { return true; }
	virtual void		OnDataChanged( DataUpdateType_t updateType );

	virtual bool		OnPredictedEntityRemove( bool isbeingremoved, C_BaseEntity *predicted );

	// Add beam to visible entities list?
	virtual void		AddEntity( void );
	virtual bool		ShouldReceiveProjectedTextures( int flags )
	{
		return false;
	}

// Beam Data Elements
private:
	// Computes the bounding box of a beam local to the origin of the beam
	void ComputeBounds( Vector& mins, Vector& maxs );

	friend void RecvProxy_Beam_ScrollSpeed( const CRecvProxyData *pData, void *pStruct, void *pOut );
	friend class CViewRenderBeams;

#endif

protected:
	CNetworkVar( float, m_flFrameRate );
	CNetworkVar( float, m_flHDRColorScale );
	float		m_flFireTime;
	float		m_flDamage;			// Damage per second to touchers.
	CNetworkVar( int, m_nNumBeamEnts );
#if defined( CLIENT_DLL )
	pixelvis_handle_t	m_queryHandleHalo;
#endif

private:
#if !defined( CLIENT_DLL )
	void InputNoise( inputdata_t &inputdata );
 	void InputWidth( inputdata_t &inputdata );
	void InputColorRedValue( inputdata_t &inputdata );
	void InputColorBlueValue( inputdata_t &inputdata );
	void InputColorGreenValue( inputdata_t &inputdata );
#endif

	// Beam Data Elements
	CNetworkVar( int, m_nHaloIndex );
	CNetworkVar( int, m_nBeamType );
	CNetworkVar( int, m_nBeamFlags );
	CNetworkArray( EHANDLE, m_hAttachEntity, MAX_BEAM_ENTS );
	CNetworkArray( int, m_nAttachIndex, MAX_BEAM_ENTS );
	CNetworkVar( float, m_fWidth );
	CNetworkVar( float, m_fEndWidth );
	CNetworkVar( float, m_fFadeLength );
	CNetworkVar( float, m_fHaloScale );
	CNetworkVar( float, m_fAmplitude );
	CNetworkVar( float, m_fStartFrame );
	CNetworkVar( float, m_fSpeed );
	CNetworkVar( int, m_nMinDXLevel );
	CNetworkVar( float, m_flFrame );

	CNetworkVector( m_vecEndPos );

	EHANDLE		m_hEndEntity;

#if !defined( CLIENT_DLL )
	int			m_nDissolveType;
#endif

public:
#ifdef PORTAL
	CNetworkVar( bool, m_bDrawInMainRender );
	CNetworkVar( bool, m_bDrawInPortalRender );
#endif //#ifdef PORTAL
};

#if !defined( CLIENT_DLL )
//-----------------------------------------------------------------------------
// Inline methods 
//-----------------------------------------------------------------------------
inline int CBeam::ObjectCaps( void )
{ 
	int flags = 0;
	if ( HasSpawnFlags( SF_BEAM_TEMPORARY ) )
		flags = FCAP_DONT_SAVE;
	return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | flags; 
}
#endif

inline void	CBeam::SetFireTime( float flFireTime )		
{ 
	m_flFireTime = flFireTime; 
}

//-----------------------------------------------------------------------------
// NOTE: Start + End Pos are specified in *relative* coordinates 
//-----------------------------------------------------------------------------
inline void CBeam::SetStartPos( const Vector &pos ) 
{ 
#if defined( CLIENT_DLL )
	SetNetworkOrigin( pos );
#endif
	SetLocalOrigin( pos );
}

inline void CBeam::SetEndPos( const Vector &pos ) 
{ 
	m_vecEndPos = pos; 
}
	 
 // center point of beam
inline const Vector &CBeam::WorldSpaceCenter( void ) const 
{
	Vector &vecResult = AllocTempVector();
	VectorAdd( GetAbsStartPos(), GetAbsEndPos(), vecResult );
	vecResult *= 0.5f;
	return vecResult;
}

inline void CBeam::SetStartAttachment( int attachment )	
{
	Assert( (attachment & ~ATTACHMENT_INDEX_MASK) == 0 );
	m_nAttachIndex.Set( 0, attachment );
}

inline void CBeam::SetEndAttachment( int attachment )		
{ 
	Assert( (attachment & ~ATTACHMENT_INDEX_MASK) == 0 );
	m_nAttachIndex.Set( m_nNumBeamEnts-1, attachment );
}

inline void CBeam::SetTexture( int spriteIndex )		
{ 
	SetModelIndex( spriteIndex ); 
}

inline void CBeam::SetHaloTexture( int spriteIndex )	
{ 
	m_nHaloIndex = spriteIndex; 
}

inline void CBeam::SetHaloScale( float haloScale )		
{ 
	m_fHaloScale = haloScale; 
}

inline void CBeam::SetWidth( float width )				
{
	Assert( width <= MAX_BEAM_WIDTH );
	m_fWidth = MIN( MAX_BEAM_WIDTH, width );
}

inline void CBeam::SetEndWidth( float endWidth )		
{ 
	Assert( endWidth <= MAX_BEAM_WIDTH );
	m_fEndWidth	= MIN( MAX_BEAM_WIDTH, endWidth );
}

inline void CBeam::SetFadeLength( float fadeLength )	
{ 
	m_fFadeLength = fadeLength; 
}

inline void CBeam::SetNoise( float amplitude )			
{ 
	m_fAmplitude = amplitude; 
}

inline void CBeam::SetColor( int r, int g, int b )		
{ 
	SetRenderColor( r, g, b, GetRenderColor().a );
}

inline void CBeam::SetBrightness( int brightness )		
{ 
	SetRenderColorA( brightness ); 
}

inline void CBeam::SetFrame( float frame )				
{ 
	m_fStartFrame = frame; 
}

inline void CBeam::SetScrollRate( int speed )			
{ 
	m_fSpeed = speed; 
}

inline CBaseEntity* CBeam::GetStartEntityPtr( void ) const 
{ 
	return m_hAttachEntity[0].Get(); 
}

inline int CBeam::GetStartEntity( void ) const 
{ 
	CBaseEntity *pEntity = m_hAttachEntity[0].Get();
	return pEntity ? pEntity->entindex() : 0; 
}

inline CBaseEntity* CBeam::GetEndEntityPtr( void ) const 
{ 
	return m_hAttachEntity[1].Get(); 
}

inline int CBeam::GetEndEntity( void ) const	
{ 
	CBaseEntity *pEntity = m_hAttachEntity[m_nNumBeamEnts-1].Get();
	return pEntity ? pEntity->entindex() : 0; 
}

inline int CBeam::GetStartAttachment() const
{
	return m_nAttachIndex[0] & ATTACHMENT_INDEX_MASK;
}

inline int CBeam::GetEndAttachment() const
{
	return m_nAttachIndex[m_nNumBeamEnts-1] & ATTACHMENT_INDEX_MASK;
}

inline int CBeam::GetTexture( void )		
{ 
	return GetModelIndex(); 
}

inline float CBeam::GetWidth( void ) const		
{
	return m_fWidth; 
}

inline float CBeam::GetEndWidth( void ) const	
{ 
	return m_fEndWidth; 
}

inline float CBeam::GetFadeLength( void ) const	
{ 
	return m_fFadeLength; 
}

inline float CBeam::GetNoise( void ) const		
{ 
	return m_fAmplitude; 
}

inline int CBeam::GetBrightness( void ) const	
{ 
	return GetRenderColor().a;
}

inline float CBeam::GetFrame( void ) const		
{ 
	return m_fStartFrame; 
}

inline float CBeam::GetScrollRate( void ) const	
{
	return m_fSpeed; 
}

inline float CBeam::GetHDRColorScale( void ) const
{
	return m_flHDRColorScale;
}

inline void CBeam::LiveForTime( float time ) 
{ 
	SetThink(&CBeam::SUB_Remove); 
	SetNextThink( gpGlobals->curtime + time ); 
}

inline void	CBeam::BeamDamageInstant( trace_t *ptr, float damage ) 
{ 
	m_flDamage = damage; 
	m_flFireTime = gpGlobals->curtime - 1;
	BeamDamage(ptr); 
}

bool IsStaticPointEntity( CBaseEntity *pEnt );

// Macro to wrap creation
#define BEAM_CREATE_PREDICTABLE( name, width, player ) \
	CBeam::BeamCreatePredictable( __FILE__, __LINE__, false, name, width, player )

#define BEAM_CREATE_PREDICTABLE_PERSIST( name, width, player ) \
	CBeam::BeamCreatePredictable( __FILE__, __LINE__, true, name, width, player )

// Start/End Entity is encoded as 12 bits of entity index, and 4 bits of attachment (4:12)
#define BEAMENT_ENTITY(x)		((x)&0xFFF)
#define BEAMENT_ATTACHMENT(x)	(((x)>>12)&0xF)


// Beam types, encoded as a byte
enum 
{
	BEAM_POINTS = 0,
	BEAM_ENTPOINT,
	BEAM_ENTS,
	BEAM_HOSE,
	BEAM_SPLINE,
	BEAM_LASER,
	NUM_BEAM_TYPES
};


#endif // BEAM_H