#include "client.h"

#define	PV_CLIP_DISTANCE	1024

cpvolume_t	cl_pvolumes[MAX_PVOLUMES];
int			cl_numpvolumes;

static	int	snow_rotation = 0;

/*
===============
CL_SnowParticles
===============
*/
void CL_SnowParticles (int type, cpvolume_t *volume, vec3_t p1, vec3_t p2)
{
	int		i, count;
	int		width, depth, height;
	int		fall_height;
	cparticle_t	*p;

	width = (int) p2[0] - p1[0];
	depth = (int) p2[1] - p1[1];
	height = (int) p2[2] - p1[2];
	
	if (!height)
		height = 10;
	
	//	a 256x256 square will have a density of 16 particles
	volume->pv_max = (width * depth) / 4096;
	//	a 256x256x256 cube will have a density of 16 particles
	//	volume->pv_max = ((double)width * (double)depth * (double)height) / 2097152;

	if (volume->pv_active >= volume->pv_max)
		return;

	if (volume->pv_active < volume->pv_max >> 2)//volume->pv_min)
		//	Nelno:	fill the particle volume if it has few particles!
		count = volume->pv_max >> 1;
	else
		count = 1;

/*
	if (type & PV_SNOW_SLOW)
	{
	}
	else if (type & PV_SNOW_FAST)
	{
	}
	else if (type & PV_SNOW_HEAVY)
	{
	}
	else if (type & PV_SNOW_LIGHT)
	{
	}
	else if (type & PV_SNOW_DRIFT1)
	{
	}
*/
	for (i=0 ; i < count ; i++)
	{
		if (!free_comparticles)
			return;
		p = free_comparticles;
		free_comparticles = p->next;
		p->next = active_comparticles;
		active_comparticles = p;

		p->color = 13;

		p->org[0] = p1[0] + (rand() % width );
		p->org[1] = p1[1] + (rand() % depth );
		p->org[2] = p1[2] + (rand() % height );

		p->vel[0] = rand () % 40 - 20;
		p->vel[1] = rand () % 40 - 20;
		p->vel[2] = -50 - (rand () & 10);
		
		//	no acceleration via gravity
		p->accel [0] = p->accel [1] = p->accel [2] = 0;

		//	time particle was spawned
		p->time = cl.time;

		//	actual distance particle will fall
		fall_height = height - (p1 [2] - p->org[2]);
		//	time particle should die
		p->die_time = cl.time - ((fall_height / p->vel [2]) * 1000);

		p->alpha = 1.0;
		p->alphavel = 1.0;

		p->volume = volume;
		p->volume->pv_active++;

		//	Nelno: added for testing complex particles
		p->type = PARTICLE_SNOW;
		
#ifdef	ROTATING_SNOW		
		snow_rotation++;
		if (snow_rotation > 15)
			snow_rotation = 0;
		p->rotation_vec = snow_rotation;
#endif
		p->size_scale = (rand () / RAND_MAX) * 2.5 + 1.0;
	}
}

/*
===============
CL_RainParticles
===============
*/
void CL_RainParticles (int type, cpvolume_t *volume, vec3_t p1, vec3_t p2)
{
	int			i, count;
	cparticle_t	*p;
	int		width, depth, height;
	int		rand_w, rand_d, rand_h;
	float	v_time, h_time;
	vec3_t	velocity;

	velocity[0] = 0;
	count = 1;

	width = (int) p2[0] - p1[0];
	depth = (int) p2[1] - p1[1];
	height = (int) p2[2] - p1[2];

	velocity [2] = -400;
	velocity[1] = 300;

	//	find a new way to send these flags, or make CL_ParseEntity handle 
	//	snow and rain differently, because these flags are conflicting with
	//	flags for regular entities and causing things like discus trails to
	//	be generated for no reason
/*
	if (type & PV_RAIN_SLOW)
		velocity[2] = -200;
	else if (type & PV_RAIN_FAST)
		velocity[2] = -400;

	if (type & PV_RAIN_0)
		velocity[1] = 0;
	else if (type & PV_RAIN_45)
		velocity[1] = 300;
*/

	//	a 256x256x256 cube will have a density of 13.1 particles
	volume->pv_max = (width * depth) / 5000;

	if (volume->pv_active >= volume->pv_max)
		return;

	for (i=0 ; i < count ; i++)
	{
		rand_w = p1[0] + ( (rand() % width ) - 8); 
		rand_d = p1[1] + ( (rand() % depth ) - 8); 
		rand_h = p1[2] + ( (rand() % height ) - 8);

//		for (j = 0; j < 16; j++)
		{
			if (!free_comparticles)
				return;
			p = free_comparticles;
			free_comparticles = p->next;
			p->next = active_comparticles;
			active_comparticles = p;

			p->color = 7 + (rand()&7);

			p->org[0] = rand_w;
			p->org[1] = rand_d;
			p->org[2] = rand_h;// - j * 1.5;

			p->vel[0] = velocity[0];
			p->vel[1] = velocity[1];
			p->vel[2] = velocity[2];

 			v_time = (p1[2] - p->org[2]) / p->vel[2];
 			h_time = (p1[1] - p->org[1]) / p->vel[1];
			p->time = cl.time + v_time;

			//	no acceleration via gravity
			p->accel [0] = p->accel [1] = p->accel [2] = 0;

			p->alpha = 1;
			p->alphavel = -1;

			//	Nelno: added for testing complex particles
			p->volume = volume;
			p->volume->pv_active++;

			//	Nelno: added for testing complex particles
			p->type = PARTICLE_RAIN;
		}
	}
}

