/**********************************************************************

	Title :			Particle Module 
	Filename :		particle.c
	Author :		Thomas Szirtes
	Revision :		1.0

	Notes :			Particle System module - contains generators,
					graphics, animation and forces

	Copyright :		(C) SEGA Europe 1998

  **********************************************************************/


/*
 *	Includes
 */
#include    "ftypes.h"
#include	<ninja.h>
#ifndef USING_SET4
#include	<malloc.h>
#endif
#include	"future.h"
#include	"particle.h"
#include	"tools.h"

/*
 *	External References
 */


/*
 *	Module Variables
 */
static 	NJS_SPRITE	sprite;



/*----------------------------- PRIVATE FUNCTIONS ----------------------------------// */

/*--------------------------------------------------------------------------

  void genparticle( PSYSTEM *ps)

  Parameters:
 	ps		pointer to particle system
 
  Returns:
 
  Notes:
 	subroutine for generating particles
 
  --------------------------------------------------------------------------*/
static void genparticle( PSYSTEM *ps)
{
	PGENERATOR	*pg;
	PARTICLE	*p;
	float		rnd1,rnd2;

	/* if counter down generate particle */
	pg	=	ps->pg;

	
	p			=	&pg->p[pg->pfront];		/* new particle */
	if (p->age > 0) return;

	rnd1			=	((njRandom()*360.f)-180.0f) * ps->random;
	rnd2			=	((njRandom()*360.f)-180.0f) * ps->random;

	p->age		=	ps->lifespan;			/* reset lifespan */
	p->vel.x	=	njSin(NJM_DEG_ANG(ps->azi+rnd1)) * ps->speed;
	p->vel.y	=	njSin(NJM_DEG_ANG(ps->elv+rnd2)) * ps->speed;
	p->vel.z	=	njCos(NJM_DEG_ANG(ps->azi+rnd1)) * njCos(NJM_DEG_ANG(ps->elv+rnd2)) * ps->speed;

	if(ps->pgfx->flags&PG_FLAG_RELATIVE)
	{
		p->pos.x	=	0.f;
		p->pos.y	=	0.f;
		p->pos.z	=	0.f;
	}
	else
	{
		p->pos.x	=	ps->pos.x;
		p->pos.y	=	ps->pos.y;
		p->pos.z	=	ps->pos.z;
	}

	pg->pfront++;

	if(pg->pfront >= pg->listsize)			/* list size  */
		pg->pfront=0; 
}

/*----------------------------- PUBLIC FUNCTIONS ----------------------------------// */


/*--------------------------------------------------------------------------

  void PARTICLE_Create( PSYSTEM *ps )
 
  Parameters:
 	ps		pointer to particle system
 
  Returns:
 
  Notes:
 	Initializes particle system
 
  --------------------------------------------------------------------------*/
void PARTICLE_Create( PSYSTEM *ps )
{
	int		n;
	PGAUTO	*pgauto;

#ifdef USING_SET4
	ps->pg			=	(PGENERATOR	*)syMalloc(sizeof(PGENERATOR));					/* allocate memory for generator */
#else
	ps->pg			=	(PGENERATOR	*)malloc(sizeof(PGENERATOR));					/* allocate memory for generator */
#endif
	ps->pg->listsize=	ps->maxparticle;							/* setup max list */
	ps->pg->frame	=	0;											/* reset frame */
	ps->pg->pfront	=	0;											/* reset index */
#ifdef USING_SET4
	ps->pg->p		=	(PARTICLE *)syMalloc(sizeof(PARTICLE)*ps->maxparticle);	/* allocate memory for particle ring buffer */
#else
	ps->pg->p		=	(PARTICLE *)malloc(sizeof(PARTICLE)*ps->maxparticle);	/* allocate memory for particle ring buffer */
#endif
	for(n=0;n<ps->maxparticle;n++)
		ps->pg->p[n].age	=	0;									/* reset ages of particles */


	/* auto or table */
	if (ps->pgfx->flags&PG_FLAG_TABLE)
	{
		/* not implemented yet */
	}
	else
	{
		pgauto	=	(PGAUTO *)ps->pgfx->pchg;
		pgauto->color_step.a	=	(pgauto->dcolor.a - pgauto->bcolor.a) / (float)ps->lifespan;
		pgauto->color_step.r	=	(pgauto->dcolor.r - pgauto->bcolor.r) / (float)ps->lifespan;
		pgauto->color_step.g	=	(pgauto->dcolor.g - pgauto->bcolor.g) / (float)ps->lifespan;
		pgauto->color_step.b	=	(pgauto->dcolor.b - pgauto->bcolor.b) / (float)ps->lifespan;
		pgauto->size_step		=	(pgauto->dsize    - pgauto->bsize   ) / (float)ps->lifespan;
	}
}


