/******************************************************************************************
	CRTEST PS   (c) 1999-2001 ISL

	fx.c			- Particle systems and other type of groovy FX
******************************************************************************************/

#include "main.h"
#include "fx.h"
#include "lights.h"
#include "timer.h"
#include "maths.h"
#include "overlays.h"
#include "debris.h"
#include "map_draw.h"


// ========
// ==== Create the particle system
// ========
int particleSys_Init(FX_PARTICLE_SYS *partSys, int flags, int num, int maxLife, TextureType *particleTexture)
{
	partSys->numParticles = num;							// Set the number of particles
	partSys->originX = 0;									// Set the center of the system
	partSys->originY = 0;									//
	partSys->originZ = 0;									//
	partSys->particleTexture = particleTexture;				// Set the texture to use (if any)
	partSys->numTextures=0;									// 
	partSys->maxLife = maxLife;								// Max age of a particle
	partSys->flags = flags;
	partSys->spriteScale = 256;								// CPW: set scale to 1:1

	partSys->particles = MALLOC(sizeof(FX_PARTICLE) * num);	// Allocate the mem. for the particles
	if (partSys->particles ==NULL)							// Check that we alloc'd the memory okay
		return 0;

	return 1;

} // particleSys_Create


// ========
// ==== Destroy the particle system
// ========
int particleSys_Destroy(FX_PARTICLE_SYS *partSys)
{
	if (partSys->particles == NULL || partSys->numParticles == 0)
		return 0;

	FREE(partSys->particles);
	partSys->numParticles = 0;
	return 1;
} // particleSys_Destroy




// ========
// ==== Generate the particle System
// ========
int particleSys_Generate(FX_PARTICLE_SYS *partSys, int xzRange, int yVelocity)
{
	int i;
	FX_PARTICLE *p;
	int currAngle=0, addAngle;
	VECTOR v;
	int baseCol = 0;

	ASSERT(partSys);

	if(partSys->flags & PARTICLE_CUSTOM_GENERATE)
	{
		if (partSys->customGenerate)
			partSys->customGenerate(partSys, xzRange, yVelocity);
		return 1;
	}

	utilSeedRandomInt(0xf46c210+frame);
	addAngle = 4096 / partSys->numParticles;

	partSys->numDead = 0;

	if(partSys->flags & PARTICLE_RAINBOW)
	{
		baseCol = utilRandomInt(6);
	}

	for(i=partSys->numParticles; i > 0; i--)
	{
		p = &partSys->particles[partSys->numParticles-i];

		p->dead = 0;

		if(partSys->flags & PARTICLE_SPHERICAL)
		{
			v.vx = 2047 - utilRandomInt(4095);
			v.vy = 2047 - utilRandomInt(4095);
			v.vz = 2047 - utilRandomInt(4095);

			MakeUnit(&v);

			p->bornVelX = (v.vx * xzRange) / 4096;
			p->bornVelY = (v.vy * xzRange) / 4096;
			p->bornVelZ = (v.vz * xzRange) / 4096;

			// CPW: DB("%d, %d, %d\n", p->bornVelX, p->bornVelY, p->bornVelZ);

			if(partSys->flags & PARTICLE_RAINBOW)
				p->colour = effectsGetPrimary(baseCol, utilRandomInt(255), 32) & 0xffffff;
		}
		else
		{
			v.vx=0;
			v.vy=0;
			v.vz=utilRandomInt(xzRange);

			RotateVector2D(&v, &v, currAngle);
			(currAngle += addAngle) & 4095;

			if(partSys->flags & PARTICLE_CHILD)
			{
				p->bornX = p->posX = 0;
				p->bornY = p->posY = 0;
				p->bornZ = p->posZ = 0;
			}
			else
			{
				p->posX = partSys->originX;
				p->posY = partSys->originY;
				p->posZ = partSys->originZ;
			} // end-if-else
	
			p->bornVelX = p->velX = v.vx;
			p->bornVelY = p->velY = yVelocity;
			p->bornVelZ = p->velZ = v.vz;
		}

		if(partSys->flags & PARTICLE_RANDOMLIFE)
			p->lifeTime = utilRandomInt(partSys->maxLife);
		else
			p->lifeTime = partSys->maxLife;

		p->age = 0;

	} // end-for

	return 1;
}