//////////////////////////////////////////////////////////
//
//	PARTICLE VOLUME MANAGEMENT
//
//////////////////////////////////////////////////////////

/*
===============
CL_AllocParticleVolume
===============
*/
cpvolume_t *CL_AllocParticleVolume (int key)
{
	int			i;
	cpvolume_t	*pv = NULL;

/*
	// if the entity has already been alloc'd, send it
	for (i = 0; i < cl_numpvolumes; i++)
	{
		if (cl_pvolumes[i].key == key)
			return &cl_pvolumes[i];
	}
	
	// make sure we can allocate a new particle volume
	if (cl_numpvolumes == MAX_PVOLUMES || pv)
		return NULL;


	// otherwise set the key send a new one
	pv = &cl_pvolumes[cl_numpvolumes++];

	pv->key = key;
*/

	// if the entity has already been alloc'd, send it
	for (i = 0; i < MAX_PVOLUMES; i++)
	{
		if (cl_pvolumes[i].key == key)
			return &cl_pvolumes[i];
		if (!pv && cl_pvolumes [i].key == 0x00)
		{
			//	key == 0 means this is a free volume, so allocate it
			cl_numpvolumes++;
			pv = &cl_pvolumes [i];
		}
	}

	if (pv)
		pv->key = key;
	//	find a free volume
	return pv;
}

/*
===============
CL_AddParticleVolume
===============
*/
void CL_AddParticleVolume (int key, int type, vec3_t mins, vec3_t maxs)
{
	cpvolume_t	*pv;
	
	if (!cl_particlevol || !cl_particlevol->value)
		return;

	pv = CL_AllocParticleVolume (key);

	if (pv == NULL)
		return;

	if (type & RF_SNOW)
		CL_SnowParticles (type, pv, mins, maxs);
	else if (type & RF_SNOW)
		CL_RainParticles (type, pv, mins, maxs);
}

/*
===============
CL_ClipParticleVolume
===============
*/
int CL_ClipParticleVolume (vec3_t mins, vec3_t maxs)
{
	int		i;
	vec3_t	origin, end, distance;

	// copy the view origin 
	VectorCopy (cl.refdef.vieworg, origin);
	origin [2] = 0;

	// get vector for mid point
	for (i = 0; i < 3; i++)
		end[i] = ((maxs[i] + mins[i]) / 2);
	
	// clear z
	end[2] = 0;

	// get length vector
	VectorSubtract (origin, end, distance);

	if (VectorLength (distance) < PV_CLIP_DISTANCE)
		return false;	// pvolume is not clipped

	//	clip against all 4 corners of the bounding box, too
	//	it's 4 corners because we're zeroing the z value, so
	//	it is as if the box is totally flat in the z dimension
	//	this handles larger particle volumes a bit better

	VectorSet (end, maxs [0], maxs [1], 0);
	VectorSubtract (origin, end, distance);
	if (VectorLength (distance) < PV_CLIP_DISTANCE)
		return	false;

	VectorSet (end, mins [0], maxs [1], 0);
	VectorSubtract (origin, end, distance);
	if (VectorLength (distance) < PV_CLIP_DISTANCE)
		return	false;

	VectorSet (end, maxs [0], mins [1], 0);
	VectorSubtract (origin, end, distance);
	if (VectorLength (distance) < PV_CLIP_DISTANCE)
		return	false;

	VectorSet (end, mins [0], mins [1], 0);
	VectorSubtract (origin, end, distance);
	if (VectorLength (distance) < PV_CLIP_DISTANCE)
		return	false;

	return true;
}

/*
===============
CL_ParticleVolume
===============
*/
void CL_ParticleVolume (int key, int type, vec3_t mins, vec3_t maxs)
{
	if (!CL_ClipParticleVolume (mins, maxs))
		CL_AddParticleVolume (key, type, mins, maxs);
}

void CL_RemoveParticleVolume (int key)
{
	cpvolume_t	*pv = NULL;
	int			i;

	//	search through particle volumes to find the one with a matching key
	for (i = 0; i < MAX_PVOLUMES; i++)
	{
		if (cl_pvolumes [i].key == key)
		{
			pv = &cl_pvolumes [i];
			break;
		}
	}

	if (!pv)
		return;

	//	remove all complex particles that are associated with this volume
	memset (pv, 0, sizeof (cpvolume_t));
	cl_numpvolumes--;
//	Com_Printf ("removed PVolume.\n");
}

/*
===============
CL_ClearPVolumes
===============
*/
void CL_ClearPVolumes (void)
{
	memset (cl_pvolumes, 0, sizeof(cl_pvolumes));
	cl_numpvolumes = 0;
}