/*--------------------------------------------------------------------------

  void PARTICLE_MoveGenerator( PSYSTEM *ps )
 
  Parameters:
 	ps		pointer to particle system
 
  Returns:
 
  Notes:
	Moves generator to desired position
 
  --------------------------------------------------------------------------*/
void PARTICLE_MoveGenerator( PSYSTEM *ps, NJS_VECTOR pos )
{
	ps->pos		=	pos;

}


/*--------------------------------------------------------------------------

  void PARTICLE_Generator( PSYSTEM *ps )
 
  Parameters:
 	ps		pointer to particle system
 
  Returns:
 
  Notes:
	Generates new particles from specified generator
 
  --------------------------------------------------------------------------*/
void PARTICLE_Generator( PSYSTEM *ps )
{
	PGENERATOR	*pg;
	int			n;

	pg	=	ps->pg;


	if(ps->rate>0)
	{
		for(n=0;n<ps->rate;n++)
		{
			genparticle(ps);
		}
	}
	else
	{
		if (pg->frame==0)
		{
			genparticle(ps);
			pg->frame	=	-ps->rate;				/* refresh counter */
		}	
		pg->frame--;
	}

}


/*--------------------------------------------------------------------------

  void PARTICLE_Update( PGENERATOR *pg )
 
  Parameters:
	pg		pointer to particle generator 
 
  Returns:
 
  Notes:
	Updates all live particles belonging to generator
 
  --------------------------------------------------------------------------*/
