/*
 * (C) Copyright Ian D. Romanick 2006
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * on the rights to use, copy, modify, merge, publish, distribute, sub
 * license, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
 * AUTHORS, COPYRIGHT HOLDERS, AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#define _CRT_SECURE_NO_DEPRECATE

#include <stdlib.h>
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <ctime> 
#include <string.h>

#ifdef __linux__
	#include <GL/glut.h>
	#include <GL/glext.h>
	#include <png.h>
	#include <pngconf.h>
	#include <zlib.h>
	#include <zconf.h>
#else
	#include "glut.h"
	#include "glext.h"
	#include "png.h"
	#include "pngconf.h"
	#include "zlib.h"
	#include "zconf.h"
#endif

#include "Texture.h"
#include "Macros.h"
#include "Settings.h"
#include "data.h"
#include "vector.h"
#include "particle.h"

idr_particle_system::idr_particle_system(void)
{
	positions = new(float[4 * 20]);
	velocities = new(float[4 * 20]);
	time_to_live = new(float[20]);

	LifeTime = 0;

	initial_ttl = 1;
	max_particles = 20;

	gravity = -0.20;
	last_particle_time = 0.0;

	c_light_id = 0;

	ParticleColor[0] = 1.0f;
	ParticleColor[1] = 1.0f;
	ParticleColor[2] = 0.0f;

	active_particles = max_particles / 2;
	for (unsigned i = 0; i < active_particles; i++) {
		init_particle(i);
	}
}

idr_particle_system::idr_particle_system(unsigned max_part, float ttl)
{
	positions = new(float[4 * max_part]);
	velocities = new(float[4 * max_part]);
	time_to_live = new(float[max_part]);

	LifeTime = 0;

	c_light_id = 0;

	ParticleColor[0] = 1.0f;
	ParticleColor[1] = 1.0f;
	ParticleColor[2] = 0.0f;

	initial_ttl = ttl;
	max_particles = max_part;

	gravity = -0.20;
	last_particle_time = 0.0;

	active_particles = max_particles / 2;
	for (unsigned i = 0; i < active_particles; i++) {
		init_particle(i);
	}
}

void idr_particle_system::reset_particles(void) {
	for (unsigned x = 0; x <= max_particles; x++) {
		init_particle(x);
	}
}

void idr_particle_system::init_particle(unsigned idx)
{
	if (LifeTime <= 0) {return;}

	if (idx < max_particles) {
		const unsigned base_offset = (idx * 4);

		/* Initialize the particle's time to live and position to the default
		* initial values.
		*/
		double ttl_fudge = (double) random();

		time_to_live[idx] = initial_ttl + ((ttl_fudge * 2.0 / RAND_MAX) - 1.0);
		positions[base_offset + 0] = 0.0;
		positions[base_offset + 1] = 0.0;
		positions[base_offset + 2] = 0.0;
		positions[base_offset + 3] = 1.0;


		/* Generate a semi-random, but genearlly upward, velocity vector for the
		* particle.
		*/
		double x_velo = GetRandom(-0.20f,  0.20f);
		double y_velo = GetRandom(-0.10f,  0.10f);
		double z_velo = GetRandom(-0.20f,  0.20f);

		velocities[base_offset + 0] = x_velo;
		velocities[base_offset + 1] = y_velo;
		velocities[base_offset + 2] = z_velo;
		velocities[base_offset + 3] = 0.0;
	}
}


