//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * 三次元ベクトルヘッダ
 * @author Junpee
 */

#ifndef VECTOR_3_H_
#define VECTOR_3_H_

#include <Core/System/Math.h>
#include <Core/Primitive/String.h>

namespace Lamp{

//------------------------------------------------------------------------------
/**
 * 三次元ベクトル
 *
 * このクラスは継承しないで下さい。
 */
class Vector3{
public:
	//--------------------------------------------------------------------------
	// メンバ変数
	//--------------------------------------------------------------------------
	/// メンバ変数
	union{
		/// 各要素
		struct{
			/// X値
			float x;
			/// Y値
			float y;
			/// Z値
			float z;
		};

		/// 配列
		float array[3];
	};

	//--------------------------------------------------------------------------
	// 定数
	//--------------------------------------------------------------------------
	/// ゼロベクトル
	static const Vector3 zero;

	/// X軸単位ベクトル
	static const Vector3 unitX;

	/// Y軸単位ベクトル
	static const Vector3 unitY;

	/// Z軸単位ベクトル
	static const Vector3 unitZ;

	/// スケール単位ベクトル
	static const Vector3 unitScale;

	//--------------------------------------------------------------------------
	// コンストラクタ
	//--------------------------------------------------------------------------
	/**
	 * コンストラクタ
	 *
	 * このコンストラクタは初期値の設定を行わないため値は不定です。
	 */
	inline Vector3(){}

	/**
	 * コンストラクタ
	 * @param sourceX Xの初期値
	 * @param sourceY Yの初期値
	 * @param sourceZ Zの初期値
	 */
	inline Vector3(float sourceX, float sourceY, float sourceZ) :
		x(sourceX), y(sourceY), z(sourceZ){
	}

	/**
	 * コンストラクタ
	 * @param source 初期値配列
	 */
	inline explicit Vector3(const float* const source) :
		x(source[0]), y(source[1]), z(source[2]){
	}

	//--------------------------------------------------------------------------
	// 値の設定
	//--------------------------------------------------------------------------
	/**
	 * 値の設定
	 * @param sourceX Xの設定値
	 * @param sourceY Yの設定値
	 * @param sourceZ Zの設定値
	 */
	inline void set(float sourceX, float sourceY, float sourceZ){
		x = sourceX;
		y = sourceY;
		z = sourceZ;
	}

	/**
	 * 値の設定
	 * @param source 設定値配列
	 */
	inline void set(const float* const source){
		x = source[0];
		y = source[1];
		z = source[2];
	}

	//--------------------------------------------------------------------------
	// 演算
	//--------------------------------------------------------------------------
	/**
	 * 加算
	 * @param addVector 加算するベクトル
	 * @return 加算されたベクトル
	 */
	inline Vector3 operator +(const Vector3& addVector) const{
		return Vector3(
			x + addVector.x,
			y + addVector.y,
			z + addVector.z);
	}

	/**
	 * 減算
	 * @param subVector 減算するベクトル
	 * @return 減算されたベクトル
	 */
	inline Vector3 operator -(const Vector3& subVector) const{
		return Vector3(
			x - subVector.x,
			y - subVector.y,
			z - subVector.z);
	}

	/**
	 * 乗算
	 * @param mulValue 乗算する値
	 * @return 乗算されたベクトル
	 */
	inline Vector3 operator *(float mulValue) const{
		return Vector3(x * mulValue, y * mulValue, z * mulValue);
	}

	/**
	 * 乗算
	 * @param mulValue 乗算する値
	 * @param mulVector 乗算するベクトル
	 * @return 乗算されたベクトル
	 */
	inline friend Vector3 operator *(float mulValue, const Vector3& mulVector){
		return Vector3(
			mulVector.x * mulValue,
			mulVector.y * mulValue,
			mulVector.z * mulValue);
	}

	/**
	 * +演算子
	 * @return ベクトルのコピー
	 */
	inline Vector3 operator +() const{ return *this; }

	/**
	 * -演算子
	 * @return 値の符号が反転したベクトル
	 */
	inline Vector3 operator -() const{ return Vector3(-x, -y, -z); }

	//--------------------------------------------------------------------------
	// 代入演算
	//--------------------------------------------------------------------------
	/**
	 * 代入加算
	 * @param addVector 加算するベクトル
	 * @return 加算されたベクトル
	 */
	inline Vector3& operator +=(const Vector3& addVector){
		x += addVector.x;
		y += addVector.y;
		z += addVector.z;
		return *this;
	}

	/**
	 * 代入減算
	 * @param subVector 減算するベクトル
	 * @return 減算されたベクトル
	 */
	inline Vector3& operator -=(const Vector3& subVector){
		x -= subVector.x;
		y -= subVector.y;
		z -= subVector.z;
		return *this;
	}

	/**
	 * 代入乗算
	 * @param mulValue 乗算する値
	 * @return 乗算されたベクトル
	 */
	inline Vector3& operator *=(float mulValue){
		x *= mulValue;
		y *= mulValue;
		z *= mulValue;
		return *this;
	}

	/**
	 * 逆ベクトル
	 */
	inline Vector3& inverse(){
		x = -x;
		y = -y;
		z = -z;
		return *this;
	}

