/// Copyright (c) 2010 Ruben Penalva. All rights reserved.
/// Redistribution and use in source and binary forms, with or without modification, 
/// are permitted provided that the following conditions are met:
///    1. Redistributions of source code must retain the above copyright notice, this 
///    list of conditions and the following disclaimer.
///    2. Redistributions in binary form must reproduce the above copyright notice,
///    this list of conditions and the following disclaimer in the documentation and/or
///    other materials provided with the distribution.
///    3. The name of the author may not be used to endorse or promote products derived
///    from this software without specific prior written permission.
///
/// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
/// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
/// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 
/// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
/// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
/// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
/// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
/// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


#include "Vector3.h"

#include <math.h>
#include <float.h>

#include <xutility>
#include <assert.h>

using namespace TestFrameWork;

Vector3::Vector3(void)	:	m_x(0.0f), m_y(0.0f), m_z(0.0f)
{
}

Vector3::Vector3(float x, float y, float z)	:	m_x(x), m_y(y), m_z(z)
{
}

Vector3::Vector3(const Vector3& vector)	:	m_x(vector.m_x), m_y(vector.m_y), m_z(vector.m_z)
{
}

Vector3::~Vector3(void)
{
}

Vector3 Vector3::Cross(const Vector3& v) const
{
	return Vector3(m_y * v.m_z - m_z * v.m_y, 
				   m_z * v.m_x - m_x * v.m_z, 
				   m_x * v.m_y - m_y * v.m_x);
}

float Vector3::Dot(const Vector3& v) const
{
	return m_x * v.m_x + m_y * v.m_y + m_z * v.m_z;
}

Vector3& Vector3::Normalize(void)
{
	float norm = Length();
	if (norm == 0.0f)
	{
		return *this;
	}

	m_x /= norm;
	m_y /= norm;
	m_z /= norm;

	return *this;
}

float Vector3::Length(void) const
{
	return sqrtf(m_x * m_x + m_y * m_y + m_z * m_z);
}

Vector3& Vector3::operator=(const Vector3& vector)
{
	if (&vector == this)
	{
		return *this;
	}

	m_x = vector.m_x;
	m_y = vector.m_y; 
	m_z = vector.m_z;
			
	return *this;
}

Vector3& Vector3::operator+=(const Vector3& vector)
{
	m_x += vector.m_x;
	m_y += vector.m_y; 
	m_z += vector.m_z;

	return *this;
}

Vector3& Vector3::operator*=(const Vector3& vector)
{
	m_x *= vector.m_x;
	m_y *= vector.m_y; 
	m_z *= vector.m_z;

	return *this;
}

Vector3& Vector3::operator/=(float constant)
{
	m_x /= constant;
	m_y /= constant;
	m_z /= constant;

	return *this;
}

Vector3& Vector3::operator*=(float constant)
{
	m_x *= constant;
	m_y *= constant;
	m_z *= constant;

	return *this;
}

Vector3 Vector3::operator-(void)
{
	return Vector3(-m_x, -m_y, -m_z);
}

Vector3 TestFrameWork::operator+(const Vector3& leftV, const Vector3& rightV)
{	
	return Vector3(leftV.m_x + rightV.m_x, leftV.m_y + rightV.m_y, leftV.m_z + rightV.m_z);
}

Vector3 TestFrameWork::operator-(const Vector3& leftV, const Vector3& rightV)
{	
	return Vector3(leftV.m_x - rightV.m_x, leftV.m_y - rightV.m_y, leftV.m_z - rightV.m_z);
}

Vector3 TestFrameWork::operator*(const Vector3& leftV, const Vector3& rightV)
{
	return Vector3(leftV.m_x * rightV.m_x, leftV.m_y * rightV.m_y, leftV.m_z * rightV.m_z);
}

Vector3 TestFrameWork::operator*(const Vector3& leftV, float k)
{
	return Vector3(leftV.m_x * k, leftV.m_y * k, leftV.m_z * k);
}

Vector3 TestFrameWork::operator*(float k, const Vector3& rightV)
{
	return Vector3(rightV.m_x * k, rightV.m_y * k, rightV.m_z * k);
}

Vector3 TestFrameWork::Interpolate(const Vector3& leftV, const Vector3& rightV, float k)
{
	return leftV * (1.0f - k) + rightV * k;
}

Vector3 TestFrameWork::Max(const Vector3& leftV, const Vector3& rightV)
{
	Vector3 result;

	result.m_x = std::max(leftV.m_x, rightV.m_x);
	result.m_y = std::max(leftV.m_y, rightV.m_y);
	result.m_z = std::max(leftV.m_z, rightV.m_z);

	return result;
}

Vector3 TestFrameWork::Truncate(const Vector3& leftV, const Vector3& rightV)
{
	Vector3 result;

	result.m_x = leftV.m_x > rightV.m_x? rightV.m_x : leftV.m_x;
	result.m_y = leftV.m_y > rightV.m_y? rightV.m_y : leftV.m_y;
	result.m_z = leftV.m_z > rightV.m_z? rightV.m_z : leftV.m_z;

	return result;
}

Vector3 TestFrameWork::Normalize(const Vector3& vector)
{
	Vector3 result(vector);
	
	return result.Normalize();
}

bool TestFrameWork::IsFinite(const Vector3& vector)
{
	return _finite(vector.m_x) && _finite(vector.m_y) && _finite(vector.m_z);
}