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

#include <Graphics/Scene/SceneObject.h>
#include <Graphics/Renderer/Clipping.h>

namespace Lamp{

//------------------------------------------------------------------------------
/**
 * カメラ
 */
class Camera : public SceneObject{
friend class SceneObjectManagerTemplate<Camera>;
friend class CameraManager;
public:
	/**
	 * リファレンスカウントの取得
	 * @return リファレンスカウント
	 */
	virtual int getReferenceCount() const{ return 0; }

	/**
	 * コピー
	 * @return コピーされたカメラ
	 */
	virtual Camera* copy() const;

	//--------------------------------------------------------------------------
	// 射影行列
	//--------------------------------------------------------------------------
	/**
	 * 射影行列の取得
	 * @return 射影行列
	 */
	virtual const Matrix44& getProjectionMatrix(){ return projectionMatrix_; }

	//--------------------------------------------------------------------------
	/**
	 * 中心のずれたパースペクティブ設定
	 * @param left 最小X値
	 * @param right 最大X値
	 * @param bottom 最小Y値
	 * @param top 最大Y値
	 * @param nearClip 最小Z値
	 * @param farClip 最大Z値
	 */
	virtual void setPerspectiveOffCenter(
		float left, float right, float bottom, float top,
		float nearClip, float farClip){
		left_ = left;
		right_  = right;
		bottom_ = bottom;
		top_ = top;
		nearClip_ = nearClip;
		farClip_ = farClip;
		isPerspective_ = true;
		buildPerspectiveMatrix();
	}

	/**
	 * パースペクティブ設定
	 * @param width 幅
	 * @param height 高さ
	 * @param nearClip 最小Z値
	 * @param farClip 最大Z値
	 */
	virtual void setPerspective(
		float width, float height, float nearClip, float farClip){
		left_ = -width * 0.5f;
		right_  = -left_;
		bottom_ = -height * 0.5f;
		top_ = -bottom_;
		nearClip_ = nearClip;
		farClip_ = farClip;
		isPerspective_ = true;
		buildPerspectiveMatrix();
	}

	/**
	 * 画角によるパースペクティブ設定
	 * @param fovY Y軸方向の画角
	 * @param aspect アスペクト比(width / height)
	 * @param nearClip 最小Z値
	 * @param farClip 最大Z値
	 */
	virtual void setPerspectiveFovY(
		float fovY, float aspect, float nearClip, float farClip){
		bottom_ = -nearClip / (1.f / Math::tan(fovY * 0.5f));
		top_ = -bottom_;
		left_ = bottom_ * aspect;
		right_  = -left_;
		nearClip_ = nearClip;
		farClip_ = farClip;
		isPerspective_ = true;
		buildPerspectiveMatrix();
	}

	/**
	 * 中心のずれた並行投影設定
	 * @param left 最小X値
	 * @param right 最大X値
	 * @param bottom 最小Y値
	 * @param top 最大Y値
	 * @param nearClip 最小Z値
	 * @param farClip 最大Z値
	 */
	virtual void setOrthoOffCenter(
		float left, float right, float bottom, float top,
		float nearClip, float farClip){
		left_ = left;
		right_  = right;
		bottom_ = bottom;
		top_ = top;
		nearClip_ = nearClip;
		farClip_ = farClip;
		isPerspective_ = false;
		buildPerspectiveMatrix();
	}

	/**
	 * 並行投影設定
	 * @param width 幅
	 * @param height 高さ
	 * @param nearClip 最小Z値
	 * @param farClip 最大Z値
	 */
	virtual void setOrtho(
		float width, float height, float nearClip, float farClip){
		left_ = -width * 0.5f;
		right_  = -left_;
		bottom_ = -height * 0.5f;
		top_ = -bottom_;
		nearClip_ = nearClip;
		farClip_ = farClip;
		isPerspective_ = false;
		buildPerspectiveMatrix();
	}

	//--------------------------------------------------------------------------
	/**
	 * 左の取得
	 * @return 左
	 */
	virtual float getLeft(){ return left_; }

	/**
	 * 右の取得
	 * @return 右
	 */
	virtual float getRight(){ return right_; }

	/**
	 * 下の取得
	 * @return 下
	 */
	virtual float getBottom(){ return bottom_; }

	/**
	 * 上の取得
	 * @return 上
	 */
	virtual float getTop(){ return top_; }

	//--------------------------------------------------------------------------
	/**
	 * ニアクリップの取得
	 * @return ニアクリップ
	 */
	virtual float getNearClip(){ return nearClip_; }

	/**
	 * ニアクリップの設定
	 * @param nearClip ニアクリップ
	 */
	virtual void setNearClip(float nearClip){
		if(isPerspective_){
			setPerspectiveOffCenter(getLeft(), getRight(),
				getBottom(), getTop(), nearClip, getFarClip());
		}else{
			setOrthoOffCenter(getLeft(), getRight(),
				getBottom(), getTop(), nearClip, getFarClip());
		}
	}

	//--------------------------------------------------------------------------
	/**
	 * ファークリップの取得
	 * @return ファークリップ
	 */
	virtual float getFarClip(){ return farClip_; }

	/**
	 * ファークリップの設定
	 * @param farClip ファークリップ
	 */
	virtual void setFarClip(float farClip){
		if(isPerspective_){
			setPerspectiveOffCenter(getLeft(), getRight(),
				getBottom(), getTop(), getNearClip(), farClip);
		}else{
			setOrthoOffCenter(getLeft(), getRight(),
				getBottom(), getTop(), getNearClip(), farClip);
		}
	}

	//--------------------------------------------------------------------------
	/**
	 * パースペクティブかどうか
	 * @return パースペクティブならtrue
	 */
	virtual bool isPerspective(){ return isPerspective_; }

