/*
 * (C) Copyright Chris B. Kaynor 2006
 * All Rights Reserved.
 *
 * CONTACT: chris@kaynor.net
*/

#include <stdlib.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 "vector.h"
#include "data.h"

cbk_object_data::cbk_object_data(void)
{
	c_position.SetValue(0, 0, 0);
	c_rotation.SetValue(0, 0, 0);
	
	c_movement_speed = 0;
	c_rotation_speed = 0;

	c_CollisonSize = 1;

	c_LifeTime = -1;

	c_texture_id = 0;

	c_Movement_Decay = 1;

	c_Rotation_Decay = 1;
}

GLint cbk_object_data::update(GLfloat time_slice)
{
	GLfloat XRotate = c_rotation.GetValue(1);
	GLfloat XPosition = c_position.GetValue(1);
	GLfloat YPosition = c_position.GetValue(2);
	GLfloat ZPosition = c_position.GetValue(3);

	//Reduce Turbo Time
	if (TurboCoolDownTime > time_slice) {
		TurboCoolDownTime -= time_slice;
	} else {
		TurboCoolDownTime = 0;
	}
	if (TurboTimeRemaining > time_slice) {
		TurboTimeRemaining -= time_slice;
	} else {
		TurboTimeRemaining = 0;
	}

	//Rotate Object
	XRotate += c_rotation_speed * time_slice;
	c_rotation_speed *= pow(c_Rotation_Decay, time_slice);
	if (fabs(c_rotation_speed) < 0.1f) {c_rotation_speed = 0.0f;}
	if (XRotate > 360.0f) {XRotate -= 360.0f;}
	if (XRotate <= 0.0f) {XRotate += 360.0f;}
	c_rotation.SetValue(1, XRotate);

	//Move Object
	XPosition += ((sin(Degrees2Radians((float) c_rotation.GetValue(1)))) * (c_movement_speed * time_slice));
	ZPosition += ((cos(Degrees2Radians((float) c_rotation.GetValue(1)))) * (c_movement_speed * time_slice));
	c_movement_speed *= pow(c_Movement_Decay, time_slice);
	if (fabs(c_movement_speed) < 0.1f) {c_movement_speed = 0.0f;}
	if (XPosition >= WorldSize)      {XPosition -= WorldSize;}
	else if (XPosition < 0)          {XPosition += WorldSize;}
	if (ZPosition >= WorldSize)      {ZPosition -= WorldSize;}
	else if (ZPosition < 0)          {ZPosition += WorldSize;}

	if (c_LifeTime != -1) {
		if (c_LifeTime > time_slice) {
			c_LifeTime -= time_slice;
		} else {
			return true;
		}
	}

	c_position.SetValue(XPosition, YPosition, ZPosition);

	return false;
}

void cbk_object_data::change_movement_speed(GLfloat Speed)
{
	c_movement_speed = Speed;
}

void cbk_object_data::change_rotation_speed(GLfloat Speed)
{
	c_rotation_speed = Speed;
}

void cbk_object_data::move(GLfloat X_Position, GLfloat Y_Position, GLfloat Z_Position)
{
	c_position.SetValue(X_Position, Y_Position, Z_Position);
}

void cbk_object_data::move(cbk_vector Position)
{
	c_position = Position;
}

void cbk_object_data::rotate(cbk_vector Rotation)
{
	c_rotation = Rotation;
}

void cbk_object_data::rotate(GLfloat X_Rotate, GLfloat Y_Rotate, GLfloat Z_Rotate)
{
	c_rotation.SetValue(X_Rotate, Y_Rotate, Z_Rotate);
}

cbk_vector cbk_object_data::get_position(void)
{
	return c_position;
}

GLfloat cbk_object_data::get_movement_speed(void)
{
	return c_movement_speed;
}

GLfloat cbk_object_data::get_rotation_speed(void)
{
	return c_rotation_speed;
}

