//------------------------------------------------------------------------------
// 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 TRIANGLE_H_
#define TRIANGLE_H_

#include <Core/Primitive/Vector3.h>
#include <Core/Primitive/Matrix33.h>
#include <Core/Primitive/Matrix34.h>
#include <Core/Primitive/Matrix44.h>

namespace Lamp{

class Intersection;
class AxisAlignedBox;
class Capsule;
class Cone;
class Line;
class OrientedBox;
class Plane;
class Ray;
class Segment;
class Sphere;

//------------------------------------------------------------------------------
/**
 * 三角
 *
 * このクラスは継承しないで下さい。
 */
class Triangle{
public:
	//--------------------------------------------------------------------------
	// 定数
	//--------------------------------------------------------------------------
	/// ゼロ三角
	static const Triangle zero;

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

	/**
	 * コンストラクタ
	 * @param vertex0 頂点0の初期値
	 * @param vertex1 頂点1の初期値
	 * @param vertex2 頂点2の初期値
	 */
	inline Triangle(const Vector3& vertex0, const Vector3& vertex1,
		const Vector3& vertex2){
		vertex_[0] = vertex0;
		vertex_[1] = vertex1;
		vertex_[2] = vertex2;
	}

	/**
	 * コンストラクタ
	 * @param vertex0x 頂点0のX初期値
	 * @param vertex0y 頂点0のY初期値
	 * @param vertex0z 頂点0のZ初期値
	 * @param vertex1x 頂点1のX初期値
	 * @param vertex1y 頂点1のY初期値
	 * @param vertex1z 頂点1のZ初期値
	 * @param vertex2x 頂点2のX初期値
	 * @param vertex2y 頂点2のY初期値
	 * @param vertex2z 頂点2のZ初期値
	 */
	inline Triangle(float vertex0x, float vertex0y, float vertex0z,
		float vertex1x, float vertex1y, float vertex1z,
		float vertex2x, float vertex2y, float vertex2z){
		vertex_[0].set(vertex0x, vertex0y, vertex0z);
		vertex_[1].set(vertex1x, vertex1y, vertex1z);
		vertex_[2].set(vertex2x, vertex2y, vertex2z);
	}

	/**
	 * コンストラクタ
	 * @param source 初期値配列
	 */
	inline explicit Triangle(const Vector3* const source){
		vertex_[0] = source[0];
		vertex_[1] = source[1];
		vertex_[2] = source[2];
	}

	//--------------------------------------------------------------------------
	// 値の設定
	//--------------------------------------------------------------------------
	/**
	 * 値の設定
	 * @param vertex0 設定する頂点0
	 * @param vertex1 設定する頂点1
	 * @param vertex2 設定する頂点2
	 */
	inline void set(const Vector3& vertex0, const Vector3& vertex1,
		const Vector3& vertex2){
		vertex_[0] = vertex0;
		vertex_[1] = vertex1;
		vertex_[2] = vertex2;
	}

	/**
	 * 値の設定
	 * @param vertex0x 設定する頂点0のX値
	 * @param vertex0y 設定する頂点0のY値
	 * @param vertex0z 設定する頂点0のZ値
	 * @param vertex1x 設定する頂点1のX値
	 * @param vertex1y 設定する頂点1のY値
	 * @param vertex1z 設定する頂点1のZ値
	 * @param vertex2x 設定する頂点2のX値
	 * @param vertex2y 設定する頂点2のY値
	 * @param vertex2z 設定する頂点2のZ値
	 */
	inline void set(float vertex0x, float vertex0y, float vertex0z,
		float vertex1x, float vertex1y, float vertex1z,
		float vertex2x, float vertex2y, float vertex2z){
		vertex_[0].set(vertex0x, vertex0y, vertex0z);
		vertex_[1].set(vertex1x, vertex1y, vertex1z);
		vertex_[2].set(vertex2x, vertex2y, vertex2z);
	}

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

	/**
	 * 頂点の設定
	 * @param index 頂点のインデックス
	 * @param vertex 設定する頂点
	 */
	inline void setVertex(int index, const Vector3& vertex){
		Assert((index >= 0) && (index < 3));
		vertex_[index] = vertex;
	}

