/***************************************************************************
    copyright            : (C) 2003 by Michael Speck
    email                : http://lgames.sf.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "particle.h"

Particle particles[MAX_PARTICLES];
int particle_count = 0;
Emitter emitters[MAX_EMITTERS];
int emitter_count = 0;

extern SDL_Surface *img_particles;

static Vector wind = { -0.005, 0, 0 };
static Vector grav = { 0, 0, -GRAV };


static void particle_add( Particle *particle )
{
    if (particle_count >= MAX_PARTICLES) return;
    
    particles[particle_count] = *particle;
    particle_count++;
}

static void particle_delete( int index )
{
    if (particle_count == 0) return;
    
    particles[index] = particles[particle_count-1];
    particle_count--;
}

static void emitter_delete( int index )
{
    if (emitter_count == 0) return;
    
    emitters[index] = emitters[emitter_count-1];
    emitter_count--;
}

void particles_clear()
{
	emitter_count = 0;
	particle_count = 0;
}

void particles_draw( SDL_Surface *dest, int camera_x, int camera_y )
{
	Particle *part;
	SDL_Rect srect, drect;
	int i;

	for (i = 0; i < particle_count; i++) {
		part = &particles[i];
		
		srect.x = part->img_x_offset + part->frame_id*part->w;
		srect.y = part->img_y_offset;
		drect.x = (int)part->pos.x - camera_x;
		drect.y = (int)part->pos.y - ((int)part->pos.z>>1) - camera_y;
		srect.w = drect.w = part->w;
		srect.h = drect.h = part->h;

		/* add in camera check? */
		
		SDL_SetAlpha( part->img, SDL_SRCALPHA | SDL_RLEACCEL, (int)part->alpha );
		
		SDL_BlitSurface( part->img, &srect, dest, &drect );
	}
}

void particles_update( int ms )
{
	Emitter *emit;
	Particle *part;
	int i, dens;

	/* emitters */
	for ( i = 0; i < emitter_count; i++ ) {
		emit = &emitters[i];

		emit->count += emit->rate * ms;
		dens = emit->count;
		if ( dens > 0 ) {
			emit->count -= dens;
			switch (emit->type ) {
				case PT_FIRE:
					particles_set_fire( emit->x, emit->y, emit->radius, dens );
					break;
				case PT_SMOKE:
					particles_set_smoke( emit->x, emit->y, emit->radius, dens );
					break;
			}
		}

		if ( emit->lifespan > 0 ) {
			emit->lifespan -= ms;
			if ( emit->lifespan <= 0 ) {
				emitter_delete( i ); 
				i--; /* last tile is copied to this one's position */
			}
		}
	}
	
	/* particles */
	for (i = 0; i < particle_count; i++) {
		part = &particles[i];
	
		if ( part->type != PT_EXPLOSION && part->type != PT_FIRE  ) {
			part->alpha += part->alpha_change * ms;
			if ( part->alpha < 32.0 ) {
				particle_delete( i );
				i--; /* last tile is copied to this one's position */
				continue;
			}
		}

		part->pos.x += part->dir.x * ms;
		part->pos.y += part->dir.y * ms;
		part->pos.z += part->dir.z * ms;

		if ( part->frame_id < part->frame_count-1 ) {
			part->frame += part->frame_change * ms;
			part->frame_id = part->frame;
			if ( part->frame_id >= part->frame_count-1 ) {
				part->frame_id = part->frame_count-1;
				/* explosion turns to smoke */
				if ( part->type == PT_EXPLOSION ) {
					part->dir.x = (double)RAND(-10,10)/1000;
					part->dir.y = (double)RAND(-10,10)/1000;
					part->dir.z = 0.0;
					part->type = PT_SMOKE;
				}
				/* fire turns to smoke */
				if ( part->type == PT_FIRE )
					part->type = PT_SMOKE;
			}
		}

		if ( part->wind )
		if ( part->type != PT_WASTE )
		if ( part->type != PT_EXPLOSION ) {
			part->pos.x += part->wind->x * ms;
			part->pos.y += part->wind->y * ms;
			part->pos.z += part->wind->z * ms;
		}
		if ( part->grav && part->type == PT_BALLISTIC ) {
			part->dir.x += part->grav->x * ms;
			part->dir.y += part->grav->y * ms;
			part->dir.z += part->grav->z * ms;
		}

		if ( part->type == PT_BALLISTIC && part->pos.z <= 0 ) {
			part->dir.x = part->dir.y = part->dir.z = 0;
			part->alpha_change = -0.20;
			part->type = PT_WASTE;
		}

	}
}

static void vector_scale( Vector *vector, double scale )
{
	double norm;
	norm = sqrt(vector->x*vector->x + vector->y*vector->y + vector->z*vector->z);
	norm /= scale;
	vector->x /= norm; vector->y /= norm; vector->z /= norm;

}