	//--------------------------------------------------------------------------
	/**
	 * 幅の取得
	 * @return 幅
	 */
	virtual float getWidth(){ return right_ - left_; }

	/**
	 * 高さの取得
	 * @return 高さ
	 */
	virtual float getHeight(){ return top_ - bottom_; }

	//--------------------------------------------------------------------------
	/**
	 * 画角の取得
	 * @return 画角
	 */
	virtual float getFovY(){
		if(!isPerspective_){ return 0.f; }
		return 2.f * Math::atan(1.f / (2.f * nearClip_ / (top_ - bottom_)));
	}

	/**
	 * 画角の設定
	 * @param fovY 画角
	 */
	virtual void setFovY(float fovY){
// OffCenterで設定されているとおかしくなる
		Assert(isPerspective_);
		setPerspectiveFovY(fovY, getAspect(), getNearClip(), getFarClip());
	}

	//--------------------------------------------------------------------------
	/**
	 * アスペクト比の取得
	 * @return アスペクト比
	 */
	virtual float getAspect(){ return (right_ - left_) / (top_ - bottom_); }

	/**
	 * アスペクト比の設定
	 * @param aspect アスペクト比
	 */
	virtual void setAspect(float aspect){
// OffCenterで設定されているとおかしくなる
		Assert(isPerspective_);
		setPerspectiveFovY(getFovY(), aspect, getNearClip(), getFarClip());
	}

	//--------------------------------------------------------------------------
	// ビュー行列
	//--------------------------------------------------------------------------
	/**
	 * ビュー行列の取得
	 * @return ビュー行列
	 */
	virtual const Matrix44& getViewMatrix(){ return viewMatrix_; }

	/**
	 * カメラ位置の取得
	 * @return カメラ位置
	 */
	virtual const Vector3& getPosition(){ return position_; }

	/**
	 * カメラ回転の取得
	 * @return カメラ回転
	 */
	virtual const Vector3& getRotation(){ return rotation_; }

	//--------------------------------------------------------------------------
	/**
	 * ビュー行列の設定
	 * @param viewMatrix ビュー行列
	 */
	virtual void setViewMatrix(const Matrix44& viewMatrix);

	/**
	 * トランスフォーメーションの設定
	 * @param rotationXYZ ビューのXYZ回転
	 * @param position ビューの位置
	 */
	virtual void setTransformation(
		const Vector3& rotationXYZ, const Vector3& position);

	/**
	 * トランスフォーメーションの設定
	 * @param rotation ビューの四元数回転
	 * @param position ビューの位置
	 */
	virtual void setTransformation(
		const Quaternion& rotation, const Vector3& position);

	/**
	 * ルックアットの設定
	 * @param position ビュー位置
	 * @param target 目標
	 * @param up 上ベクトル
	 */
	virtual void setLookAt(
		const Vector3& position, const Vector3& target, const Vector3& up);

	//--------------------------------------------------------------------------
	// クリップ関係
	//--------------------------------------------------------------------------
	/**
	 * クリッピング
	 * @param boundingSphere バウンディングスフィア
	 * @return クリッピングステート
	 */
	virtual Clipping::State clipping(const Sphere& boundingSphere);

	/**
	 * クリッピング
	 * @param boundingBox バウンディングボックス
	 * @return クリッピングステート
	 */
	virtual Clipping::State clipping(const AxisAlignedBox& boundingBox);

	/**
	 * コーナーの取得
	 *
	 * 以下の図のインデックスに沿ってコーナーを取得します。
	 * <pre>
	 *    y+
	 *    |  5----4
	 *    | /|   /|
	 *    |1-+--2 |
	 *    || 6--+-7
	 *    ||/   |/
	 *    |0----3
	 *    +-------- x+
	 *   /
	 *  z+
	 * </pre>
	 * @param index コーナーインデックス
	 * @return コーナー
	 */
	virtual const Vector3& getCorner(int index){
		Assert(index >= 0);
		Assert(index < 8);
		return corner_[index];
	}

	/**
	 * バウンディングスフィアの取得
	 * @return バウンディングスフィア
	 */
	virtual const Sphere& getBoundingSphere(){ return boundingSphere_; }

	//--------------------------------------------------------------------------
	// RTTI
	//--------------------------------------------------------------------------
	/**
	 * カメラかどうか
	 * @return カメラならtrue
	 */
	virtual bool isCamera() const{ return true; }

	//--------------------------------------------------------------------------
protected:
	/**
	 * コンストラクタ
	 * @param name 名前
	 * @param scene シーン
	 */
	Camera(const String& name, Scene* scene);

	/**
	 * デストラクタ
	 */
	virtual ~Camera();

	//--------------------------------------------------------------------------
	/**
	 * 射影行列の構築
	 */
	virtual void buildPerspectiveMatrix();

	/**
	 * クリッピングのセットアップ
	 */
	virtual void clippingSetup();

	//--------------------------------------------------------------------------
private:
	// 射影行列
	Matrix44 projectionMatrix_;
	// ビュー行列
	Matrix44 viewMatrix_;
	// クリップ面
	Plane clipPlane_[6];
	// バウンディングスフィア
	Sphere boundingSphere_;
	// コーナー
	Vector3 corner_[8];
	// 位置
	Vector3 position_;
	// 回転
	Vector3 rotation_;

	// 左
	float left_;
	// 右
	float right_;
	// 下
	float bottom_;
	// 上
	float top_;
	// ニアクリップ
	float nearClip_;
	// ファークリップ
	float farClip_;
	// パースペクティブかどうか
	bool isPerspective_;
};

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