	/**
	 * 絶対値
	 * @return 絶対値にされたベクトル
	 */
	inline Vector3& abs(){
		x = Math::abs(x);
		y = Math::abs(y);
		z = Math::abs(z);
		return *this;
	}

	//--------------------------------------------------------------------------
	// ベクトル演算
	//--------------------------------------------------------------------------
	/**
	 * 内積
	 * @param dotVector 内積をとるベクトル
	 * @return 内積値
	 */
	inline float dotProduct(const Vector3& dotVector) const{
		return x * dotVector.x + y * dotVector.y + z * dotVector.z;
	}

	/**
	 * 外積
	 * @param crossVector 外積をとるベクトル
	 * @return 外積ベクトル
	 */
	// テンポラリの作成が必要になるので代入バージョンは作成しない
	inline Vector3 crossProduct(const Vector3& crossVector) const{
		return Vector3(
			y * crossVector.z - z * crossVector.y,
			z * crossVector.x - x * crossVector.z,
			x * crossVector.y - y * crossVector.x
		);
	}

	//--------------------------------------------------------------------------
	// 長さ関連
	//--------------------------------------------------------------------------
	/**
	 * ベクトル長の取得
	 * @return ベクトル長
	 */
	inline float getLength() const{
		return Math::sqrt(x * x + y * y + z * z);
	}

	/**
	 * ベクトル長の設定
	 * @param length 設定するベクトル長
	 */
	inline Vector3& setLength(float length){
		float nowLength = getLength();
		// 長さが0の場合における対処はアプリケーションによって違うのでAssert
		Assert(nowLength >= Math::epsilon);
		(*this) *= (length / nowLength);
		return *this;
	}

	/**
	 * ベクトル長の二乗を取得
	 * @return ベクトル長の二乗
	 */
	inline float getSquaredLength() const{
		return (x * x + y * y + z * z);
	}

	/**
	 * 正規化
	 * @return 正規化されたベクトル
	 */
	inline Vector3& normalize(){
		float nowLength = getLength();
		// 長さが0の場合における対処はアプリケーションによって違うのでAssert
		Assert(nowLength >= Math::epsilon);
		(*this) *= (1.f / nowLength);
		return *this;
	}

	/**
	 * ゼロベクトルかどうか
	 * @return ゼロベクトルならtrueを返す
	 */
	inline bool isZero() const{
		return ((Math::abs(x) <= Math::epsilon) &&
			(Math::abs(y) <= Math::epsilon) &&
			(Math::abs(z) <= Math::epsilon));
	}

	/**
	 * 単位ベクトルかどうか
	 * @return 単位ベクトルならtrueを返す
	 */
	inline bool isUnit() const{
		return (Math::abs(getLength() - 1.f) <= Math::epsilon);
	}

	/**
	 * 最大値
	 * @return 最大値
	 */
	inline float maximumValue() const{
		float result = x;
		if(y > result){ result = y; }
		if(z > result){ result = z; }
		return result;
	}

	/**
	 * 最小値
	 * @return 最小値
	 */
	inline float minimumValue() const{
		float result = x;
		if(y < result){ result = y; }
		if(z < result){ result = z; }
		return result;
	}

	//--------------------------------------------------------------------------
	// 論理演算
	//--------------------------------------------------------------------------
	/**
	 * ベクトルが同じかどうか
	 * @param target 比較するベクトル
	 * @return 同じ値であればtrueを返す
	 */
	inline bool operator ==(const Vector3& target) const{
		return ((x == target.x) && (y == target.y) && (z == target.z));
	}

	/**
	 * ベクトルが同じかどうか
	 * @param target 比較するベクトル
	 * @param epsilon 誤差
	 * @return 誤差の範囲内で同じ値であればtrueを返す
	 */
	inline bool epsilonEquals(const Vector3& target, float epsilon) const{
		Assert(epsilon >= 0.f);
		return (
			(Math::abs(x - target.x) <= epsilon) &&
			(Math::abs(y - target.y) <= epsilon) &&
			(Math::abs(z - target.z) <= epsilon));
	}

	/**
	 * ベクトルが同じでないかどうか
	 * @param target 比較するベクトル
	 * @return 同じでない値であればtrueを返す
	 */
	inline bool operator !=(const Vector3& target) const{
		return ((x != target.x) || (y != target.y) || (z != target.z));
	}

	/**
	 * ベクトルが同じでないかどうか
	 * @param target 比較するベクトル
	 * @param epsilon 誤差
	 * @return 誤差の範囲内で同じでない値であればtrueを返す
	 */
	inline bool notEpsilonEquals(const Vector3& target, float epsilon) const{
		Assert(epsilon >= 0.f);
		return (
			(Math::abs(x - target.x) > epsilon) ||
			(Math::abs(y - target.y) > epsilon) ||
			(Math::abs(z - target.z) > epsilon));
	}

	//--------------------------------------------------------------------------
	// その他
	//--------------------------------------------------------------------------
	/**
	 * 文字列化
	 * @return ベクトルの文字列表記
	 */
	inline String toString() const{
		String returnString;
		returnString.format("( %.8f, %.8f, %.8f )", x, y, z);
		return returnString;
	}

	//--------------------------------------------------------------------------
private:

};

//------------------------------------------------------------------------------
} // End of namespace Lamp
#endif // End of VECTOR_3_H_
//------------------------------------------------------------------------------
