#include "cVector.h"

#include <math.h>
#include <assert.h>

// copy constructor
cVector::cVector (const cVector& toCopy)
{
	x_ = toCopy.x_;
	y_ = toCopy.y_;
	z_ = toCopy.z_;
}

// assignment operator
cVector& cVector::operator = (const cVector& toCopy)
{

	if (this != &toCopy) {
		x_ = toCopy.x_;
		y_ = toCopy.y_;
		z_ = toCopy.z_;
	}
	return *this;
}

// comparison
const bool cVector::operator == (const cVector& compare) const
{
	
	if (x_==compare.x_ && y_ == compare.y_ && z_ == compare.z_)
		return true;
	return false;
}

// comparison - not
const bool cVector::operator != (const cVector& compare) const
{
	return !(*this==compare);
}


// addition
const cVector cVector::operator + (const cVector& toAdd) const
{
	cVector sum = *this;
	sum += toAdd;
	return sum;
}

// unary addition
cVector& cVector::operator += (const cVector& toAdd)
{
	x_ += toAdd.x_;
	y_ += toAdd.y_;
	z_ += toAdd.z_;
	return *this;
}

// subtraction
const cVector cVector::operator - (const cVector& toSub) const
{
	cVector diff=*this;
	diff -= toSub;
	return diff;
}

// unary subtraction
cVector& cVector::operator -= (const cVector& toSub)
{
	x_ -= toSub.x_;
	y_ -= toSub.y_;
 	z_ -= toSub.z_;
	return *this;
}


// negation
const cVector cVector::operator - () const
{
	cVector neg = *this;
	neg *= -1;
	return neg;
}



// unary scalar multiplication
cVector& cVector::operator *= (float scaleFactor)
{
	x_ *= scaleFactor;
	y_ *= scaleFactor;
    z_ *= scaleFactor;
	return *this;
}



// unary scalar multiplication
cVector& cVector::operator /= (float scaleFactor)
{
	assert(scaleFactor !=0);
	x_ /= scaleFactor;
	y_ /= scaleFactor;
	z_ /= scaleFactor;
	return *this;
}

// index
float& cVector::operator [] (const int i)
{
    assert(i>=0 && i < 3);
    switch(i)
    {
        case 0: return x_;
        case 1: return y_;
        case 2: return z_;
        default: break; // assert will have already handled this
    }
}

// read-only index
const float& cVector::operator [] (const int i) const
{
    assert(i>=0 && i < 3);
    switch(i)
    {
        case 0: return x_;
        case 1: return y_;
        case 2: return z_;
        default: break;
    }
}


// dot product
const float cVector::dot (const cVector& toDot) const
{
	float dotProduct = x_*toDot.x_ + y_*toDot.y_ + z_*toDot.z_;
	return dotProduct;
}

// cross product
const cVector cVector::cross (const cVector& toCross) const
{
  cVector crossProduct;
  crossProduct.x_ = y_ * toCross.z_ - z_ * toCross.y_;
  crossProduct.y_ = z_ * toCross.x_ - x_ * toCross.z_;
  crossProduct.z_ = x_ * toCross.y_ - y_ * toCross.x_;
  return crossProduct;
}


// normalize
cVector& cVector::normalize ()
{
  float len = this->length();

  assert(len!=0);
  x_ /= len;
  y_ /= len;
  z_ /= len;
  
  return *this;
}


// get length
const float cVector::length() const
{
  double sumSquares = x_*x_ + y_*y_ + z_*z_;
  
  return (float) sqrt(sumSquares);
}


// scalar multiplication
const cVector operator * (const cVector& toMultiply, const float& factor)
{
  cVector product = toMultiply;
  product *= factor;
  return product;
}

// scalar multiplication
const cVector operator * (const float& factor,const cVector& toMultiply)
{
  return toMultiply*factor;
}

// scalar division
const cVector operator / (const cVector& toDivide, const float& factor)
{
	cVector quotient = toDivide;
	quotient /= factor;
	return quotient;
}

// write
ostream &operator<<(ostream &out_file, const cVector& thecVector)
{
	out_file << thecVector.x_ << ", " << thecVector.y_ << ", " << thecVector.z_;
	return out_file;
}

cVector cVector::matrixMultiply(float m[])
{
    // m is a 4x4 matrix stored in column-major order
    // in a length 16 array
    // 
    // to compute the multiplication we should convert
    // this into a 4-tuple with a 1 in the new dimension
    // perform the multiplication, then strip off the
    // last dimension
    //
    // the following achieves the same effect
    // without the conversion
    
    cVector tmp(0,0,0);
    for (int j=0; j<3; j++) { // for each row of the matrix 
        for (int k=0; k<3; k++) {  // for each column
            tmp[j] += m[j+4*k]*(*this)[k];  // the matrix is column-major
        }
        tmp[j]+=m[j+12];
    }
    return tmp;
}