cbk_vector cbk_object_data::get_rotation(void)
{
	return c_rotation;
}

cbk_vector cbk_object_data::get_velocity(void)
{
	GLfloat XSpeed = 0.0001;
	GLfloat ZSpeed = 0.0001;

	cbk_vector MovementVelocity;

	XSpeed = (sin(Degrees2Radians((float) c_rotation.GetValue(1))) * c_movement_speed);
	ZSpeed = (cos(Degrees2Radians((float) c_rotation.GetValue(1))) * c_movement_speed);

	MovementVelocity.SetValue(XSpeed, 0, ZSpeed);

	return MovementVelocity;
}

void cbk_object_data::change_movement_decay(GLfloat Movement_Decay) {
	c_Movement_Decay = Movement_Decay;
}

void cbk_object_data::change_rotation_decay(GLfloat Rotation_Decay) {
	c_Rotation_Decay = Rotation_Decay;
}

void cbk_object_data::set_texture(GLuint TextureID) {
	c_texture_id = TextureID;
}


GLfloat cbk_object_data::GetCollsionSize(void)
{
	return c_CollisonSize;
}

void cbk_object_data::SetCollsionSize(GLfloat CollsionSize)
{
	c_CollisonSize = CollsionSize;
}

void cbk_object_data::SetLifeTime(GLfloat LifeTime) {
	c_LifeTime = LifeTime;
}

GLfloat cbk_object_data::GetLifeTime() {
	return c_LifeTime;
}



void cbk_object_data_tank::Accelerate(GLfloat Dt) {
	GLfloat MaxSpeed = (TurboTimeRemaining > 0) ? TurboMovementSpeed : MaxMovementSpeed;

	if (get_movement_speed() > 0) {
		change_movement_speed(get_movement_speed() * 1.2);
	} else if (get_movement_speed() < 0) {
		change_movement_speed(get_movement_speed() * 0.8);
	} else {
		change_movement_speed(0.2);
	}

	if (fabs(get_movement_speed()) < 0.2) {change_movement_speed(0);}
	if (c_movement_speed > MaxSpeed) {change_movement_speed(max(c_movement_speed * 0.8, MaxSpeed));}
	if (c_movement_speed < MinMovementSpeed) {change_movement_speed(min(c_movement_speed * 0.8, MinMovementSpeed));}
}

void cbk_object_data_tank::Decelerate() {
	GLfloat MaxSpeed = (TurboTimeRemaining > 0) ? TurboMovementSpeed : MaxMovementSpeed;

	if (get_movement_speed() < 0) {
		change_movement_speed(get_movement_speed() * 1.2);
	} else if (get_movement_speed() > 0) {
		change_movement_speed(get_movement_speed() * 0.8);
	} else {
		change_movement_speed(-0.2);
	}

	if (fabs(get_movement_speed()) < 0.2) {change_movement_speed(0);}
	if (get_movement_speed() > MaxSpeed) {change_movement_speed(MaxSpeed);}
	if (get_movement_speed() < MinMovementSpeed) {change_movement_speed(MinMovementSpeed);}
}

void cbk_object_data_tank::TurnRight() {
		if (get_rotation_speed() < 0) {
			change_rotation_speed(get_rotation_speed() * 1.3);
		} else if (get_rotation_speed() > 0) {
			change_rotation_speed(get_rotation_speed() * 0.7);
		} else {
			change_rotation_speed(-1);
		}

		if (fabs(get_rotation_speed()) < 0.2) {change_rotation_speed(0);}
		if (get_rotation_speed() > MaxRotationSpeed) {change_rotation_speed(MaxRotationSpeed);}
		if (get_rotation_speed() < -MaxRotationSpeed) {change_rotation_speed(-MaxRotationSpeed);}
}