void particles_explode_bomb( int x, int y )
{
	int i, yoff;
	Particle part;
	double energy;
	
	memset( &part, 0, sizeof( part ) );
	part.type = PT_BALLISTIC;
	part.img = img_particles;
	part.frame_count = 1;
	part.alpha = 200;
	part.grav = &grav;
	part.wind = &wind;
	part.pos.x = x;
	part.pos.y = y;
	for ( energy = 0.2, i = 0; i < 40; i++, energy += 0.01 ) {
		part.w = part.h = 4;
		part.dir.x = (double)RAND(-12,12);
		part.dir.y = (double)RAND(-12,12);
		part.dir.z = (double)RAND(100,400);
		vector_scale( &part.dir, energy );
		particle_add( &part );
	}
	for ( energy = 0.2, i = 0; i < 100; i++, energy += 0.004 ) {
		part.w = part.h = 2;
		part.img_x_offset = 4;
		part.dir.x = (double)RAND(-10,10);
		part.dir.y = (double)RAND(-10,10);
		part.dir.z = (double)RAND(100,400);
		vector_scale( &part.dir, energy );
		particle_add( &part );
	}
	
	memset( &part, 0, sizeof( part ) );
	part.type = PT_EXPLOSION;
	part.img = img_particles;
	part.frame_count = 8;
	part.frame_change = 0.1;
	part.alpha = 200;
	part.alpha_change = -0.2;
	part.wind = &wind;
	part.pos.x = x;
	part.pos.y = y;
	part.w = part.h = 6;
	for ( yoff = 0, energy = 0.4, i = 0; i < 100; i++, energy += 0.002, yoff += 6 ) {
		if ( yoff == 18 ) yoff = 0;
		part.img_y_offset = 18 + yoff;
		part.dir.x = (double)RAND(-80,80);
		part.dir.y = (double)RAND(-80,80);
		part.dir.z = (double)RAND(200,500);
		vector_scale( &part.dir, energy );
		particle_add( &part );
	}
}

void particles_explode_clusterbomb( int x, int y )
{
	int i, yoff;
	Particle part;
	
	memset( &part, 0, sizeof( part ) );
	part.type = PT_EXPLOSION;
	part.img = img_particles;
	part.frame_count = 8;
	part.frame_change = 0.1;
	part.alpha = 200;
	part.alpha_change = -0.2;
	part.wind = &wind;
	part.pos.x = x;
	part.pos.y = y;
	part.w = part.h = 6;
	for ( yoff = 0, i = 0; i < 100; i++, yoff += 6 ) {
		if ( yoff == 18 ) yoff = 0;
		part.img_y_offset = 18 + yoff;
		part.dir.x = (double)RAND(-80,80);
		part.dir.y = (double)RAND(-80,80);
		part.dir.z = (double)RAND(200,500);
		vector_scale( &part.dir, 0.4 );
		particle_add( &part );
	}
}

void particles_explode_grenade( int x, int y )
{
	int i;
	Particle part;
	double energy;
	
	memset( &part, 0, sizeof( part ) );
	part.type = PT_BALLISTIC;
	part.img = img_particles;
	part.frame_count = 1;
	part.alpha = 200;
	part.grav = &grav;
	part.wind = &wind;
	part.pos.x = x;
	part.pos.y = y;
	for ( energy = 0.2, i = 0; i < 10; i++, energy += 0.02 ) {
		part.w = part.h = 4;
		part.dir.x = (double)RAND(-10,10);
		part.dir.y = (double)RAND(-10,10);
		part.dir.z = (double)RAND(100,400);
		vector_scale( &part.dir, energy );
		particle_add( &part );
	}
	for ( energy = 0.2, i = 0; i < 50; i++, energy += 0.004 ) {
		part.w = part.h = 2;
		part.img_x_offset = 4;
		part.dir.x = (double)RAND(-10,10);
		part.dir.y = (double)RAND(-10,10);
		part.dir.z = (double)RAND(100,400);
		vector_scale( &part.dir, energy );
		particle_add( &part );
	}
	
	memset( &part, 0, sizeof( part ) );
	part.type = PT_EXPLOSION;
	part.img = img_particles;
	part.frame_count = 8;
	part.frame_change = 0.1;
	part.alpha = 200;
	part.alpha_change = -0.2;
	part.wind = &wind;
	part.pos.x = x;
	part.pos.y = y;
	part.w = part.h = 4;
	part.img_y_offset = 14;
	for ( energy = 0.2, i = 0; i < 40; i++, energy += 0.002 ) {
		part.dir.x = (double)RAND(-40,40);
		part.dir.y = (double)RAND(-40,40);
		part.dir.z = (double)RAND(200,500);
		vector_scale( &part.dir, energy );
		particle_add( &part );
	}
}

