/* 
 * PlanetPenguin Racer 
 * Copyright (C) 2004-2005 Volker Stroebel <volker@planetpenguin.de>
 *
 * Copyright (C) 1999-2001 Jasmin F. Patry
 * 
 * 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.
 * 
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "part_sys.h"
#include "phys_sim.h"
#include "gl_util.h"
#include "course_render.h"
#include "render_util.h"

#include "ppogl/base/defs.h"
#include "ppogl/base/glwrappers.h"

/* This constant is here as part of a debugging check to prevent an infinite 
   number of particles from being created */
#define MAX_PARTICLES 500000

#define START_RADIUS 0.04
#define OLD_PART_SIZE 0.07
#define NEW_PART_SIZE 0.02
#define MIN_AGE     -0.2
#define MAX_AGE      1.0

#define VARIANCE_FACTOR 0.8

#define PARTICLE_SHADOW_HEIGHT 0.05
#define PARTICLE_SHADOW_ALPHA 0.1

typedef struct _Particle {
    ppogl::Vec3d pt;
    short type;
    double base_size;
    double cur_size;
    double terrain_height;
    double age;
    double death;
    double alpha;
    ppogl::Vec3d vel;
	GLuint particle_binding;
    struct _Particle *next;
} Particle;

static ppogl::Color particleColor;

static Particle* head = NULL;
static int num_particles = 0;

double frand() 
{
    return double(rand())/RAND_MAX;
} 

void create_new_particles( ppogl::Vec3d loc, ppogl::Vec3d vel, int num, GLuint particle_binding) 
{
    Particle *newp;
    int i;
    double speed;

    speed = vel.normalize();

    /* Debug check to track down infinite particle bug */
    if ( num_particles + num > MAX_PARTICLES ) {
		PP_ERROR( "maximum number of particles exceeded" );
    } 

    for (i=0; i<num; i++) {

        newp = new Particle;
		PP_CHECK_ALLOC(newp);

        num_particles += 1;

        newp->next = head;
        head = newp;

        newp->pt.x() = loc.x() + 2.*(frand() - 0.5) * START_RADIUS;
        newp->pt.y() = loc.y();
        newp->pt.z() = loc.z() + 2.*(frand() - 0.5) * START_RADIUS;

	newp->type = int(frand() * (4.0 - EPS));
		
	//get_texture_binding( "snow_particle", &(newp->particle_binding) );	
	newp->particle_binding = particle_binding;	
	
	newp->base_size = ( frand() + 0.5 ) * OLD_PART_SIZE;
	newp->cur_size = NEW_PART_SIZE;

        newp->age = frand() * MIN_AGE;
        newp->death = frand() * MAX_AGE;


        newp->vel =(speed*vel)+
	    ppogl::Vec3d( VARIANCE_FACTOR * (frand() - 0.5) * speed, 
			 VARIANCE_FACTOR * (frand() - 0.5) * speed,
			 VARIANCE_FACTOR * (frand() - 0.5) * speed  );
    }
} 

void update_particles( double time_step )
{
    Particle **p, *q;
    double ycoord;

    for (p = &head; *p != NULL; ) {
        (**p).age += time_step;

        if ( (**p).age < 0 ) continue;

	(**p).pt = ( (**p).pt)+(time_step*(**p).vel );

	ycoord = find_y_coord( (**p).pt.x(), (**p).pt.z() );

	if ( (**p).pt.y() < ycoord - 3 ) {
	    (**p).age = (**p).death + 1;
	} 

        if ( (**p).age >= (**p).death ) {
            q = *p;
            *p = q->next;
            delete q;
            num_particles -= 1;
            continue;
        } 

        (**p).alpha = ( (**p).death - (**p).age ) / (**p).death;

	(**p).cur_size = NEW_PART_SIZE + 
	    ( OLD_PART_SIZE - NEW_PART_SIZE ) * ( (**p).age / (**p).death );

        (**p).vel.y() += -EARTH_GRAV * time_step;

        p = &( (**p).next );
    } 
} 