void idr_particle_system::draw_particles(cbk_object_data_tank PlayerView, PFNGLBINDBUFFERARBPROC bind_buffer)
{
	if (LifeTime <= 0) {glDisable(GL_LIGHT1 + c_light_id); return;}

	const cbk_vector Player_Position(PlayerView.get_position());
	cbk_vector Compare_Position[9];

	const GLfloat Mutiple = 0.5f - (LifeTime/ 8.0f);

	static const GLfloat lightPosition[4] = {0, 1, 0, 1};
	static const GLfloat lightAmbient[4] = {0, 0, 0, 1};

	long X = 0;
	bool Found = false;

	Compare_Position[0].SetValue(Position);
	Compare_Position[1].SetValue(Position.GetValue(1) + WorldSize, Position.GetValue(2), Position.GetValue(3));
	Compare_Position[2].SetValue(Position.GetValue(1) - WorldSize, Position.GetValue(2), Position.GetValue(3));
	Compare_Position[3].SetValue(Position.GetValue(1), Position.GetValue(2), Position.GetValue(3) + WorldSize);
	Compare_Position[4].SetValue(Position.GetValue(1), Position.GetValue(2), Position.GetValue(3) - WorldSize);
	Compare_Position[5].SetValue(Position.GetValue(1) + WorldSize, Position.GetValue(2), Position.GetValue(3) + WorldSize);
	Compare_Position[6].SetValue(Position.GetValue(1) + WorldSize, Position.GetValue(2), Position.GetValue(3) - WorldSize);
	Compare_Position[7].SetValue(Position.GetValue(1) - WorldSize, Position.GetValue(2), Position.GetValue(3) + WorldSize);
	Compare_Position[8].SetValue(Position.GetValue(1) - WorldSize, Position.GetValue(2), Position.GetValue(3) - WorldSize);

	Found = false;
	for (X = 0; X < 9; X++) {
		if ((Compare_Position[X] - Player_Position).GetMagitude() < HorizonDistance) {
			Found = true;
			break;
		}
	}

	if (Found) {
		glPushMatrix();

		glTranslatef(Compare_Position[X].GetValue(1), Compare_Position[X].GetValue(2), Compare_Position[X].GetValue(3)); 

		glColor3fv(ParticleColor);

		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, 0);

		(*bind_buffer)(GL_ARRAY_BUFFER, 0);
		glVertexPointer(4, GL_FLOAT, 0, positions);
		glDrawArrays(GL_POINTS, 0, active_particles);

		glEnable(GL_LIGHT1 + c_light_id);
		glLightfv(GL_LIGHT1 + c_light_id, GL_AMBIENT, lightAmbient);
		glLightfv(GL_LIGHT1 + c_light_id, GL_DIFFUSE, ParticleColor);
		glLightf(GL_LIGHT1 + c_light_id, GL_QUADRATIC_ATTENUATION, Mutiple);
		glLightfv(GL_LIGHT1 + c_light_id, GL_POSITION, lightPosition);

		glEnable(GL_TEXTURE_2D);
		glEnable(GL_LIGHTING);

		glColor3fv(AmbientColor);

		glPopMatrix();
	}
}


bool idr_particle_system::update_particles(double time_slice)
{
	if (LifeTime <= time_slice) {glDisable(GL_LIGHT1 + c_light_id); LifeTime = 0; return true;}

	if (Tank != NULL) {
		Position = Tank->get_position();
		Position.SetValue(2, Position.GetValue(2) + 1.2);
	}

	for (unsigned i = 0; i < max_particles; i++) {
		positions[(i * 4) + 0] += velocities[(i * 4) + 0] * time_slice;
		positions[(i * 4) + 1] += velocities[(i * 4) + 1] * time_slice;
		positions[(i * 4) + 2] += velocities[(i * 4) + 2] * time_slice;
		positions[(i * 4) + 3] += velocities[(i * 4) + 3] * time_slice;

		velocities[(i * 4) + 1] += gravity * time_slice;

		time_to_live[i] -= time_slice;
		if (time_to_live[i] <= 0.0) {
			init_particle(i);
		}
		if (positions[(i * 4) + 1] <= 0) {
			init_particle(i);
		}
	}
   
	if (active_particles < max_particles) {
		last_particle_time += time_slice;
		if (last_particle_time > 0.2) {
			init_particle(active_particles);
			active_particles++;
			last_particle_time = 0.0;
		}
	}

	LifeTime -= time_slice;

	return false;
}
