//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: provide client-side access to the new particle system, with similar
// usage to CSimpleEmitter
//
// $NoKeywords: $
//===========================================================================//

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

#include "particlemgr.h"
#include "particles/particles.h"
#include "particlesphererenderer.h"
#include "smartptr.h"
#include "particles_simple.h"
#include "tier1/utlobjectreference.h"


//-----------------------------------------------------------------------------
// Particle effect
//-----------------------------------------------------------------------------
class CNewParticleEffect : public IParticleEffect, public CParticleCollection, public CDefaultClientRenderable
{
public:
	DECLARE_CLASS_NOBASE( CNewParticleEffect );
	DECLARE_REFERENCED_CLASS( CNewParticleEffect );

public:
	friend class CRefCountAccessor;

	// list management
	CNewParticleEffect *m_pNext;
	CNewParticleEffect *m_pPrev;

	// Call this before adding a bunch of particles to give it a rough estimate of where
	// your particles are for sorting amongst other translucent entities.
	void SetSortOrigin( const Vector &vSortOrigin );
	bool ShouldDraw( void );
	virtual bool IsTransparent( void );
	virtual bool IsTwoPass( void );
	virtual bool UsesPowerOfTwoFrameBufferTexture( void );
	virtual bool UsesFullFrameBufferTexture( void );
	const QAngle& GetRenderAngles( void );
	const matrix3x4_t& RenderableToWorldTransform();
	void GetRenderBounds( Vector& mins, Vector& maxs );

	// check if the new bounds of the particle system needs its client-leaf info needs to be updated
	void DetectChanges( void );
	const Vector &GetRenderOrigin( void );
	PMaterialHandle GetPMaterial( const char *name );
	bool RecalculateBoundingBox();
	Particle* AddParticle( unsigned int particleSize, PMaterialHandle material, const Vector &origin );
	const char *GetEffectName();
	void SetDontRemove( bool bSet );
	void SetDrawn( bool bDrawn );
	void SetFirstFrameFlag( bool bFirst );
	void SetNeedsBBoxUpdate( bool bNeedsUpdate );
	void SetAutoUpdateBBox( bool bNeedsUpdate );
	void SetRemoveFlag( void );
	bool GetRemoveFlag( void );
	bool GetFirstFrameFlag( void );
	bool GetNeedsBBoxUpdate( void );
	bool GetAutoUpdateBBox( void );
	bool ShouldPerformCullCheck() const;
	void MarkShouldPerformCullCheck( bool bEnable );
	CBaseEntity *GetOwner( void ) { return m_hOwner; }
	void SetOwner( CBaseEntity *pOwner ) { m_hOwner = pOwner; }
	CNewParticleEffect* ReplaceWith( const char *pParticleSystemName );

	static CSmartPtr<CNewParticleEffect> Create( CBaseEntity *pOwner, const char *pParticleSystemName,
												 const char *pDebugName = NULL );
	static CSmartPtr<CNewParticleEffect> Create( CBaseEntity *pOwner, CParticleSystemDefinition *pDef,
												 const char *pDebugName = NULL );
	virtual int DrawModel( int flags );

	void DebugDrawBbox ( bool bCulled );

	// CParticleCollection overrides
public:
	void StopEmission( bool bInfiniteOnly = false, bool bRemoveAllParticles = false, bool bWakeOnStop = false );
	void SetDormant( bool bDormant );
	void SetControlPoint( int nWhichPoint, const Vector &v );
	void SetControlPointEntity( int nWhichPoint, CBaseEntity *pEntity );
	void SetControlPointOrientation( int nWhichPoint, const Quaternion &q );
	void SetControlPointOrientation( int nWhichPoint, const Vector &forward, const Vector &right, const Vector &up );
	void SetControlPointForwardVector( int nWhichPoint, const Vector &v );
	void SetControlPointUpVector( int nWhichPoint, const Vector &v );
	void SetControlPointRightVector( int nWhichPoint, const Vector &v );

	FORCEINLINE EHANDLE const &GetControlPointEntity( int nWhichPoint )
	{
		return m_hControlPointOwners[ nWhichPoint ];
	}


// IParticleEffect overrides
public:

	virtual void	SimulateParticles( CParticleSimulateIterator *pIterator )
	{
	}
	virtual void	RenderParticles( CParticleRenderIterator *pIterator )
	{
	}		

	virtual void				SetParticleCullRadius( float radius );
	virtual void				NotifyRemove( void );
	virtual const Vector &		GetSortOrigin( void );

//	virtual void				NotifyDestroyParticle( Particle* pParticle );
	virtual void				Update( float flTimeDelta );

	// All Create() functions should call this so the effect deletes itself
	// when it is removed from the particle manager.
	void SetDynamicallyAllocated( bool bDynamic = true );

	virtual bool				ShouldSimulate() const { return m_bSimulate; }
	virtual void				SetShouldSimulate( bool bSim ) { m_bSimulate = bSim; }

	int AllocateToolParticleEffectId();
	int GetToolParticleEffectId() const;
	CNewParticleEffect( CBaseEntity *pOwner, const char *pEffectName );
	CNewParticleEffect( CBaseEntity *pOwner, CParticleSystemDefinition *pEffect );
	virtual ~CNewParticleEffect();

protected:
	// Returns nonzero if Release() has been called.
	int		IsReleased();
	
	// Used to track down bugs.
	const char	*m_pDebugName;

	bool		m_bDontRemove : 1;
	bool		m_bRemove : 1;
	bool		m_bDrawn : 1;
	bool		m_bNeedsBBoxUpdate : 1;
	bool		m_bIsFirstFrame : 1;
	bool		m_bAutoUpdateBBox : 1;
	bool		m_bAllocated : 1;
	bool		m_bSimulate : 1;
	bool		m_bShouldPerformCullCheck : 1;

