/*
 * (C) C3R14L.K1L4 / João Carlos Ferreira Gonçalves
 * further contact: jcgonc@student.dei.uc.pt
 * started on 03-04-2007
 * checked on 14-04-2007: OK, clean
 */
#ifndef C3R14L_VECTOR3F_HEADER
#define C3R14L_VECTOR3F_HEADER

#ifndef GLfloat
#include<gl/GL.h>
#endif

#include <math.h>
class Vector3f {
public:

	float x, y, z;
	float array[4];

	Vector3f() {
		x = 0;
		y = 0;
		z = 0;
	}

	Vector3f(float x0) {
		x = x0;
		y = 0;
		z = 0;
	}

	Vector3f(float x0, float y0) {
		x = x0;
		y = y0;
		z = 0;
	}

	Vector3f(float x0, float y0, float z0) {
		x = x0;
		y = y0;
		z = z0;
	}

	Vector3f(Vector3f* org) {
		x = org->x;
		y = org->y;
		z = org->z;
	}

	~Vector3f() {
	}

	char* toString() {
		char* str = (char*) malloc(sizeof(char) * 128);
		sprintf(str, "%f,%f,%f", x, y, z);
		return str;
	}

	/* VECTOR <-> VECTOR OPERATIONS */
	void add(Vector3f* b) {
		x = x + b->x;
		y = y + b->y;
		z = z + b->z;
	}

	void subtract(Vector3f* b) {
		x = x - b->x;
		y = y - b->y;
		z = z - b->z;
	}

	Vector3f* crossProduct(Vector3f* b) {
		return (new Vector3f(-z * b->y + y * b->z, z * b->x - x * b->z, -y * b->x + x * b->y));
	}

	float dotProduct(Vector3f* a) {
		return (x * a->x + y * a->y + z * a->z);
	}

	/* VECTOR OPERATIONS */
	void negate() {
		x = -x;
		y = -y;
		z = -z;
	}

	float length() {
		return (sqrtf(x * x + y * y + z * z));
	}

	void normalize() {
		float l = length();
		x = x / l;
		y = y / l;
		z = z / l;
	}

	// NOT USED
	Vector3f* createRightVector() {
		//	/* TAKEN FROM QUAKE 3 ARENA (C) ID SOFTWARE */
		Vector3f* right = new Vector3f(z, -x, y);
		float d = dotProduct(right);
		right->addScaledByf(this, -d);
		right->normalize();
		return (right);
	}

	void debug() {
		printf("(%f, %f, %f)\n", x, y, z);
	}

	void callglVertex3f() {
		glVertex3f(x, y, z);
	}

	GLfloat* to3GLfloat() {
		array[0] = (GLfloat) x;
		array[1] = (GLfloat) y;
		array[2] = (GLfloat) z;
		array[3] = (GLfloat) 0;
		return array;
	}

	GLfloat* to4GLfloatW1() {
		array[0] = (GLfloat) x;
		array[1] = (GLfloat) y;
		array[2] = (GLfloat) z;
		array[3] = (GLfloat) 1;
		return array;
	}

	GLfloat* to4GLfloatW0() {
		array[0] = (GLfloat) x;
		array[1] = (GLfloat) y;
		array[2] = (GLfloat) z;
		array[3] = (GLfloat) 0;
		return array;
	}

	/* VECTOR <-> SCALAR OPERATIONS */
	void rotateAxisX(float t) {
		//rotates on X axis
		float cost = cos(t);
		float sint = sin(t);
		y = cost * y - sint * z;
		z = sint * y + cost * z;
	}

	void rotateAxisY(float t) {
		//rotates on Y axis
		float cost = cos(t);
		float sint = sin(t);
		x = cost * x + sint * z;
		z = -sint * x + cost * z;
	}

	void rotateAxisZ(float t) {
		//rotates on Z axis
		float cost = cos(t);
		float sint = sin(t);
		x = cost * x - sint * y;
		y = sint * x + cost * y;
	}

	void rotateVector(float t, float vx, float vy, float vz) {
		//rotates on specified (arbitrary) vector
		float cost = cos(t);
		float sint = sin(t);
		float oldx = x;
		float oldy = y;
		float oldz = z;

		x = (cost + (1 - cost) * vx * vx) * oldx + ((1 - cost) * vx * vy - vz * sint) * oldy
				+ ((1 - cost) * vx * vz + vy * sint) * oldz;

		y = ((1 - cost) * vx * vy + vz * sint) * oldx + (cost + (1 - cost) * vy * vy) * oldy
				+ ((1 - cost) * vy * vz - vx * sint) * oldz;

		z = ((1 - cost) * vx * vz - vy * sint) * oldx + ((1 - cost) * vy * vz + vx * sint) * oldy
				+ (cost + (1 - cost) * vz * vz) * oldz;
	}

	void multiply(float b) {
		x = x * b;
		y = y * b;
		z = z * b;
	}

	void divide(float b) {
		x = x / b;
		y = y / b;
		z = z / b;
	}

	void setup(float _x, float _y, float _z) {
		x = _x;
		y = _y;
		z = _z;
	}

	/* VECTOR <-> VECTOR OPERATIONS */
	void add(float x0, float y0, float z0) {
		x = x + x0;
		y = y + y0;
		z = z + z0;
	}

	void subtract(float x0, float y0, float z0) {
		x = x - x0;
		y = y - y0;
		z = z - z0;
	}

	/* MULTITYPE OPERATIONS */
	void addScaledByf(Vector3f* scaled, float scale) {
		x = scaled->x * scale + x;
		y = scaled->y * scale + y;
		z = scaled->z * scale + z;
	}

	void rotateVector(float t, Vector3f* base) {
		rotateVector(t, base->x, base->y, base->z);
	}
};
#endif
