/* Tuple.cpp
 *
 * Definition of the Tuple class.
 */

#include "Tuple.h"
#include <cmath>
#include <assert.h>

using namespace std;

Tuple::Tuple()
: x_(0.0),
  y_(0.0),
  z_(0.0)
{
	// Nothing (more) to do
}

Tuple::Tuple(const double x, const double y, const double z)
: x_(x),
  y_(y),
  z_(z)
{
	// Nothing (more) to do
}

Tuple::Tuple(const Tuple& toCopy)
: x_(toCopy.x()),
  y_(toCopy.y()),
  z_(toCopy.z())
{
	// Nothing (more) to do
}

Tuple::~Tuple()
{
	// Virtual stub; nothing to do
}

/* = : assignment
 *
 * Sets x, y, and z to match.
 */
Tuple& Tuple::operator = (const Tuple& toCopy)
{
	if (this != &toCopy)
	{
		x_ = toCopy.x();
		y_ = toCopy.y();
		z_ = toCopy.z();
	}
	return *this;
}

/* == : comparison
 *
 * True if x, y, and z match.
 */
const bool Tuple::operator == (const Tuple& toCompare) const
{
	return (
		x_ == toCompare.x()
		&& y_ == toCompare.y()
		&& z_ == toCompare.z());
}

/* != : anti-comparison
 *
 * True if x, y, or z don't match.
 */
const bool Tuple::operator != (const Tuple& toCompare) const
{
	return !(*this == toCompare);
}

/* + : addition
 *
 * Element-wise sum of the two Tuples.
 */
const Tuple Tuple::operator + (const Tuple& toAdd) const
{
	Tuple sum = *this;
	sum += toAdd;
	return sum;
}

/* += : unary addition
 *
 * Unary element-wise sum of the two Tuples.
 */
Tuple& Tuple::operator += (const Tuple& toAdd)
{
	x_ += toAdd.x();
	y_ += toAdd.y();
	z_ += toAdd.z();
	return *this;
}

/* - : negation
 *
 * Equivalent to multiplying by -1.
 */
const Tuple Tuple::operator - () const
{
	Tuple neg = *this;
	neg *= -1;
	return neg;
}

/* - : subtraction
 *
 * Element-wise difference of the two Tuples.
 */
const Tuple Tuple::operator - (const Tuple& toSub) const
{
	Tuple diff = *this;
	diff -= toSub;
	return diff;
}

/* -= : unary subtraction
 *
 * Unary element-wise difference of the two Tuples.
 */
Tuple& Tuple::operator -= (const Tuple& toSub)
{
	x_ -= toSub.x();
	y_ -= toSub.y();
	z_ -= toSub.z();
	return *this;
}

/* *= : unary scalar multiplication
 *
 * Unary element-wise product of the Tuple and scalar.
 */
Tuple& Tuple::operator *= (double scaleFactor)
{
	x_ *= scaleFactor;
	y_ *= scaleFactor;
	z_ *= scaleFactor;
	return *this;
}

/* /= : unary scalar division
 *
 * Unary element-wise quotient of the Tuple and scalar.
 */
Tuple& Tuple::operator /= (double scaleFactor)
{
	assert(scaleFactor != 0);

	x_ /= scaleFactor;
	y_ /= scaleFactor;
	z_ /= scaleFactor;
	return *this;
}

double& Tuple::operator [] (int index)
{
    switch(index)
    {
    case 0:
		return x_;
		break;
	case 1:
		return y_;
		break;
    case 2:
		return z_;
		break;
    default:
		cerr << "Invalid Tuple index: " << index << endl;
		exit(1);
    }
}

/* dot
 *
 * Compute the dot product of two Tuples.
 */
const double Tuple::dot(const Tuple& toDot) const
{
	return x_ * toDot.x() + y_ * toDot.y() + z_ * toDot.z();
}

/* cross
 *
 * Compute the cross product of two Tuples.
 */
const Tuple Tuple::cross(const Tuple& toCross) const
{
	return Tuple(
		y_ * toCross.z() - z_ * toCross.y(),
		z_ * toCross.x() - x_ * toCross.z(),
		x_ * toCross.y() - y_ * toCross.x());
}

/* norm
 *
 * Normalize the Tuple (set length to 1 while maintaining direction).
 */
const Tuple Tuple::norm() const
{
	double len = length();
	assert(len != 0);

	return Tuple(x_ / len, y_ / len, z_ / len);
}

/* length
 *
 * Calculate the length of the Tuple.
 */
const double Tuple::length() const
{
	return sqrt(x_ * x_ + y_ * y_ + z_ * z_);
}

/* * : scalar multiplication
 *
 * Multiply each element of the given Tuple by the given scalar.
 */
const Tuple operator * (const Tuple& toMultiply, const double& scaleFactor)
{
	Tuple product = toMultiply;
	product *= scaleFactor;
	return product;
}

/* * : scalar multiplication, reverse order
 *
 * Multiply each element of the given Tuple by the given scalar.
 */
const Tuple operator * (const double& scaleFactor, const Tuple& toMultiply)
{
	return toMultiply * scaleFactor;
}

/* / : scalar division
 *
 * Divide each element of the given Tuple by the given scalar.
 */
const Tuple operator / (const Tuple& toDivide, const double& scaleFactor)
{
	Tuple quotient = toDivide;
	quotient /= scaleFactor;
	return quotient;
}

/* << : write
 *
 * Output the given Tuple to a ostream.
 */
ostream& operator << (ostream& out_file, const Tuple& theTuple)
{
	out_file << "("
		<< theTuple.x() << ", "
		<< theTuple.y() << ", "
		<< theTuple.z() << ")";
	return out_file;
}
