/*   Copyright 2005,2006 Paweł Niegowski
*
*    This file is part of Fenrir.
*
*    Fenrir 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.
*
*    Fenrir is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with Fenrir; if not, write to the Free Software
*    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "particle.h"
#include "lookup.h"
#include "palette.h"
#include <math.h>

enum {
 EMITTER_NONE,
 EMITTER_POINT,
 EMITTER_BOX
};

ParticleSystem::ParticleSystem(int _max_particles, int _color, int _trail, int _trail_skip)
: polar_mode(false), polar_radius(10.0), polar_scaling(0.01), active(true), max_particles(_max_particles), last_particle(0), start_color(_color), trail(_trail), trail_skip(_trail_skip)
{
 particles = new Particle[max_particles];
 for(int i=0 ; i < max_particles; i++)
  particles[i].life = 0;
 if(trail)
  trail_particles = new Particle[max_particles*trail];
 for(int i = 0; i < trail; i++)
 {
  trail_last_particle[i] = 0;
  trail_trans[i] = 7 - (int)((float)i  / trail * TRANS_LEVELS);
 }
 p_frame_count = 0;
 emx = emy = 0;
 emitter_type = EMITTER_NONE;
}

ParticleSystem::~ParticleSystem()
{
 delete[] particles;
 if(trail)
  delete[] trail_particles;
}

void ParticleSystem::think()
{
 if(show_delay) { show_delay--; return; }
 for(int i = trail - 2; i >= 0; i--)
 {
  memcpy(trail_particles+i*max_particles+max_particles,trail_particles+i*max_particles,sizeof(Particle)*trail_last_particle[i]);
  trail_last_particle[i+1] = trail_last_particle[i];
 }
 if(trail)
 {
  memcpy(trail_particles,particles,sizeof(Particle)*last_particle);
  trail_last_particle[0] = last_particle;
 }
 if(kill_counter == -1 && active)
 {
  if(emitter_type == EMITTER_POINT)
  {
   p_frame_count += pps;
   for(int i = 0; i < (int)p_frame_count; i++)
    _add_particle(polar_mode?rand()%1000/1000.0 * 6.28:x+emx,polar_mode?0:y+emy,vx + (rand()%100)*randvx/100.0,vy + (rand()%100)*randvy/100.0,start_color,life + rand()%life_rand);
   if(p_frame_count >= 1.0) p_frame_count = 0;
  } else //EMITTER_POINT
  if(emitter_type == EMITTER_BOX)
  {
   p_frame_count += pps;
   for(int i = 0; i < (int)p_frame_count; i++)
    _add_particle(polar_mode ? (rand()%1000)/1000.0 * 6.28 : x+emx-w/2 + rand()%w,
                  polar_mode ? rand()%h : y+emy-h/2 + rand()%h,
                  vx + (rand()%100)*randvx/100.0,vy + (rand()%100)*randvy/100.0,start_color,life + rand()%life_rand);
   if(p_frame_count >= 1.0) p_frame_count = 0;
  }
 }//kill_counter == -1
 if(kill_counter > 0) kill_counter--;
 if(kill_counter == 0) remove = true;

 if(lifetime != -1)
 {
  if(!lifetime) kill_counter = KILL_DELAY;
  lifetime--;
 }

 for(int i = 0; i < last_particle; i++)
  if(particles[i].life)
  {
   particles[i].x += particles[i].vx;
   particles[i].y += particles[i].vy;
   particles[i].life--;
  }


}

void ParticleSystem::draw_lower()
{
// rect(buf,emx-cam_x,emy-cam_y,emx-cam_x+10,emy-cam_y+10,255);
 if(show_delay) return;
 int temp_x,temp_y;
 //drawing back part only
 if(high_offset && polar_mode)
 {
  for(int i = 0; i < last_particle; i++)
   if(particles[i].life && sin(particles[i].x) <= 0)
    {
     temp_x = (int)(cos(particles[i].x)*(polar_radius-particles[i].life*polar_scaling) + emx) - cam_x;
     temp_y = (int)(sin(particles[i].x)*(polar_radius-particles[i].life*polar_scaling)/2 + emy - particles[i].y) - cam_y;
     if(temp_x < 0 || temp_y < 0 || temp_x >= buf->w || temp_y >= buf->h) continue;
     buf->line[temp_y][temp_x] = particles[i].color;
    }
   for(int t = 0; t < trail; t++)
   {
    if(t % trail_skip) continue;
    color_map = trans_map+trail_trans[t];
    for(int i = 0; i < trail_last_particle[t]; i++)
     if(trail_particles[t*max_particles+i].life && sin(trail_particles[t*max_particles+i].x) <= 0)
     {
      temp_x = (int)(cos(trail_particles[t*max_particles+i].x)*(polar_radius-trail_particles[t*max_particles+i].life*polar_scaling) + emx) - cam_x;
      temp_y = (int)(sin(trail_particles[t*max_particles+i].x)*(polar_radius-trail_particles[t*max_particles+i].life*polar_scaling)/2 + emy - trail_particles[t*max_particles+i].y) - cam_y;

      if(temp_x < 0 || temp_y < 0 || temp_x >= buf->w || temp_y >= buf->h) continue;
      buf->line[temp_y][temp_x] = color_map->data[trail_particles[t*max_particles+i].color][buf->line[temp_y][temp_x]];
     }
   }

  return;
 }
}


void ParticleSystem::draw_upper()
{
 if(show_delay) return;
 int temp_x,temp_y;
 //drawing front part only
 if(high_offset && polar_mode)
 {
  for(int i = 0; i < last_particle; i++)
   if(particles[i].life && sin(particles[i].x) > 0)
    {
     temp_x = (int)(cos(particles[i].x)*(polar_radius-particles[i].life*polar_scaling) + emx) - cam_x;
     temp_y = (int)(sin(particles[i].x)*(polar_radius-particles[i].life*polar_scaling)/2 + emy - particles[i].y) - cam_y;
     if(temp_x < 0 || temp_y < 0 || temp_x >= buf->w || temp_y >= buf->h) continue;
     buf->line[temp_y][temp_x] = particles[i].color;
    }
   for(int t = 0; t < trail; t++)
   {
    if(t % trail_skip) continue;
    color_map = trans_map+trail_trans[t];
    for(int i = 0; i < trail_last_particle[t]; i++)
     if(trail_particles[t*max_particles+i].life && sin(trail_particles[t*max_particles+i].x) > 0)
     {
      temp_x = (int)(cos(trail_particles[t*max_particles+i].x)*(polar_radius-trail_particles[t*max_particles+i].life*polar_scaling) + emx) - cam_x;
      temp_y = (int)(sin(trail_particles[t*max_particles+i].x)*(polar_radius-trail_particles[t*max_particles+i].life*polar_scaling)/2 + emy - trail_particles[t*max_particles+i].y) - cam_y;

      if(temp_x < 0 || temp_y < 0 || temp_x >= buf->w || temp_y >= buf->h) continue;
      buf->line[temp_y][temp_x] = color_map->data[trail_particles[t*max_particles+i].color][buf->line[temp_y][temp_x]];
     }
   }

  return;
 }
 if(polar_mode)
 {
  for(int i = 0; i < last_particle; i++)
   if(particles[i].life)
   {
    temp_x = (int)(cos(particles[i].x)*(polar_radius-particles[i].life*polar_scaling) + emx) - cam_x;
    temp_y = (int)(sin(particles[i].x)*(polar_radius-particles[i].life*polar_scaling)/2 + emy - particles[i].y) - cam_y;
    if(temp_x < 0 || temp_y < 0 || temp_x >= buf->w || temp_y >= buf->h) continue;
    buf->line[temp_y][temp_x] = particles[i].color;
   }
  for(int t = 0; t < trail; t++)
  {
   if(t % trail_skip) continue;
   color_map = trans_map+trail_trans[t];
   for(int i = 0; i < trail_last_particle[t]; i++)
    if(trail_particles[t*max_particles+i].life)
    {
     temp_x = (int)(cos(trail_particles[t*max_particles+i].x)*(polar_radius-trail_particles[t*max_particles+i].life*polar_scaling) + emx) - cam_x;
     temp_y = (int)(sin(trail_particles[t*max_particles+i].x)*(polar_radius-trail_particles[t*max_particles+i].life*polar_scaling)/2 + emy - trail_particles[t*max_particles+i].y) - cam_y;
     if(temp_x < 0 || temp_y < 0 || temp_x >= buf->w || temp_y >= buf->h) continue;
     buf->line[temp_y][temp_x] = color_map->data[trail_particles[t*max_particles+i].color][buf->line[temp_y][temp_x]];
    }
  }
 }
 else
 {
 for(int i = 0; i < last_particle; i++)
  if(particles[i].life)
  {
   //to avoid converting from float to int multiple times, also eliminates the cost of array use
   temp_x = (int)particles[i].x - cam_x;
   temp_y = (int)particles[i].y - cam_y;
   if(temp_x < 0 || temp_y < 0 || temp_x >= buf->w || temp_y >= buf->h) continue;
   buf->line[temp_y][temp_x] = particles[i].color;
  }
  for(int t = 0; t < trail; t++)
  {
   if(t % trail_skip) continue;
   color_map = trans_map+trail_trans[t];
   for(int i = 0; i < trail_last_particle[t]; i++)
   if(trail_particles[t*max_particles+i].life)
   {
    //to avoid converting from float to int multiple times, also eliminates the cost of array use
    temp_x = (int)trail_particles[t*max_particles+i].x - cam_x;
    temp_y = (int)trail_particles[t*max_particles+i].y - cam_y;
    if(temp_x < 0 || temp_y < 0 || temp_x >= buf->w || temp_y >= buf->h) continue;
    buf->line[temp_y][temp_x] = color_map->data[trail_particles[t*max_particles+i].color][buf->line[temp_y][temp_x]];
   }
  }
 }
}

void ParticleSystem::set_point_emitter(int _x, int _y, float _vx, float _vy, float _randvx, float _randvy, int particles_per_sec, int min_life, int max_life)
{
 emitter_type = EMITTER_POINT;
 emx = _x; emy = _y;
 vx = _vx; randvx = _randvx;
 vy = _vy; randvy = _randvy;
 pps = particles_per_sec/60.0;
 life = min_life;
 life_rand = max_life - min_life;
 p_frame_count = 0;
 if(!life_rand) life_rand = 1;
}

void ParticleSystem::set_box_emitter(int _x, int _y, int _w, int _h, float _vx, float _vy, float _randvx, float _randvy, int particles_per_sec, int min_life, int max_life)
{
 emitter_type = EMITTER_BOX;
 emx = _x; emy = _y;
 w = _w; h = _h;
 if(!w) w = 1; if(!h) h = 1;
 vx = _vx; randvx = _randvx;
 vy = _vy; randvy = _randvy;
 pps = particles_per_sec/60.0;
 life = min_life;
 life_rand = max_life - min_life;
 p_frame_count = 0;
 if(!life_rand) life_rand = 1;
}

void ParticleSystem::_add_particle(float _x, float _y, float _vx, float _vy, int color, int _life)
{
 for(int i = 0; i < max_particles; i++)
  if(!particles[i].life)
  {
   particles[i].life = _life;
   particles[i].x = _x;
   particles[i].y = _y;
   particles[i].vx = _vx;
   particles[i].vy = _vy;
   particles[i].color = color;
   if(last_particle <= i) last_particle = i+1;
   return;
  }
}