	//--------------------------------------------------------------------------
	// 値の取得
	//--------------------------------------------------------------------------
	/**
	 * 頂点の取得
	 * @param index 頂点のインデックス
	 * @return 頂点
	 */
	inline const Vector3& getVertex(int index) const{
		Assert((index >= 0) && (index < 3));
		return vertex_[index];
	}

	/**
	 * 法線の取得
	 * @return 法線
	 */
	inline Vector3 getNormal() const{
		return Math3D::calculateNormal(vertex_[0], vertex_[1], vertex_[2]);
	}

	//--------------------------------------------------------------------------
	// 三角演算
	//--------------------------------------------------------------------------
	/**
	 * ゼロ三角かどうか
	 * @return ゼロ三角ならtrueを返す
	 */
	inline bool isZero() const{
		return epsilonEquals(zero, Math::epsilon);
	}

	//--------------------------------------------------------------------------
	// トランスフォーム
	//--------------------------------------------------------------------------
	/**
	 * トランスフォーム
	 * @param matrix 乗算する行列
	 * @return 変換後の三角
	 */
	inline Triangle transform(const Matrix33& matrix) const{
		return Triangle(
			matrix * vertex_[0], matrix * vertex_[1], matrix * vertex_[2]);
	}

	/**
	 * トランスフォーム
	 * @param matrix 乗算する行列
	 * @return 変換後の三角
	 */
	inline Triangle transform(const Matrix34& matrix) const{
		return Triangle(
			matrix * vertex_[0], matrix * vertex_[1], matrix * vertex_[2]);
	}

	/**
	 * トランスフォーム
	 * @param matrix 乗算する行列
	 * @return 変換後の三角
	 */
	inline Triangle transform(const Matrix44& matrix) const{
		return Triangle(
			matrix * vertex_[0], matrix * vertex_[1], matrix * vertex_[2]);
	}

	//--------------------------------------------------------------------------
	// 距離
	//--------------------------------------------------------------------------
	/**
	 * 点距離
	 * @param point 距離判定する点
	 * @return 距離
	 */
	float getDistance(const Vector3& point) const{
		return Math::sqrt(getSquaredDistance(point));
	}

	/**
	 * 点距離の二乗
	 * @param point 距離判定する点
	 * @return 距離の二乗
	 */
	float getSquaredDistance(const Vector3& point) const;

	//--------------------------------------------------------------------------
	/**
	 * 軸沿いボックス距離
	 * @param axisAlignedBox 距離判定する軸沿いボックス
	 * @return 距離
	 */
	float getDistance(const AxisAlignedBox& axisAlignedBox) const{
		return Math::sqrt(getSquaredDistance(axisAlignedBox));
	}

	/**
	 * 軸沿いボックス距離の二乗
	 * @param axisAlignedBox 距離判定する軸沿いボックス
	 * @return 距離の二乗
	 */
	float getSquaredDistance(const AxisAlignedBox& axisAlignedBox) const;

	//--------------------------------------------------------------------------
	/**
	 * カプセル距離
	 * @param capsule 距離判定するカプセル
	 * @return 距離
	 */
	float getDistance(const Capsule& capsule) const{
		return Math::sqrt(getSquaredDistance(capsule));
	}

	/**
	 * カプセル距離の二乗
	 * @param capsule 距離判定するカプセル
	 * @return 距離の二乗
	 */
	float getSquaredDistance(const Capsule& capsule) const;

	//--------------------------------------------------------------------------
	/**
	 * コーン距離
	 * @param cone 距離判定するコーン
	 * @return 距離
	 */
	float getDistance(const Cone& cone) const{
		return Math::sqrt(getSquaredDistance(cone));
	}

	/**
	 * コーン距離の二乗
	 * @param cone 距離判定するコーン
	 * @return 距離の二乗
	 */
	float getSquaredDistance(const Cone& cone) const;

	//--------------------------------------------------------------------------
	/**
	 * ライン距離
	 * @param line 距離判定するライン
	 * @return 距離
	 */
	float getDistance(const Line& line) const{
		return Math::sqrt(getSquaredDistance(line));
	}

	/**
	 * ライン距離の二乗
	 * @param line 距離判定するライン
	 * @return 距離の二乗
	 */
	float getSquaredDistance(const Line& line) const;