void cbk_object_data_tank::TurnLeft() {
		if (get_rotation_speed() > 0) {
			change_rotation_speed(get_rotation_speed() * 1.3);
		} else if (get_rotation_speed() < 0) {
			change_rotation_speed(get_rotation_speed() * 0.7);
		} else {
			change_rotation_speed(1);
		}

		if (fabs(get_rotation_speed()) < 0.2) {change_rotation_speed(0);}
		if (get_rotation_speed() > MaxRotationSpeed) {change_rotation_speed(MaxRotationSpeed);}
		if (get_rotation_speed() < -MaxRotationSpeed) {change_rotation_speed(-MaxRotationSpeed);}
}

void cbk_object_data_tank::draw(cbk_object_data_tank PlayerView)
{
	const GLfloat Object_Plane_T[] = {0, 0, 1};

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

	long X = 0;
	bool Found = false;

	Compare_Position[0].SetValue(c_position);
	Compare_Position[1].SetValue(c_position.GetValue(1) + WorldSize, c_position.GetValue(2), c_position.GetValue(3));
	Compare_Position[2].SetValue(c_position.GetValue(1) - WorldSize, c_position.GetValue(2), c_position.GetValue(3));
	Compare_Position[3].SetValue(c_position.GetValue(1), c_position.GetValue(2), c_position.GetValue(3) + WorldSize);
	Compare_Position[4].SetValue(c_position.GetValue(1), c_position.GetValue(2), c_position.GetValue(3) - WorldSize);
	Compare_Position[5].SetValue(c_position.GetValue(1) + WorldSize, c_position.GetValue(2), c_position.GetValue(3) + WorldSize);
	Compare_Position[6].SetValue(c_position.GetValue(1) + WorldSize, c_position.GetValue(2), c_position.GetValue(3) - WorldSize);
	Compare_Position[7].SetValue(c_position.GetValue(1) - WorldSize, c_position.GetValue(2), c_position.GetValue(3) + WorldSize);
	Compare_Position[8].SetValue(c_position.GetValue(1) - WorldSize, c_position.GetValue(2), c_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) {
		glBindTexture(GL_TEXTURE_2D, c_texture_id);
		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGenfv(GL_T, GL_OBJECT_PLANE, Object_Plane_T);

		glMatrixMode(GL_TEXTURE);
			glPushMatrix();
			glLoadIdentity();
			glTranslatef(0.5, 0.5, 0);
		glMatrixMode(GL_MODELVIEW);

		glPushMatrix();
		glTranslatef(Compare_Position[X].GetValue(1), Compare_Position[X].GetValue(2), Compare_Position[X].GetValue(3));
		glRotatef(c_rotation.GetValue(1) + 90, 0, 1, 0);
		glRotatef(c_rotation.GetValue(2), 1, 0, 0);
		glRotatef(c_rotation.GetValue(3), 0, 0, 1);
		glTranslatef(0.00, 0.40, 0.00);
		glScalef(    1.00, 0.40, 1.00);

		glutSolidSphere(c_CollisonSize, 20, 20);
		glPopMatrix();

		glMatrixMode(GL_TEXTURE);
			glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
	}
}