// ========
// ==== Update the particle System
// ========
int particleSys_Update(FX_PARTICLE_SYS *partSys)
{
int i;
FX_PARTICLE *p;

	ASSERT(partSys);

	if(partSys->flags & PARTICLE_CUSTOM_UPDATE)
	{
		if (partSys->customUpdate)
			partSys->customUpdate(partSys);
		return 1;
	}

	// hex = 0...5, dist = 0...255, pastel = 0...127
	
	for(i=partSys->numParticles; i > 0; i--)
	{
		p = &partSys->particles[partSys->numParticles-i];

		if(partSys->flags & PARTICLE_RAINBOW)
		{
			int f=frame<<3;

			p->colour = effectsGetPrimary((f>>8) % 6, f & 255, 0) & 0xffffff;
			// CPW: DB("New sparkle colour = %08x, %d\n", p->colour, frame);
		}
		else
			p->colour = 0x808080;


		if (p->age < p->lifeTime)
		{
			p->posX += p->velX;
			p->posY -= p->velY;
			p->posZ += p->velZ;

			// Affect the particles Y-Velocity if the gravity flag is set
			if (partSys->flags & PARTICLE_GRAVITY)
				p->velY -= 2;

			p->age++;
		}
		else
		{
			if(!(partSys->flags & PARTICLE_REINCARNATION))
			{
				p->dead = TRUE;
				partSys->numDead++;
			}

			if(partSys->flags & PARTICLE_CHILD)
			{
				p->posX = 0;
				p->posY = 0;
				p->posZ = 0;
			}
			else
			{
				p->posX = partSys->originX;
				p->posY = partSys->originY;
				p->posZ = partSys->originZ;
			}

			// Only need to reset the velocity if it's affected 
			if(partSys->flags & PARTICLE_GRAVITY)
			{
				p->velX = p->bornVelX;
				p->velY = p->bornVelY;
				p->velZ = p->bornVelZ;
			} // end-if

			p->age = 0;

		} // end-if-else
	} // end-for

	DB("Dead = %d/%d\n", partSys->numDead, partSys->numParticles);
	if((!(partSys->flags & PARTICLE_REINCARNATION)) && (partSys->numDead==partSys->numParticles))
	{
		DB("Finished!\n");
		return 0;
	}
	return 1;
}



// ========
// ==== Draw the particle System
// ========
int particleSys_Draw(FX_PARTICLE_SYS *partSys)
{
int i;
FX_PARTICLE *p;
VERT v;

	ASSERT(partSys);

	if (partSys->flags & PARTICLE_CUSTOM_DRAW)
	{
		if (partSys->customDraw)
			partSys->customDraw(partSys);
		return 1;
	}

	gte_SetRotMatrix(&GsWSMATRIX);
	gte_SetTransMatrix(&GsWSMATRIX);

	for(i=partSys->numParticles; i > 0; i--)
	{
		p = &partSys->particles[partSys->numParticles-i];	// Get pointer to current particle

		if (partSys->flags & PARTICLE_CHILD)
		{
			v.vx = ((partSys->originX - mapCamData.gameCam.vpx) + p->posX);		// 
			v.vy = ((partSys->originY - mapCamData.gameCam.vpy) + p->posY);		// 
			v.vz = ((partSys->originZ - mapCamData.gameCam.vpz) + p->posZ);		// 
		} else {
			v.vx = p->posX - mapCamData.gameCam.vpx;		// 
			v.vy = p->posY - mapCamData.gameCam.vpy;		// 
			v.vz = p->posZ - mapCamData.gameCam.vpz;		// 
		} // end-if-else

		if(p->dead == 0)
		{
			if(partSys->flags & PARTICLE_ADDITIVE)
				Print3DSelfAlphaSprite(partSys->particleTexture, &v,partSys->spriteScale, /*((128-p->age) << 24) | 0x808080*/ p->colour);
			else
				Print3DSprite(partSys->particleTexture, &v,partSys->spriteScale, /*((128-p->age) << 24) | 0x808080*/ p->colour);
		}

	} // end-for

	return 1;
}
