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


#include "imesh.h"
#include "particledraw.h"


#define NUM_PARTICLES_PER_BATCH 200
#ifndef _XBOX
#define MAX_TOTAL_PARTICLES		2048	     
#else
#define MAX_TOTAL_PARTICLES		1024
#endif


class CParticleRenderIterator
{
	friend class CParticleMgr;
	friend class CParticleEffectBinding;
public:
	CParticleRenderIterator();

	const Particle* GetFirst();
	const Particle* GetNext(float sortKey);

	ParticleDraw* GetParticleDraw() const;


private:

	void TestFlushBatch();


private:
	CParticleEffectBinding* m_pEffectBinding;
	CEffectMaterial* m_pMaterial;
	ParticleDraw* m_pParticleDraw;
	CMeshBuilder* m_pMeshBuilder;
	IMesh* m_pMesh;
	bool m_bBucketSort;

	float m_MinZ;
	float m_MaxZ;
	float m_zCoords[MAX_TOTAL_PARTICLES];
	int m_nZCoords;

	Particle* m_pCur;
	bool m_bGotFirst;
	float m_flPrevZ;
	int m_nParticlesInCurrentBatch;
};


class CParticleSimulateIterator
{
	friend class CParticleMgr;
	friend class CParticleEffectBinding;
public:
	CParticleSimulateIterator();

	Particle* GetFirst();
	Particle* GetNext();
	float GetTimeDelta() const;

	void RemoveParticle(Particle* pParticle);
	void RemoveAllParticles();

private:
	CParticleEffectBinding* m_pEffectBinding;
	CEffectMaterial* m_pMaterial;
	float m_flTimeDelta;

	bool m_bGotFirst;
	Particle* m_pNextParticle;
};


inline CParticleRenderIterator::CParticleRenderIterator()
{
	m_pCur = NULL;
	m_bGotFirst = false;
	m_flPrevZ = 0;
	m_nParticlesInCurrentBatch = 0;
	m_MinZ = 1e24;
	m_MaxZ = -1e24;
	m_nZCoords = 0;
}

inline const Particle* CParticleRenderIterator::GetFirst()
{
	Assert(!m_bGotFirst);
	m_bGotFirst = true;

	m_pCur = m_pMaterial->m_Particles.m_pNext;
	if (m_pCur == &m_pMaterial->m_Particles)
		return NULL;

	m_pParticleDraw->m_pSubTexture = m_pCur->m_pSubTexture;
	return m_pCur;
}

inline void CParticleRenderIterator::TestFlushBatch()
{
	++m_nParticlesInCurrentBatch;
	if (m_nParticlesInCurrentBatch >= NUM_PARTICLES_PER_BATCH)
	{
		m_pMeshBuilder->End(false, true);
		m_pMeshBuilder->Begin(m_pMesh, MATERIAL_QUADS, NUM_PARTICLES_PER_BATCH * 4);

		m_nParticlesInCurrentBatch = 0;
	}
}

inline const Particle* CParticleRenderIterator::GetNext(float sortKey)
{
	Assert(m_bGotFirst);
	Assert(m_pCur);

	TestFlushBatch();

	Particle* pNext = m_pCur->m_pNext;

	if (m_bBucketSort)
	{
		m_MinZ = MIN(sortKey, m_MinZ);
		m_MaxZ = MAX(sortKey, m_MaxZ);

		m_zCoords[m_nZCoords] = sortKey;
		++m_nZCoords;
	}
	else
	{
		if (m_pCur != m_pMaterial->m_Particles.m_pNext && m_flPrevZ > sortKey)
		{
			SwapParticles(m_pCur->m_pPrev, m_pCur);
		}
		else
		{
			m_flPrevZ = sortKey;
		}
	}

	m_pCur = pNext;
	if (m_pCur == &m_pMaterial->m_Particles)
		return NULL;

	m_pParticleDraw->m_pSubTexture = m_pCur->m_pSubTexture;
	return m_pCur;
}

inline ParticleDraw* CParticleRenderIterator::GetParticleDraw() const
{
	return m_pParticleDraw;
}


inline CParticleSimulateIterator::CParticleSimulateIterator()
{
	m_pNextParticle = NULL;
#ifdef _DEBUG
	m_bGotFirst = false;
#endif
}

inline Particle* CParticleSimulateIterator::GetFirst()
{
#ifdef _DEBUG
	if (m_bGotFirst)
	{
		Assert(m_pNextParticle == &m_pMaterial->m_Particles);
	}
#endif

	Particle* pRet = m_pMaterial->m_Particles.m_pNext;
	if (pRet == &m_pMaterial->m_Particles)
		return NULL;

#ifdef _DEBUG
	m_bGotFirst = true;
#endif

	m_pNextParticle = pRet->m_pNext;
	return pRet;
}

inline Particle* CParticleSimulateIterator::GetNext()
{
	Particle* pRet = m_pNextParticle;

	if (pRet == &m_pMaterial->m_Particles)
		return NULL;

	m_pNextParticle = pRet->m_pNext;
	return pRet;
}

inline void CParticleSimulateIterator::RemoveParticle(Particle* pParticle)
{
	m_pEffectBinding->RemoveParticle(pParticle);
}

inline void CParticleSimulateIterator::RemoveAllParticles()
{
	Particle* pParticle = GetFirst();
	while (pParticle)
	{
		RemoveParticle(pParticle);
		pParticle = GetNext();
	}
}

inline float CParticleSimulateIterator::GetTimeDelta() const
{
	return m_flTimeDelta;
}


#endif  