void cbk_object_data_static::draw(cbk_object_data_tank PlayerView)
{
	const GLfloat Object_Plane_T[] = {0, 0, 1};

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

	long X = 0;
	bool Found = false;

	Compare_Position[0].SetValue(c_position);
	Compare_Position[1].SetValue(c_position.GetValue(1) + WorldSize, c_position.GetValue(2), c_position.GetValue(3));
	Compare_Position[2].SetValue(c_position.GetValue(1) - WorldSize, c_position.GetValue(2), c_position.GetValue(3));
	Compare_Position[3].SetValue(c_position.GetValue(1), c_position.GetValue(2), c_position.GetValue(3) + WorldSize);
	Compare_Position[4].SetValue(c_position.GetValue(1), c_position.GetValue(2), c_position.GetValue(3) - WorldSize);
	Compare_Position[5].SetValue(c_position.GetValue(1) + WorldSize, c_position.GetValue(2), c_position.GetValue(3) + WorldSize);
	Compare_Position[6].SetValue(c_position.GetValue(1) + WorldSize, c_position.GetValue(2), c_position.GetValue(3) - WorldSize);
	Compare_Position[7].SetValue(c_position.GetValue(1) - WorldSize, c_position.GetValue(2), c_position.GetValue(3) + WorldSize);
	Compare_Position[8].SetValue(c_position.GetValue(1) - WorldSize, c_position.GetValue(2), c_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) {
		glBindTexture(GL_TEXTURE_2D, c_texture_id);
		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGenfv(GL_T, GL_OBJECT_PLANE, Object_Plane_T);

		glMatrixMode(GL_TEXTURE);
			glPushMatrix();
			glLoadIdentity();
			glScalef(5, 5, 5);
		glMatrixMode(GL_MODELVIEW);

		glPushMatrix();
		glTranslatef(Compare_Position[X].GetValue(1), Compare_Position[X].GetValue(2), Compare_Position[X].GetValue(3));
		glRotatef(c_rotation.GetValue(1), 0, 1, 0);
		glRotatef(c_rotation.GetValue(2), 1, 0, 0);
		glRotatef(c_rotation.GetValue(3), 0, 0, 1);
		glScalef(1, 5, 1);
		glRotatef(-90, 1, 0, 0);

		glutSolidCone(c_CollisonSize, 2, 10, 10);
		glPopMatrix();

		glMatrixMode(GL_TEXTURE);
			glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
	}
}



void cbk_object_data_projectile::draw(cbk_object_data_tank PlayerView)
{
	const GLfloat Object_Plane_T[] = {0, 0, 1};

	static const GLfloat Accel = (-2 * ProjectileLife - 0.4) / (ProjectileLife * ProjectileLife);
	const GLfloat ProjectileLifeUsed = ProjectileLife - c_LifeTime;

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

	long X = 0;
	bool Found = false;

	Compare_Position[0].SetValue(c_position);
	Compare_Position[1].SetValue(c_position.GetValue(1) + WorldSize, c_position.GetValue(2), c_position.GetValue(3));
	Compare_Position[2].SetValue(c_position.GetValue(1) - WorldSize, c_position.GetValue(2), c_position.GetValue(3));
	Compare_Position[3].SetValue(c_position.GetValue(1), c_position.GetValue(2), c_position.GetValue(3) + WorldSize);
	Compare_Position[4].SetValue(c_position.GetValue(1), c_position.GetValue(2), c_position.GetValue(3) - WorldSize);
	Compare_Position[5].SetValue(c_position.GetValue(1) + WorldSize, c_position.GetValue(2), c_position.GetValue(3) + WorldSize);
	Compare_Position[6].SetValue(c_position.GetValue(1) + WorldSize, c_position.GetValue(2), c_position.GetValue(3) - WorldSize);
	Compare_Position[7].SetValue(c_position.GetValue(1) - WorldSize, c_position.GetValue(2), c_position.GetValue(3) + WorldSize);
	Compare_Position[8].SetValue(c_position.GetValue(1) - WorldSize, c_position.GetValue(2), c_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) {
		glBindTexture(GL_TEXTURE_2D, c_texture_id);
		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
		glTexGenfv(GL_T, GL_OBJECT_PLANE, Object_Plane_T);

		glPushMatrix();
		glTranslatef(Compare_Position[X].GetValue(1), Compare_Position[X].GetValue(2), Compare_Position[X].GetValue(3));
		glRotatef(c_rotation.GetValue(1), 0, 1, 0);
		glRotatef(c_rotation.GetValue(2), 1, 0, 0);
		glRotatef(c_rotation.GetValue(3), 0, 0, 1);
		glTranslatef(0, c_CollisonSize + ((Accel * ProjectileLifeUsed * ProjectileLifeUsed) + (2 * ProjectileLifeUsed)), 0);

		glutSolidSphere(c_CollisonSize, 5, 5);

		glPopMatrix();
	}
}