/* Universidade Federal Fluminense
 * Programa de Pós-Graduação em Computação
 *
 * Tópicos Avançados em Computação Visual e Interfaces II (Técnicas Avançadas de Rendering)
 * Professor: Leandro Augusto Frata Fernandes
 *
 * Version: 2012.05.05-001
 */

#ifndef _CAMERA_HEADER_
#define _CAMERA_HEADER_

// Include some custom definitions.
#include "basic.h"

// A simple abstract camera.
class AbstractCamera {
public:

	AbstractCamera();
	AbstractCamera(const coord3f& location, const coord3f& forwardDirection, const coord3f& upDirection);

public:

	coord3f getForwardDirection() const;
	coord3f getLocation() const;
	coord3f getRightDirection() const;
	coord3f getUpDirection() const;

	inline const matrix4x4f& getViewMatrix() const { return m_viewMatrix; }
	inline matrix4x4f getViewMatrixInverse() const { return inverse(m_viewMatrix); }

	void setPosition(const coord3f& location, const coord3f& forwardDirection, const coord3f& upDirection);

public:

	void rotateInViewSpace(const coord3f& axis, const GLfloat& angleInRadians);
	void rotateInWorldSpace(const coord3f& axis, const GLfloat& angleInRadians);

	void translateInViewSpace(const coord3f& delta);
	void translateInWorldSpace(const coord3f& delta);

private:

	matrix4x4f m_viewMatrix;
};

// A simple pinhole camera for OpenGL applications.
class PinholeCamera : public AbstractCamera {
public:

	PinholeCamera();
	PinholeCamera(const coord3f& location, const coord3f& forwardDirection, const coord3f& upDirection, const GLfloat& fovYInRadians, const GLfloat& aspectRatio, const GLfloat& zNear, const GLfloat& zFar);

public:

	inline const GLfloat& getAspectRatio() const { return m_aspectRatio; }
	inline const GLfloat& getFovY() const { return m_fovYInRadians; }
	inline const GLfloat& getZFar() const { return m_zFar; }
	inline const GLfloat& getZNear() const { return m_zNear; }

	inline const matrix4x4f& getProjectionMatrix() const { return m_projectionMatrix; }
	
	void setAspectRatio(const GLfloat& aspectRatio);
	void setFovY(const GLfloat& fovYInRadians);
	void setZFar(const GLfloat& zFar);
	void setZNear(const GLfloat& zNear);

	void setProjection(const GLfloat& fovYInRadians, const GLfloat& aspectRatio, const GLfloat& zNear, const GLfloat& zFar);

private:

	void updateProjectionMatrix();

private:

	GLfloat m_fovYInRadians; // Field of view angle in radians, in the y direction.
	GLfloat m_aspectRatio;   // Aspect ratio (aspect = width / height).

	GLfloat m_zNear;         // Near plane distance.
	GLfloat m_zFar;          // Far plane distance.

	matrix4x4f m_projectionMatrix; // Projection matrix.
};

#endif // _CAMERA_HEADER_
