//////////////////////////////////////////////////////////////////////////
//Tom Valesky
//CS-752
//Dr. Chen, instructor
//explosion.cpp -- source code for the Explosion class
//////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include "point.h"
#include "explosion.h"
#include "util.h"
#include "list.h"
#include <math.h>
#include "glhdr.h"

Explosion::Explosion()
{
        printf("default constructor for Explosion;\nthis should never be called\n");
        printf("exiting\n");
        exit(0);
}

Explosion::~Explosion()
{
	printf("destructor for explosion called\n");		
}


Explosion::Explosion(int num_points, int center_x, int center_y, int center_z,
                        float initial_radius, float initial_red, float initial_green,
                        float initial_blue, float initial_alpha, float timestep,
                        float initial_velocity, float alpha_decrement, int lifetime, int countdown)
{
        printf("called Explosion constructor\n");
        this->num_points = num_points;
        this->center_x = center_x;
        this->center_y = center_y;
        this->center_z = center_z;
        this->initial_radius = initial_radius;
        this->initial_red = initial_red;
        this->initial_green = initial_green;
        this->initial_blue = initial_blue;
        this->initial_alpha = initial_alpha;
        this->timestep = timestep;
        this->initial_velocity = initial_velocity;
        this->alpha_decrement = alpha_decrement;
		this->countdown = countdown;

        this->unit_x = 0.0;
        this->unit_y = 0.0;
        this->unit_z = 0.0;
        this->dist = 0.0;
		this->lifetime = lifetime;
		this->radius = initial_radius;


        generate_points_on_sphere();   //generate new sphere for next frame
		
}

float Explosion::get_radius(void)
{
	return radius;
}

float Explosion::get_velocity(void)
{
	return initial_velocity;
}


float Explosion::get_x(void)
{
	return center_x;
}

float Explosion::get_y(void)
{
	return center_y;
}

float Explosion::get_z(void)
{
	return center_z;
}


int Explosion::move(void)
{
	int i;
	int retval = FALSE;
        Point *cur_point;
	int printflag = TRUE;

	if (countdown > 0)
	{
		countdown--;
		return TRUE;
	}

	if (live_list.is_empty())
	{
		return FALSE;
	}
	//printf("called Explosion::move()\n");
	radius += initial_velocity; //calculate explosion radius
	printf("explosion radius = %f\n", radius);

    live_list.set_head();
	i = 0;
	if (lifetime-- > 0)
	{
		generate_points_on_sphere();	//generate next set of points
	}

    do
	{
		i++;
		//move each point
                cur_point = (Point *)live_list.get_current_data();
                cur_point->x += cur_point->dx;
                cur_point->y += cur_point->dy;
                cur_point->z += cur_point->dz;
                cur_point->alpha -= alpha_decrement;
                if (cur_point->alpha > 0.0)
                        {
                        //printf("Explosion::move() set retval to TRUE\n");
						retval  = TRUE;
                        }
                else
                        {
                                //delete current point from livelist
                                //and add to deadlist
						
                        dead_list.add(live_list.del());

                        }

        } while (live_list.set_next());

	if (retval == FALSE)
	{
		printf("retval = FALSE; out of points in livelist after %d iterations\n", i);
	}
	return retval;
}

void Explosion::draw(void)
{
	int i;
    int radius;
    Point *cur_point;

	printf("in explosion::draw; countdown = %d\n", countdown);
	if (countdown > 0)
	{
		return;
	}

	if (live_list.is_empty())
	{
		return;
	}

        glBegin(GL_POINTS);
        live_list.set_head();
        do
	{
              cur_point = (Point *) live_list.get_current_data();
           
              glColor4f(cur_point->r, cur_point->g,
                                cur_point->b, cur_point->alpha);
              glVertex3f(cur_point->x, cur_point->y,
                                      cur_point->z);
              } while (live_list.set_next());
        glEnd();
}

void Explosion::generate_points_on_sphere(void)
{
	int i;
	Point p;
        Point *cur_point;

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

					//generate a valid point
		do 
		{
			generate_candidate_point (&p);
		} while(!is_valid_point(p));

					//project it onto the sphere
		project_point_onto_sphere(&p);

					//initialize it
		initialize_velocity(&p);

                cur_point = alloc_node();
                cur_point->x = p.x;
                cur_point->y = p.y;
                cur_point->z = p.z;
                cur_point->dx = p.dx;
                cur_point->dy = p.dy;
                cur_point->dz = p.dz;
                cur_point->r = initial_red;
                cur_point->g = initial_green;
                cur_point->b = initial_blue;
                cur_point->alpha = initial_alpha;
	
	}
}

void Explosion::generate_candidate_point (Point *p)
{
        p->x = get_rand(-1 + center_x, 1 + center_x);
        p->y = get_rand(-1 + center_y, 1 + center_y);
        p->z = get_rand(-1 + center_z, 1 + center_z);

}

int Explosion::is_valid_point (Point p)
{
	float x_component = 0.0, y_component = 0.0, z_component = 0.0;

					//find components
	x_component = center_x - p.x;
	y_component = center_y - p.y;
	z_component = center_z - p.z;


					//square components
	x_component *= x_component;
	y_component *= y_component;
	z_component *= z_component;
					//apply distance formula
	dist = sqrt (x_component + y_component + z_component);

	if (dist <= initial_radius)
		return TRUE;
	else
		return FALSE;
}

void Explosion::project_point_onto_sphere (Point *p)
{
				//create unit vector by dividing thru
				//by length of vector
                        //need to correct for center here

        p->x = ((p->x - center_x)/dist) + center_x;
        p->y = ((p->y - center_y)/dist) + center_y;
        p->z = ((p->z - center_z)/dist) + center_z;

        unit_x = p->x;
        unit_y = p->y;
        unit_z = p->z;
				//multiply by desired length to put
				//point onto unit sphere
	p->x *= initial_radius;
	p->y *= initial_radius;
	p->z *= initial_radius;
}

void Explosion::initialize_velocity(Point *p)
{
        float randomized_velocity = 0.0;

                                        //add randomness to velocity
        randomized_velocity = initial_velocity * get_rand (.9, 1.1);
	p->dx = (unit_x - center_x) * randomized_velocity;
	p->dy = (unit_y - center_y) * randomized_velocity;
	p->dz = (unit_z - center_z) * randomized_velocity;

}

//print out all private values
void Explosion::dump(void)
{
        printf("num_points = %d\n", num_points);
        printf("center_x = %d\n", center_x);
        printf("center_y = %d\n", center_y);
        printf("center_z = %d\n", center_z);
        printf("initial_radius = %f\n", initial_radius);
        printf("initial_red = %f\n", initial_red);
        printf("initial_green = %f\n", initial_green);
        printf("initial_blue = %f\n", initial_blue);
        printf("initial_alpha = %f\n", initial_alpha);
        printf("dist = %f\n", dist);
        printf("unit_x = %f\n", unit_x);
        printf("unit_y = %f\n", unit_y);
        printf("unit_z = %f\n", unit_z);
        printf("timestep = %f\n", timestep);
        printf("initial_velocity = %f\n", initial_velocity);
        printf("alpha_decrement = %f\n", alpha_decrement);
}

Point *Explosion::alloc_node (void )
{
        Node *n = 0;
        Point *retval = 0;
        if (!dead_list.is_empty())
                {
				n = dead_list.del();
				n = live_list.add(n);
                retval = (Point *)n->data;
                }
        else
                {
                n = new Node();
                live_list.add(n);
                n->data = new Point();
                retval = (Point *)n->data;
                }

        return retval;

}
