#ifndef FXPARTICLESYSTEM_H
#define FXPARTICLESYSTEM_H

#include "fxparticle.h"
#include "fxparticleemitter.h"

// Particle amounts relating to allocating memory to the free pool
#define FXPARTSYS_STARTPARTICLES	16
#define FXPARTSYS_DOUBLELIM			256
#define FXPARTSYS_OVERDOUBLE_PERINC	.30

// System relative type
#define FXPARTSYS_RELATIVE_NONE		0
#define FXPARTSYS_RELATIVE_ORIGIN	1
#define FXPARTSYS_RELATIVE_ORIGIN_ANGLE	2

// Particle system origin type
#define FXPARTSYS_ORIGIN_CENTER		0
#define FXPARTSYS_ORIGIN_ENTITY		1
#define FXPARTSYS_ORIGIN_BONE		2
#define FXPARTSYS_ORIGIN_TRACE		3

// Particle system angle type
#define FXPARTSYS_ANGLE_NONE		0
#define FXPARTSYS_ANGLE_ENTITY		1
#define FXPARTSYS_ANGLE_BONE		2
#define FXPARTSYS_ANGLE_NORMAL		3

// Trace
#define FXPARTSYS_TRACE_WORLD		0
#define FXPARTSYS_TRACE_WORLD_WATER	1

// Velocity type
#define FXPARTSYS_VELT_NORMAL		0
#define FXPARTSYS_VELT_OUTWARD		1

// Parse defines
#define FXPARTICLEPARSE_ORG_CENT	"center"
#define FXPARTICLEPARSE_ORG_ENT		"entity"
#define FXPARTICLEPARSE_ORG_BONE	"bone"
#define FXPARTICLEPARSE_ORG_TRACE	"trace"

#define FXPARTICLEPARSE_ANG_NONE	"none"
#define FXPARTICLEPARSE_ANG_ENT		"entity"
#define FXPARTICLEPARSE_ANG_BONE	"bone"
#define FXPARTICLEPARSE_ANG_NORM	"normal"

#define FXPARTICLEPARSE_REL_NONE	"none"
#define FXPARTICLEPARSE_REL_ORG		"origin"
#define FXPARTICLEPARSE_REL_ORGANG	"origin_angle"

#define FXPARTICLEPARSE_TRC_WRLD	"world"
#define FXPARTICLEPARSE_TRC_WRLDWTR	"world_water"

class CBoneAccessor;
class CFXParticleSystem
{
public:
	// Constructors
	CFXParticleSystem();
	CFXParticleSystem( CFXParticleEmitter *pEmitter );

	// Destructor
	~CFXParticleSystem();

	// Parse Data
	virtual void parseData( CFXStorage *pData );

	// Post create (call after entity/bones are set)
	virtual void postCreate( void );

	// Calculate time delta
	virtual void calculateTimeDelta( void );

	// Think particles
	virtual void thinkParticles( void );

	// Init data
	virtual void initData( void );

	// Update positional data
	virtual bool updatePositionalData( void );

	// Update system angle
	virtual bool updateSystemAngle( void );

	// Update system origin
	virtual bool updateSystemOrigin( void );

	// Think
	virtual void Think( void );

	// Finalize Position
	virtual vec3_t finalizePosition( const vec3_t &vecOrg );

	// Render
	virtual void Render( void );

	/*
	* Linked list functionality
	*/
	// Add to particle list
	CFXParticle *addToParticleList( CFXParticle *pParticle );

	// Add to free list
	CFXParticle *addToFreeList( CFXParticle *pParticle );

	// Remove from Particle List
	CFXParticle *removeFromParticleList( CFXParticle *pRemove );

	// Remove from Free List
	CFXParticle *removeFromFreeList( CFXParticle *pRemove );

	// Delete free particles
	void deleteFreeList( void );

	// Delete particle list
	void deleteParticleList( void );

	// Free particle (transfer from particle to free list, return next particle in particle list)
	CFXParticle *freeParticle( CFXParticle *pFree );

	// Get particle from free list
	CFXParticle *getFreeParticle( void );

	// Increase free particle list
	void increaseFreeParticles( int iIncrease );

	// Get new particle
	CFXParticle *getNewParticle( void );

	/*
	* Accessors
	*/
	CFXParticleEmitter *getParticleEmitter( void ) { return m_pParticleEmitter; }
	void setParticleEmitter( CFXParticleEmitter *pEmitter ) { m_pParticleEmitter = pEmitter; }

	CFXParticle *getParticleListHead( void ) { return m_pParticleListHead; }
	void setParticleListHead( CFXParticle *pHead ) { m_pParticleListHead = pHead; }

	CFXParticle *getFreeListHead( void ) { return m_pFreeListHead; }
	void setFreeListHead( CFXParticle *pHead ) { m_pFreeListHead = pHead; }

	CBoneAccessor *getBoneAccessor( void ) { return m_pBoneAccessor; }
	void setBoneAccessor( CBoneAccessor *pAccessor ) { m_pBoneAccessor = pAccessor; }