void PARTICLE_Update( PSYSTEM *ps )
{
	int			n;
	PARTICLE	*p;
	PGENERATOR	*pg;
	PGRAPHIC	*pgfx;
	PGAUTO		*pgauto;
	PFORCE		*force;
	NJS_ARGB	argb;
	
	
	pg				=	ps->pg;
	pgfx			=	ps->pgfx;
	sprite.tlist	=	pgfx->texlist;
	sprite.tanim	=	pgfx->texanim;
	sprite.ang		=	0;
	argb.r			=	1.0f;
	argb.g			=	1.0f;
	argb.b			=	1.0f;

	if (!(pgfx->flags&PG_FLAG_TABLE)) pgauto = (PGAUTO *)pgfx->pchg;

	/* First order physics - nothing fancy here  */
	/* Add velocities */
	for(n=0;n<pg->listsize;n++)
	{
		p	=	&pg->p[n];
		/* check particle is not dead */
		if (p->age!=0)
		{
			/* process particles */
			p->pos.x	+=	p->vel.x;
			p->pos.y	+=	p->vel.y;
			p->pos.z	+=	p->vel.z;
		}
	}

	/* is there weight - in which case add acceleration */
	if (ps->weight!=0.0f)
	{
		for(n=0;n<pg->listsize;n++)
		{
			p	=	&pg->p[n];
			p->vel.y	-=	ps->weight;
		}
	}

	/* Calculate winds - work out some way of reducing calculations... */
	/* and also must reduce floating point unit load throughout application... */
	/* ... will concuct some fixed point stuff... */
	force	=	ps->force;
	if (force!=NULL)
	{
		/* recalculate vector */
		force->vel.x	=	njSin(NJM_DEG_ANG(force->azi)) * force->speed;
		force->vel.y	=	njSin(NJM_DEG_ANG(force->elv)) * force->speed;
		force->vel.z	=	njCos(NJM_DEG_ANG(force->azi)) * force->speed;
		do 
		{
			for(n=0;n<pg->listsize;n++)
			{
				p	=	&pg->p[n];
				p->pos.x	+=	force->vel.x;
				p->pos.y	+=	force->vel.y;
				p->pos.z	+=	force->vel.z;
			}
		} while((force=force->next)!=NULL);
	}


	/* Display */
	/* ABSOLUTE OR RELATIVE MODE? */
	/* absolute mode positions particles in absolute world space - good for slow moving generators */
	/* relative mode positions particles relative to generator - better for fast moving generators */
	if (pgfx->flags&PG_FLAG_RELATIVE)
	{
		for(n=0;n<pg->listsize;n++)
		{
			p	=	&pg->p[n];
			/* check particle is not dead */
			if (p->age!=0)
			{
				p->age--;
			
				sprite.p.x	=	ps->pos.x + p->pos.x; 
				sprite.p.y	=	ps->pos.y + p->pos.y; 
				sprite.p.z	=	ps->pos.z + p->pos.z; 

				if (ps->pgfx->flags&PG_FLAG_TABLE)
				{
				}
				else
				{
					sprite.sx		=	sprite.sy	=	pgauto->dsize - (pgauto->size_step * p->age);
					argb.a							=	pgauto->dcolor.a - (pgauto->color_step.a * p->age);
					argb.r							=	pgauto->dcolor.r - (pgauto->color_step.r * p->age);
					argb.g							=	pgauto->dcolor.g - (pgauto->color_step.g * p->age);
					argb.b							=	pgauto->dcolor.b - (pgauto->color_step.b * p->age);
					njSetConstantMaterial	( &argb );
					if (pgfx->animtab==NULL)
						njDrawSprite3D	( &sprite, 0, NJD_SPRITE_ANGLE | NJD_SPRITE_SCALE | NJD_SPRITE_COLOR | NJD_SPRITE_ALPHA );
					else
						njDrawSprite3D	( &sprite, pgfx->animtab[p->age], NJD_SPRITE_ANGLE | NJD_SPRITE_SCALE | NJD_SPRITE_COLOR | NJD_SPRITE_ALPHA );

				}
			}
		}
	}
	else
	{
		for(n=0;n<pg->listsize;n++)
		{
			p	=	&pg->p[n];
			/* check particle is not dead */
			if (p->age!=0)
			{
				/* process particles */
				p->pos.x	+=	p->vel.x;
				p->pos.y	+=	p->vel.y;
				p->pos.z	+=	p->vel.z;
				p->age--;
			
				sprite.p.x		=	p->pos.x; 
				sprite.p.y		=	p->pos.y; 
				sprite.p.z		=	p->pos.z; 

				if (ps->pgfx->flags&PG_FLAG_TABLE)
				{
					/* draw particles */
					/*sprite.sx		=	sprite.sy	=	pchg->size; */
					/*njSetConstantMaterial	( &pchg->argb ); */
					/*njDrawSprite3D	( &sprite, 0, NJD_SPRITE_ANGLE | NJD_SPRITE_SCALE | NJD_SPRITE_COLOR | NJD_SPRITE_ALPHA ); */
				}
				else
				{
					sprite.sx		=	sprite.sy	=	pgauto->dsize - (pgauto->size_step * p->age);
					argb.a							=	pgauto->dcolor.a - (pgauto->color_step.a * p->age);
					argb.r							=	pgauto->dcolor.r - (pgauto->color_step.r * p->age);
					argb.g							=	pgauto->dcolor.g - (pgauto->color_step.g * p->age);
					argb.b							=	pgauto->dcolor.b - (pgauto->color_step.b * p->age);
					njSetConstantMaterial	( &argb );
					if (pgfx->animtab==NULL)
						njDrawSprite3D	( &sprite, 0, NJD_SPRITE_ANGLE | NJD_SPRITE_SCALE | NJD_SPRITE_COLOR | NJD_SPRITE_ALPHA );
					else
						njDrawSprite3D	( &sprite, pgfx->animtab[p->age], NJD_SPRITE_ANGLE | NJD_SPRITE_SCALE | NJD_SPRITE_COLOR | NJD_SPRITE_ALPHA );
				}
			}
		}
	}
}