	//--------------------------------------------------------------------------
	/**
	 * 指向性ボックス距離
	 * @param orientedBox 距離判定する指向性ボックス
	 * @return 距離
	 */
	float getDistance(const OrientedBox& orientedBox) const{
		return Math::sqrt(getSquaredDistance(orientedBox));
	}

	/**
	 * 指向性ボックス距離の二乗
	 * @param orientedBox 距離判定する指向性ボックス
	 * @return 距離の二乗
	 */
	float getSquaredDistance(const OrientedBox& orientedBox) const;

	//--------------------------------------------------------------------------
	/**
	 * 平面距離
	 * @param plane 距離判定する平面
	 * @return 距離
	 */
	float getDistance(const Plane& plane) const;

	/**
	 * 平面距離の二乗
	 * @param plane 距離判定する平面
	 * @return 距離の二乗
	 */
	float getSquaredDistance(const Plane& plane) const{
		float distance = getDistance(plane);
		return (distance * distance);
	}

	//--------------------------------------------------------------------------
	/**
	 * レイ距離
	 * @param ray 距離判定するレイ
	 * @return 距離
	 */
	float getDistance(const Ray& ray) const{
		return Math::sqrt(getSquaredDistance(ray));
	}

	/**
	 * レイ距離の二乗
	 * @param ray 距離判定するレイ
	 * @return 距離の二乗
	 */
	float getSquaredDistance(const Ray& ray) const;

	//--------------------------------------------------------------------------
	/**
	 * セグメント距離
	 * @param segment 距離判定するセグメント
	 * @return 距離
	 */
	float getDistance(const Segment& segment) const{
		return Math::sqrt(getSquaredDistance(segment));
	}

	/**
	 * セグメント距離の二乗
	 * @param segment 距離判定するセグメント
	 * @return 距離の二乗
	 */
	float getSquaredDistance(const Segment& segment) const;

	//--------------------------------------------------------------------------
	/**
	 * 球距離
	 * @param sphere 距離判定する球
	 * @return 距離
	 */
	float getDistance(const Sphere& sphere) const{
		return Math::sqrt(getSquaredDistance(sphere));
	}

	/**
	 * 球距離の二乗
	 * @param sphere 距離判定する球
	 * @return 距離の二乗
	 */
	float getSquaredDistance(const Sphere& sphere) const;

	//--------------------------------------------------------------------------
	/**
	 * 三角距離
	 * @param triangle 距離判定する三角
	 * @return 距離
	 */
	float getDistance(const Triangle& triangle) const{
		return Math::sqrt(getSquaredDistance(triangle));
	}

	/**
	 * 三角距離の二乗
	 * @param triangle 距離判定する三角
	 * @return 距離の二乗
	 */
	float getSquaredDistance(const Triangle& triangle) const;

	//--------------------------------------------------------------------------
	// 交差
	//--------------------------------------------------------------------------
	/**
	 * 点交差
	 * @param point 交差判定する点
	 * @param range 交差範囲
	 * @return 交差していればtrue
	 */
	bool intersect(const Vector3& point, float range = Math::epsilon) const;

	//--------------------------------------------------------------------------
	/**
	 * 軸沿いボックス交差
	 * @param axisAlignedBox 交差判定する軸沿いボックス
	 * @return 交差していればtrue
	 */
	bool intersect(const AxisAlignedBox& axisAlignedBox) const;

	//--------------------------------------------------------------------------
	/**
	 * カプセル交差
	 * @param capsule 交差判定するカプセル
	 * @return 交差していればtrue
	 */
	bool intersect(const Capsule& capsule) const;

	//--------------------------------------------------------------------------
	/**
	 * コーン交差
	 * @param cone 交差判定するコーン
	 * @return 交差していればtrue
	 */
	bool intersect(const Cone& cone) const;

	//--------------------------------------------------------------------------
	/**
	 * ライン交差
	 * @param line 交差判定するライン
	 * @return 交差していればtrue
	 */
	bool intersect(const Line& line) const;

	//--------------------------------------------------------------------------
	/**
	 * 指向性ボックス交差
	 * @param orientedBox 交差判定する指向性ボックス
	 * @return 交差していればtrue
	 */
	bool intersect(const OrientedBox& orientedBox) const;