void draw_particles( Player& plyr )
{
    Particle *p;
    //GLuint   texture_id;
    //char *binding;
    ppogl::Vec2d min_tex_coord, max_tex_coord;

    set_gl_options( PARTICLES );

 /*
	binding = "snow_particle";
    if (!get_texture_binding( "snow_particle", &texture_id ) ) {
	print_warning( IMPORTANT_WARNING,
		       "Couldn't get texture for binding %s", 
		       binding );
	texture_id = 0;
  	} 
 */

    gl::TexEnv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    //glBindTexture( GL_TEXTURE_2D, texture_id );
	
    for (p=head; p!=NULL; p = p->next) {
        if ( p->age < 0 ) continue;
		gl::BindTexture(GL_TEXTURE_2D, p->particle_binding);
	if ( p->type == 0 || p->type == 1 ) {
	    min_tex_coord.y() = 0;
	    max_tex_coord.y() = 0.5;
	} else {
	    min_tex_coord.y() = 0.5;
	    max_tex_coord.y() = 1.0;
	}

	if ( p->type == 0 || p->type == 3 ) {
	    min_tex_coord.x() = 0;
	    max_tex_coord.x() = 0.5;
	} else {
	    min_tex_coord.x() = 0.5;
	    max_tex_coord.x() = 1.0;
	}

	gl::Color(particleColor, particleColor.a() * p->alpha );

	draw_billboard( plyr, p->pt, p->cur_size, p->cur_size,
			false, min_tex_coord, max_tex_coord );
    } 

} 

void clear_particles()
{
    Particle *p, *q;

    p = head;
    for (;;) {
        if (p == NULL ) break;
        q=p;
        p=p->next;
        delete q;
    } 
    head = NULL;
    num_particles = 0;
}

void
reset_particles()
{
	particleColor = ppogl::Color::white;
} 

static int
particle_color_cb(ppogl::Script *vm)
{
    if(vm->getTop()!=1){
		PP_WARNING("pptheme.particle_color: Invalid number of arguments");
		return 0;
	}
				
	vm->pushNull();
	for(int i=0; i<4; i++){
		vm->next(1);
		particleColor.values[i]=vm->getFloat(-1);
		vm->pop();
	}
	
	return 0;
	
	/*
	double tmp_arr[4];
    bool error = false;

    if ( argc == 2 ) {
	// New format
	if ( get_tcl_tuple ( ip, argv[1], tmp_arr, 4 ) == TCL_ERROR ) {
	    error = true;
	} else {
		particleColor.set(tmp_arr);
	}
    } else {
	// Old format 
	if (argc < 3) {
	    error = true;
	} else {

	    NEXT_ARG;

	    PP_WARNING( 
			   "This format for tux_particle_color is deprecated."
			   "  The new format is:\n"
			   "\ttux_particle_color {r g b a}" );

	    while ( !error && argc > 0 ) {
		
		if ( strcmp( "-ambient_and_diffuse", *argv ) == 0 ||
		     strcmp( "-diffuse",  *argv ) == 0 ) 
		{
		    NEXT_ARG;
		    if ( argc == 0 ) {
			error = true;
			break;
		    }
		    if ( get_tcl_tuple ( ip, *argv, tmp_arr, 4 ) == 
			 TCL_ERROR ) 
		    {
			error = true;
			break;
		    }
			particleColor.set(tmp_arr);
		} else if ( strcmp( "-specular", *argv ) == 0 ) {
		    // Ignore
		    NEXT_ARG;
		} else if ( strcmp( "-shininess", *argv ) == 0 ) {
		    // Ignore
		    NEXT_ARG;
		} else {
		    PP_WARNING( "tux_particle_color: unrecognized "
				   "parameter `%s'", *argv );
		}
		
		NEXT_ARG;
	    }
	}
    }

    if ( error ) {
		PP_WARNING( "error in call to tux_particle_color" );
		Tcl_AppendResult(
	    	ip, 
	    	"\nUsage: tux_particle_color {r g b a}", NULL );
		return TCL_ERROR;
    }

    return TCL_OK;
	*/
}

static const struct ppogl::Script::Lib ppthemelib[]={
	{"particle_color", particle_color_cb},
	{NULL, NULL}
};

void register_particle_callbacks()
{
	script.registerLib("pptheme", ppthemelib);
}