/*--------------------------------------------------------------------------

  void PARTICLE_Kill( PSYSTEM *ps )

  Parameters:
	ps		pointer to particle system 
 
  Returns:
 
  Notes:
	Kills system
 
  --------------------------------------------------------------------------*/

void PARTICLE_Kill( PSYSTEM *ps )
{
/*	free (ps->pg->p); */
#ifdef USING_SET4
	if (ps->pg != NULL ) 
	{
		if (ps->pg->p != NULL) syFree (ps->pg->p);
		syFree (ps->pg);
	}
#else
	if (ps->pg != NULL ) 
	{
		if (ps->pg->p != NULL) free (ps->pg->p);
		free (ps->pg);
	}
#endif
}




/*--------------------------------------------------------------------------

  void PDATABASE_AnalysisMap( void )

  Parameters:
 
  Returns:
 
  Notes:
	Counts up all smoke generators - which are basically a special case of
	a building
  
  --------------------------------------------------------------------------*/


void	PDATABASE_AnalysisMap( PDATABASE *pd )
{
	int		i;
	int		count = 0;


	/* First count number of chimneys in map */
	for	( i=0; i<instance_count; i++ )
	{
		if( buildings[build_inst[i].id].particletemplate != NULL && build_inst[i].id!=0xff)	count++;
	}

	if (count==0)
	{
		pd->numitems	=	count;
		return;
	}

	/* Allocate memory for a database */
#ifdef	USING_SET4
	pd->systems	=	syMalloc( sizeof(PSYSTEM *) * count );
#else
	pd->systems	=	malloc( sizeof(PSYSTEM *) * count );
#endif

	pd->numitems	=	count;

	/* Generate our particle systems */
	for( i=0; i<instance_count; i++)
	{
		if( buildings[build_inst[i].id].particletemplate != NULL && build_inst[i].id!=0xff)	
		{	
			count--;
			/* Make a copy of system */
			pd->systems[count]	=	malloc(sizeof(PSYSTEM));
			memcpy (pd->systems[count], buildings[build_inst[i].id].particletemplate, sizeof(PSYSTEM) );
			/* Set its position */
			pd->systems[count]->pos.x += build_inst[i].i*GRID_SIZE;
			pd->systems[count]->pos.z += build_inst[i].j*GRID_SIZE;
			pd->systems[count]->pos.y += (float)map.map[build_inst[i].i][build_inst[i].j].gnd+40.f;
			/* Make a generator from the system */
			PARTICLE_Create( pd->systems[count] );
			/* Save index back into building instance for quick reference */
			build_inst[i].particleinst	=	count;		
		}
	}

}


/*--------------------------------------------------------------------------

  void	PDATABASE_Kill	(void)

  Parameters:
 
  Returns:
 
  Notes:

  --------------------------------------------------------------------------*/

void	PDATABASE_Kill	( PDATABASE *pd )
{
	int	i;

	for(i=0;i<pd->numitems;i++)
	{
		PARTICLE_Kill ( pd->systems[i] );
#ifdef	USING_SET4
		syFree( pd->systems[i] );
#else
		free( pd->systems[i] );
#endif
		pd->numitems--;
	}

#ifdef	USING_SET4
		syFree( pd->systems );
#else
		free ( pd->systems );
#endif
}