	//--------------------------------------------------------------------------
	/**
	 * 平面交差
	 * @param plane 交差判定する平面
	 * @return 交差していればtrue
	 */
	bool intersect(const Plane& plane) const;

	//--------------------------------------------------------------------------
	/**
	 * レイ交差
	 * @param ray 交差判定するレイ
	 * @return 交差していればtrue
	 */
	bool intersect(const Ray& ray) const;

	//--------------------------------------------------------------------------
	/**
	 * セグメント交差
	 * @param segment 交差判定するセグメント
	 * @return 交差していればtrue
	 */
	bool intersect(const Segment& segment) const;

	//--------------------------------------------------------------------------
	/**
	 * 球交差
	 * @param sphere 交差判定する球
	 * @return 交差していればtrue
	 */
	bool intersect(const Sphere& sphere) const;

	/**
	 * 球交差
	 * @param intersection 交差、返り値がtrueなら設定されている
	 * @param sphere 交差判定する球
	 * @return 交差していればtrue
	 */
	bool intersect(Intersection* intersection, const Sphere& sphere) const;

	//--------------------------------------------------------------------------
	/**
	 * 三角交差
	 * @param triangle 交差判定する三角
	 * @return 交差していればtrue
	 */
	bool intersect(const Triangle& triangle) const;

	//--------------------------------------------------------------------------
	// 論理演算
	//--------------------------------------------------------------------------
	/**
	 * 三角が同じかどうか
	 * @param target 比較する三角
	 * @return 同じ値であればtrueを返す
	 */
	inline bool operator ==(const Triangle& target) const{
		return (
			(vertex_[0] == target.vertex_[0]) &&
			(vertex_[1] == target.vertex_[1]) &&
			(vertex_[2] == target.vertex_[2]));
	}

	/**
	 * 三角が同じかどうか
	 * @param target 比較する三角
	 * @param epsilon 誤差
	 * @return 誤差の範囲内で同じ値であればtrueを返す
	 */
	inline bool epsilonEquals(
		const Triangle& target, float epsilon) const{
		Assert(epsilon >= 0.f);
		return (
			(vertex_[0].epsilonEquals(target.vertex_[0], epsilon)) &&
			(vertex_[1].epsilonEquals(target.vertex_[1], epsilon)) &&
			(vertex_[2].epsilonEquals(target.vertex_[2], epsilon)));
	}

	/**
	 * 三角が同じでないかどうか
	 * @param target 比較する三角
	 * @return 同じでない値であればtrueを返す
	 */
	inline bool operator !=(const Triangle& target) const{
		return (
			(vertex_[0] != target.vertex_[0]) ||
			(vertex_[1] != target.vertex_[1]) ||
			(vertex_[2] != target.vertex_[2]));
	}

	/**
	 * 三角が同じでないかどうか
	 * @param target 比較する三角
	 * @param epsilon 誤差
	 * @return 誤差の範囲内で同じでない値であればtrueを返す
	 */
	inline bool notEpsilonEquals(
		const Triangle& target, float epsilon) const{
		Assert(epsilon >= 0.f);
		return (
			(vertex_[0].notEpsilonEquals(target.vertex_[0], epsilon)) ||
			(vertex_[1].notEpsilonEquals(target.vertex_[1], epsilon)) ||
			(vertex_[2].notEpsilonEquals(target.vertex_[2], epsilon)));
	}

	//--------------------------------------------------------------------------
	// その他
	//--------------------------------------------------------------------------
	/**
	 * 文字列化
	 * @return 三角の文字列表記
	 */
	inline String toString() const{
		String returnString;
		returnString.format("{ ( %.8f, %.8f, %.8f ) ( %.8f, %.8f, %.8f ) "
			"( %.8f, %.8f, %.8f ) }",
			vertex_[0].x, vertex_[0].y, vertex_[0].z,
			vertex_[1].x, vertex_[1].y, vertex_[1].z,
			vertex_[2].x, vertex_[2].y, vertex_[2].z);
		return returnString;
	}

private:
	//--------------------------------------------------------------------------
	// メンバ変数
	//--------------------------------------------------------------------------
	/// 頂点
	Vector3 vertex_[3];

};

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