	int			m_nToolParticleEffectId;
	Vector		m_vSortOrigin;
	EHANDLE		m_hOwner;
	EHANDLE     m_hControlPointOwners[MAX_PARTICLE_CONTROL_POINTS];

	// holds the min/max bounds used to manage this thing in the client leaf system
	Vector		m_LastMin;
	Vector		m_LastMax;

private:
	// Update the reference count.
	void		AddRef();
	void		Release();
	void		RecordControlPointOrientation( int nWhichPoint );
	void		Construct();
	
	int			m_RefCount;		// When this goes to zero and the effect has no more active
								// particles, (and it's dynamically allocated), it will delete itself.

	CNewParticleEffect( const CNewParticleEffect & ); // not defined, not accessible
};


//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline int CNewParticleEffect::GetToolParticleEffectId() const
{
	return m_nToolParticleEffectId;
}

inline int CNewParticleEffect::AllocateToolParticleEffectId()
{
	m_nToolParticleEffectId = ParticleMgr()->AllocateToolParticleEffectId();
	return m_nToolParticleEffectId;
}

// Call this before adding a bunch of particles to give it a rough estimate of where
// your particles are for sorting amongst other translucent entities.
inline void CNewParticleEffect::SetSortOrigin( const Vector &vSortOrigin )
{
	m_vSortOrigin = vSortOrigin;
}

inline const Vector &CNewParticleEffect::GetSortOrigin( void )
{
	return m_vSortOrigin;
}

inline bool CNewParticleEffect::ShouldDraw( void )
{
	return true;
}

inline bool CNewParticleEffect::IsTransparent( void )
{
	return CParticleCollection::IsTranslucent();
}

inline const QAngle& CNewParticleEffect::GetRenderAngles( void )
{
	return vec3_angle;
}

inline const matrix3x4_t &	CNewParticleEffect::RenderableToWorldTransform()
{
	static matrix3x4_t mat;
	SetIdentityMatrix( mat );
	PositionMatrix( GetRenderOrigin(), mat );
	return mat;
}

inline Vector const &CNewParticleEffect::GetRenderOrigin( void )
{
	return m_vSortOrigin;
}

inline PMaterialHandle CNewParticleEffect::GetPMaterial(const char *name)
{
	//!!
	Assert( 0 );
	return NULL;
}

inline Particle* CNewParticleEffect::AddParticle( unsigned int particleSize, PMaterialHandle material, const Vector &origin )
{
	//!!
	Assert( 0 );
	return NULL;
}


inline const char *CNewParticleEffect::GetEffectName()
{
	return GetName();
}

inline void CNewParticleEffect::SetDontRemove( bool bSet )
{
	m_bDontRemove = bSet;
}

inline void CNewParticleEffect::SetDrawn( bool bDrawn )
{
	m_bDrawn = bDrawn;
}

inline void CNewParticleEffect::SetFirstFrameFlag( bool bFirst )
{
	m_bIsFirstFrame = bFirst;
}

inline void CNewParticleEffect::SetDynamicallyAllocated( bool bDynamic )
{
	m_bAllocated = bDynamic;
}

inline void CNewParticleEffect::SetNeedsBBoxUpdate( bool bNeedsUpdate )
{
	m_bNeedsBBoxUpdate = bNeedsUpdate;
}

inline void CNewParticleEffect::SetAutoUpdateBBox( bool bNeedsUpdate )
{
	m_bAutoUpdateBBox = bNeedsUpdate;
}

inline void CNewParticleEffect::SetRemoveFlag( void )
{
	m_bRemove = true;
}

inline bool CNewParticleEffect::GetRemoveFlag( void )
{
	return m_bRemove;
}

inline bool CNewParticleEffect::GetFirstFrameFlag( void )
{
	return m_bIsFirstFrame;
}

inline bool CNewParticleEffect::GetNeedsBBoxUpdate( void )
{
	return m_bNeedsBBoxUpdate;
}

inline bool CNewParticleEffect::GetAutoUpdateBBox( void )
{
	return m_bAutoUpdateBBox;
}

inline bool CNewParticleEffect::ShouldPerformCullCheck() const
{
	return m_bShouldPerformCullCheck;
}

inline void CNewParticleEffect::MarkShouldPerformCullCheck( bool bEnable )
{
	m_bShouldPerformCullCheck = bEnable;
}

inline CSmartPtr<CNewParticleEffect> CNewParticleEffect::Create( CBaseEntity *pOwner, const char *pParticleSystemName, const char *pDebugName )
{
	CNewParticleEffect *pRet = new CNewParticleEffect( pOwner, pParticleSystemName );
	pRet->m_pDebugName = pDebugName;
	pRet->SetDynamicallyAllocated( true );
	return pRet;
}

inline CSmartPtr<CNewParticleEffect> CNewParticleEffect::Create( CBaseEntity *pOwner, CParticleSystemDefinition *pDef, const char *pDebugName )
{
	CNewParticleEffect *pRet = new CNewParticleEffect( pOwner, pDef );
	pRet->m_pDebugName = pDebugName;
	pRet->SetDynamicallyAllocated( true );
	return pRet;
}

//--------------------------------------------------------------------------------
// If you use an HPARTICLEFFECT instead of a cnewparticleeffect *, you get a pointer
// which will go to null when the effect is deleted
//--------------------------------------------------------------------------------
typedef CUtlReference<CNewParticleEffect> HPARTICLEFFECT;


#endif // PARTICLES_NEW_H