#pragma once
#include "ui-mousectrl.hh"


/*= T_Camera =================================================================*/

/* Data for a camera.
 *
 * For the field of view, both the near plane distance and the angle are
 * stored. Updating one causes the other to be updated.
 *
 * For the camera position and orientation, the data is stored as two different
 * sets:
 *	- target, angles, distance
 *	- position, direction and up vectors
 * Modifying one of the sets updates the other.
 */
struct T_Camera
{
	T_Camera( ) noexcept;

	DEF_COPY( T_Camera );
	DEF_MOVE( T_Camera );

	// ---------------------------------------------------------------------
	// FIELD OF VIEW / NEAR PLANE

    public:
	void fieldOfView( const float angle ) noexcept
	{
		fov_ = std::min( 179.f , std::max( 1.f , angle ) );
		cvtFov2Np( );
	}

	void nearPlane( const float distance ) noexcept
	{
		np_ = std::max( .001f , distance );
		cvtNp2Fov( );
	}

	float fieldOfView( ) const noexcept
		{ return fov_; }
	float& fieldOfView( ) noexcept
		{ return fov_; }
	float nearPlane( ) const noexcept
		{ return np_; }
	float& nearPlane( ) noexcept
		{ return np_; }

    private:
	float fov_ = 90.f;
	float np_;


	// ---------------------------------------------------------------------
	// POSITION AND ORIENTATION

    public:
	void camera( glm::vec3 const& lookAt ,
			glm::vec3 const& angles ,
			float distance ) noexcept;
	void camera( glm::vec3 const& lookAt ,
			glm::vec3 const& position ,
			glm::vec3 const& up ) noexcept;

	glm::vec3 const& lookAt( ) const noexcept
		{ return lookAt_; }
	glm::vec3& lookAt( ) noexcept
		{ return lookAt_; }

	glm::vec3 const& angles( ) const noexcept
		{ return angles_; }
	glm::vec3& angles( ) noexcept
		{ return angles_; }
	float distance( ) const noexcept
		{ return distance_; }
	float& distance( ) noexcept
		{ return distance_; }

	glm::vec3 const& position( ) const noexcept
		{ return pos_; }
	glm::vec3& position( ) noexcept
		{ return pos_; }
	glm::vec3 const& upVector( ) const noexcept
		{ return up_; }
	glm::vec3& upVector( ) noexcept
		{ return up_; }

	glm::vec3 const& direction( ) const noexcept
		{ return dir_; }
	glm::vec3& direction( ) noexcept
		{ return dir_; }

    private:
	glm::vec3 lookAt_ = glm::vec3( 0 );
	glm::vec3 angles_ = glm::vec3( 0 );
	float distance_ = 10;

	glm::vec3 dir_;
	glm::vec3 up_;
	glm::vec3 pos_;

	glm::mat3x3 rotMat_;

	// ---------------------------------------------------------------------
	// CONVERSION

    public:
	void cvtFov2Np( ) noexcept
		{ np_ = 2 * tanf( M_PI * ( 180.f - fov_ ) / 360.f ); }
	void cvtNp2Fov( ) noexcept
		{ fov_ = 180.f - atanf( .5 * np_ ) * 360.f / M_PI; }

	void cvtAnglesToVectors( ) noexcept;
	void cvtVectorsToAngles( ) noexcept;
};