	std::string getSystemName( void ) { return m_strSystemName; }
	void setSystemName( std::string strName ) { m_strSystemName = strName; }

	cl_entity_t *getEntity( void ) { return m_pEntity; }
	void setEntity( cl_entity_t *pEntity ) { m_pEntity = pEntity; }

	vec3_t getOrigin( void ) { return m_vecOrigin; }
	void setOrigin( vec3_t vecOrigin ) { m_vecOrigin = vecOrigin; }

	vec3_t getOriginOffset( void ) { return m_vecOriginOffset; }
	void setOriginOffset( vec3_t vecOrigin ) { m_vecOriginOffset = vecOrigin; }

	vec3_t getAngleOffset( void ) { return m_vecAngleOffset; }
	void setAngleOffset( vec3_t vecAngle ) { m_vecAngleOffset = vecAngle; }

	vec3_t getEmitOrigin( void ) { return m_vecEmitOrigin; }
	void setEmitOrigin( vec3_t vecOrigin ) { m_vecEmitOrigin = vecOrigin; }

	vec3_t getAngles( void ) { return m_vecAngles; }
	void setAngles( vec3_t vecAngles ) { m_vecAngles = vecAngles; }

	vec3_t getTraceDir( void ) { return m_vecTraceDir; }
	void setTraceDir( vec3_t vecDir ) { m_vecTraceDir = vecDir; }

	vec3_t getNormal( void ) { return m_vecNormal; }
	void setNormal( vec3_t vecNorm ) { m_vecNormal = vecNorm; }

	std::string getOriginBone( void ) { return m_strOriginBone; }
	void setOriginBone( std::string strBone ) { m_strOriginBone = strBone; }

	std::string *getAngleBones( void ) { return m_strAngleBones; }

	int getRelative( void ) { return m_iRelative; }
	void setRelative( int iRel ) { m_iRelative = iRel; }

	int getEmitOriginType( void ) { return m_iEmitOriginType; }
	void setEmitOriginType( int iEmit ) { m_iEmitOriginType = iEmit; } 

	int getEmitAngleType( void ) { return m_iEmitAngleType; }
	void setEmitAngleType( int iEmit ) { m_iEmitAngleType = iEmit; } 

	int getTraceCheck( void ) { return m_iTraceCheck; }
	void setTraceCheck( int iCheck ) { m_iTraceCheck = iCheck; }

	int getParticleCount( void ) { return m_iParticleCount; }
	void incParticleCount( void ) { m_iParticleCount++; }
	void decParticleCount( void ) { m_iParticleCount--; }
	void setParticleCount( int iCount ) { m_iParticleCount = iCount; }

	int getFreeCount( void ) { return m_iFreeCount; }
	void incFreeCount( void ) { m_iFreeCount++; }
	void decFreeCount( void ) { m_iFreeCount--; }
	void setFreeCount( int iCount ) { m_iFreeCount = iCount; }

	float getLastTime( void ) { return m_flLastTime; }
	void setLastTime( float flTime ) { m_flLastTime = flTime; }

	float getTimeDelta( void ) { return m_flTimeDelta; }
	void setTimeDelta( float flDelta ) { m_flTimeDelta = flDelta; }

	bool getNoEmitOnNoHitTrace( void ) { return m_bNoEmitOnNoHitTrace; }
	void setNoEmitOnNoHitTrace( bool trace ) { m_bNoEmitOnNoHitTrace = trace; }

	bool canEmit( void ) { return m_bCanEmit; }
	void setEmit( bool bEmit ) { m_bCanEmit = bEmit; }

	bool getDieOut( void ) { return m_bDieOut; }
	void setDieOut( bool bDie ) { m_bDieOut = bDie; }

protected:
	// Basic particle system data
	CFXParticleEmitter	*m_pParticleEmitter;
	CFXParticle			*m_pParticleListHead;
	CFXParticle			*m_pFreeListHead;
	CBoneAccessor		*m_pBoneAccessor;
	cl_entity_t			*m_pEntity;
	std::string			m_strSystemName;
	std::string			m_strOriginBone;
	std::string			m_strAngleBones[2];
	vec3_t				m_vecOrigin;
	vec3_t				m_vecOriginOffset;	// Always relative to the angle
	vec3_t				m_vecAngleOffset;
	vec3_t				m_vecEmitOrigin;	// Physical location particles emit from
	vec3_t				m_vecAngles;
	vec3_t				m_vecTraceDir;
	vec3_t				m_vecNormal;		// Normal from traceline
	float				m_rmatrix[3][4];	// Used when particles are rel to origin + angle, performance++
	int					m_iRelative;
	int					m_iEmitOriginType;
	int					m_iEmitAngleType;
	int					m_iTraceCheck;
	int					m_iParticleCount;
	int					m_iFreeCount;
	float				m_flLastTime;
	float				m_flTimeDelta;
	bool				m_bNoEmitOnNoHitTrace;	// Don't emit when the trace doesn't hit anything
	bool				m_bCanEmit;
	bool				m_bDieOut;	// Allow particles to die out before removing system
};

#endif