void particles_set_fire( int x, int y, int radius, int density )
{
	int i, frame;
	Particle part;
	double energy;
	
	memset( &part, 0, sizeof( part ) );
	part.type = PT_FIRE;
	part.img = img_particles;
	part.img_y_offset = 6;
	part.w = part.h = 2;
	part.frame_count = 10;
	part.frame_change = 0.01;
	part.alpha = 200;
	part.alpha_change = -0.2;
	part.wind = &wind;
	for ( frame = 0, energy = 0.03, i = 0; i < density; i++, frame++, energy += 0.001 ) {
		if ( frame == 6 ) frame = 0;
		part.frame = frame;
		part.pos.x = x + RAND(-radius, radius);
		part.pos.y = y + RAND(-(radius>>1),(radius>>1));
		part.dir.x = (double)RAND(-20,20);
		part.dir.y = (double)RAND(-20,20);
		part.dir.z = (double)RAND(100,400);
		vector_scale( &part.dir, energy );
		particle_add( &part );
	}
}

void particles_set_smoke( int x, int y, int radius, int density )
{
	int i, xoff;
	Particle part;
	double energy;
	
	memset( &part, 0, sizeof( part ) );
	part.type = PT_SMOKE;
	part.img = img_particles;
	part.img_y_offset = 8;
	part.w = part.h = 6;
	part.frame_count = 1;
	part.alpha = 128;
	part.alpha_change = -0.1;
	part.wind = &wind;
	for ( xoff = 0, energy = 0.04, i = 0; i < density; i++, energy += 0.01, xoff += 6 ) {
		if ( xoff == 18 ) xoff = 0;
		part.img_x_offset = xoff;
		part.pos.x = x + RAND(-radius, radius);
		part.pos.y = y + RAND(-(radius>>1),(radius>>1));
		part.dir.x = (double)RAND(-20,20);
		part.dir.y = (double)RAND(-20,20);
		part.dir.z = (double)RAND(100,400);
		vector_scale( &part.dir, energy );
		particle_add( &part );
	}
}

void particles_set_muzzle_fire( int x, int y, int density )
{
	int i;
	Particle part;
	
	memset( &part, 0, sizeof( part ) );
	part.type = PT_EXPLOSION;
	part.img = img_particles;
	part.frame_count = 8;
	part.frame_change = 0.1;
	part.alpha = 200;
	part.alpha_change = -0.2;
	part.wind = &wind;
	part.pos.x = x;
	part.pos.y = y;
	part.w = part.h = 4;
	part.img_y_offset = 14;
	for ( i = 0; i < density; i++ ) {
		part.dir.x = (double)RAND(-80,80);
		part.dir.y = (double)RAND(-80,80);
		part.dir.z = (double)RAND(200,500);
		vector_scale( &part.dir, 0.2 );
		particle_add( &part );
	}
}

void particles_explode_human( int x, int y, int sx, int sy )
{
	int i;
	double dx = x - sx, dy = y - sy;
	Particle part;
	double energy;

	energy = sqrt(dx*dx+dy*dy); dx /= energy; dy /= energy;
	
	memset( &part, 0, sizeof( part ) );
	part.type = PT_BALLISTIC;
	part.img = img_particles;
	part.img_y_offset = 4;
	part.frame_count = 1;
	part.alpha = 200;
	part.wind = &wind;
	part.grav = &grav;
	for ( energy = 0.1, i = 0; i < 10; i++, energy += 0.01 ) {
		part.w = part.h = 2;
		part.img_x_offset = 1;
		part.pos.x = x + RAND(-2,2);
		part.pos.y = y + RAND(-2,2);
		part.dir.x = dx;
		part.dir.y = dy;
		part.dir.z = 3+ rand() % 3;
		vector_scale( &part.dir, energy );
		particle_add( &part );
	}
	for ( energy = 0.1, i = 0; i < 20; i++, energy += 0.01 ) {
		part.w = part.h = 1;
		part.pos.x = x + RAND(-2,2);
		part.pos.y = y + RAND(-2,2);
		part.dir.x = dx;
		part.dir.y = dy;
		part.dir.z = 3 + rand() % 3;
		vector_scale( &part.dir, energy );
		particle_add( &part );
	}

}

void particles_add_emitter( int type, int x, int y, int radius, double rate, int lifespan )
{
	Emitter emitter;
	
	if ( emitter_count >= MAX_EMITTERS ) return;
	
	memset( &emitter, 0, sizeof( emitter ) );
	emitter.type = type;
	emitter.x = x;
	emitter.y = y;
	emitter.radius = radius;
	emitter.lifespan = lifespan;
	emitter.rate = rate;

	emitters[emitter_count] = emitter; emitter_count++;
}

