#include "vector.h"

#include "constants.h"

#include <iostream>

#include <math.h>

#include <assert.h>





using namespace std;



/*

 * Implement the Vector class.

 */



// Default constructor

Vector::Vector(void)

{

	d[0] = 0;

	d[1] = 0;

	d[2] = 0;

}



// Constructor with arguments

Vector::Vector(double _x, double _y, double _z)

{

	d[0] = _x;

	d[1] = _y;

	d[2] = _z;

}



// Copy constructor

Vector::Vector(const Vector& src)

{

	d[0] = src[0]; 

	d[1] = src[1];

	d[2] = src[2];

}



Vector::~Vector(void)

{}



// Equality operator with EPSILON

const bool Vector::operator==(const Vector& src) const

{

	return ((fabs(d[0] - src[0]) < EPSILON) &&

			(fabs(d[1] - src[1]) < EPSILON) &&

			(fabs(d[2] - src[2]) < EPSILON));

}



// Inequality operator uses equality operator

const bool Vector::operator!=(const Vector& src) const

{

	return (!(*this == src));

}



// Assignment operator

Vector& Vector::operator=(const Vector& src)

{

	this->d[0] = src[0];

	this->d[1] = src[1];

	this->d[2] = src[2];

	return *this;

}



// Addition operator

const Vector Vector::operator+(const Vector& src) const

{

	return Vector(src[0] + d[0], src[1] + d[1], src[2] + d[2]); 

}



// Subtraction operator

const Vector Vector::operator-(const Vector& src) const

{

	return Vector(d[0] - src[0], d[1] - src[1], d[2] - src[2]);

}



// Negation operator

const Vector Vector::operator-(void) const

{

	return Vector(-d[0], -d[1], -d[2]);

}



// Times operator

const Vector Vector::operator*(double scale) const

{

	return Vector(scale * d[0], scale * d[1], scale * d[2]); 

}



// Divide operator

const Vector Vector::operator/(double scale) const

{

	return Vector(d[0] / scale, d[1] / scale, d[2] / scale); 

}



// Plus-equals operator

Vector& Vector::operator+=(const Vector& src)

{

	d[0] = src[0] + d[0];

	d[1] = src[1] + d[1];

	d[2] = src[2] + d[2];

	return *this; 

}



// Minus-equals operator

Vector& Vector::operator-=(const Vector& src)

{

	d[0] = d[0] - src[0];

	d[1] = d[1] - src[1];

	d[2] = d[2] - src[2];

	return *this; 

}



// Times-equals operator

Vector& Vector::operator*=(double scale)

{

	d[0] = scale * d[0];

	d[1] = scale * d[1];

	d[2] = scale * d[2];

	return *this; 

}



// Division-equals operator

Vector& Vector::operator/=(double scale)

{

	d[0] = d[0] / scale;

	d[1] = d[1] / scale;

	d[2] = d[2] / scale;

	return *this; 

}



// Indexing operator

double& Vector::operator[](const int index)

{

	return d[index];

}



// Second indexing operator

const double& Vector::operator[](const int index) const

{

	return d[index];

}



// Convert to unit vector

Vector Vector::normalize(void)

{

	double magnitude = sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]);

	assert(magnitude != 0);

	return Vector(d[0] / magnitude,

			      d[1] / magnitude,

	              d[2] / magnitude);

	

}



// Cross-product function

Vector Vector::cross(const Vector& v) const

{

	double tmp[3];

	tmp[0] = d[1] * v[2] - d[2] * v[1];

	tmp[1] = d[2] * v[0] - d[0] * v[2];

	tmp[2] = d[0] * v[1] - d[1] * v[0];

	return Vector(tmp[0], tmp[1], tmp[2]);

}



// Dot-product

double Vector::dot(const Vector& v) const

{

	return d[0] * v[0] + d[1] * v[1] + d[2] * v[2];

}



// Calculate length

const double Vector::length(void) const

{

	return sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]);

}



double Vector::distance(Vector v)

{

	return sqrt((d[0]-v[0])*(d[0]-v[0]) + (d[1]-v[1])*(d[1]-v[1]) + (d[2]-v[2])*(d[2]-v[2]));

}



// Reflect vector about provided normal vector

Vector Vector::reflect(Vector norm) const

{

	Vector nhat = norm.normalize();

	Vector b = *this;

	double a = nhat.dot(b * -1);

	return Vector(b + nhat * (a * 2));



}



void Vector::print()

{

	cout << "<" << d[0] << ", " << d[1] << ", " << d[2] << ">" << endl;

}









Vector average(Vector l1, Vector l2)

{

	return ((l1 + l2) / 2);

}



// Determine if three vectors are collinear; i.e. there's

// a scalar factor f such that v1 * f = v2, and a factor

// g such that v2 * g = v3.

bool collinear(Vector v1, Vector v2, Vector v3)

{

	double f = v1[0] / v2[0];

	bool test1 = ((v2[1] == (v1[1] / f)) &&

		          (v2[2] == (v1[2] / f)));

	double g = v2[0] / v3[0];

	bool test2 = ((v3[1] == (v2[1] / g)) &&

		          (v3[2] == (v2[2] / g)));

	return (test1 && test2);

}

