/*======================================================================================

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkOpenGLCamera.h
	Version:		$Revision: 2478 $
	Date:			$Date: 2012-01-25 11:58:00 +0100 (Mi, 25 Jan 2012) $

	Copyright (c) 2010-2011
	Pattern Recognition Lab, 
    University Erlangen-Nuremberg, Germany
    All rights reserved.

	This program 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 RITKCopyright.txt for more information.

	http://www5.informatik.uni-erlangen.de/research/software/ritk

========================================================================================*/

#ifndef OPENGLCAMERA_H__
#define OPENGLCAMERA_H__


#include "ritkVisualization.h"

#include "ritkOpenGLCameraControlUnit.h"
#include "ritkOpenGLEntity.h"

#include <itkObject.h>
#include <itkObjectFactory.h>
#include <itkSimpleFastMutexLock.h>

#include <itkMatrix.h>
#include <itkVector.h>
#include <itkVersor.h>

#include <GL/glu.h>

namespace ritk
{

#define CHECK_GL_ERROR()													\
{																			\
	GLenum errCode = glGetError();											\
	if ( errCode != GL_NO_ERROR )											\
	{																		\
		std::cout << "An OpenGL error occured: " << errCode << std::endl;	\
		std::cout << gluErrorString(errCode) << std::endl;					\
		std::cout << __FILE__ << " Line " << __LINE__  << std::endl;		\
	}																		\
}

/**	@ingroup	Visualization
 *	@class		OpenGLCamera
 *	@author		Jakob Wasza
 *	@brief		Simple camera used in the ritk OpenGL visualization system
 *
 *	@details
 */
class VISUALIZATION_EXPORT OpenGLCamera : public OpenGLEntity, public itk::Object
{
	Q_OBJECT

public:
	/**	@name	Standard ITK typedefs */
	//@{
	typedef OpenGLCamera									Self;
	typedef itk::Object										Superclass;
	typedef itk::SmartPointer<Self>							Pointer;
	typedef itk::SmartPointer<const Self>					ConstPointer;
	//@}

	/**	@name	Transformation typedefs */
	//@{
	typedef float							RealType;
	typedef itk::Vector<RealType, 2>		Vec2;
	typedef itk::Vector<RealType, 3>		Vec3;
	typedef itk::Vector<RealType, 4>		Vec4;
	typedef itk::Matrix<RealType, 3, 3>		Mat3x3;
	typedef itk::Matrix<RealType, 4, 4>		Mat4x4;
	typedef itk::Versor<RealType>			VersorType;
	//@}

	/// Object creation
	itkNewMacro(Self);

	/// RTTI
	itkTypeMacro(OpenGLCamera, itk::Object);


	/// The controlunit of this entity
	virtual QDockWidget* GetControlUnit() const;

	/**	@brief	Camera coordinate system 
	 *	
	 *	@details
	 *	The camera coordinate system is spanned by 3 axis:			\n
	 *		-	a0 = [ViewCenter-EyePos] / ||[ViewCenter-EyePos]	\n
	 *		-	a1 = ViewUp / ||ViewUp||							\n
	 *		-	a2 = a0xa1											\n
	 */
	//@{
	virtual void SetEyePos(const Vec3 &v);
	virtual Vec3 GetEyePos() const;
	virtual void SetViewCenter(const Vec3 &v);
	virtual Vec3 GetViewCenter() const;
	virtual void SetViewUp(const Vec3 &v);
	virtual Vec3 GetViewUp() const;
	virtual void SetViewPort(const Vec2 &v);
	virtual Vec2 GetViewPort() const;
	virtual Vec2 GetClippingPlanes() const;
	virtual float GetFieldOfView() const;
	//@}

	/// Returns whether this camera is initialized
	virtual bool IsInitialized() const;
	

	/**	@brief	Reset the camera
	 *	@param	BoundingSphere	the bounding sphere of the scene [cx,cy,cz,r]
	 *
	 *	@details
	 *	Will reset the camera to fit the bounding sphere
	 */
	virtual void ResetCamera3D(const Vec4 &BoundingSphere);
	virtual void ResetCamera2D();

	/**	@brief	Rotate the camera
	 *	@param	d	delta movement in viewport-units (x,y)
	 *
	 *	@details
	 *	Rotate the camera based on relative movement in the viewport.
	 */
	virtual void Rotate(const Vec2 &d);

	/**	@brief	Translate the camera
	 *	@param	d	delta movement in viewport-units (x,y)
	 *
	 *	@details
	 *	Translate the camera based on relative moevment in the viewport.
	 */
	virtual void Translate(const Vec2 &d);

	/**	@brief	Zoom the camera
	 *	@param	zoom	the zoom
	 *	
	 *	@details
	 *	Zoom the camera
	 */
	virtual void Zoom(RealType Zoom, QPoint mousePos);

	/// Bounding box
	virtual BoundingBoxType GetBoundingBox() const;

	/// Render
	//@{
	virtual void Render3D(const OpenGLVisualizationWidget *Widget);
	virtual void Render2D(const OpenGLVisualizationWidget *Widget, QVector2D ImgSize);
	//}@

	/// Calculate 2D World coordinates from 2D Screen coordinates (2D Mode only);
	Vec2 Unapply2D(const Vec2 &v);

protected:
	/// Constructor
	OpenGLCamera();
	/// Destructor
	virtual ~OpenGLCamera();

	/// Print the camera to the given stream
	virtual void PrintSelf(std::ostream& os, itk::Indent indent) const;

	/// Needed for synchronization
	virtual Vec2 Unapply2D_internal(const Vec2 &v);

	/// Sets the ViewCenter back inside of the boundaries (2D Function)
	virtual void CheckTransformBoundaries2D();

protected:
	/// General purpose mutex to synchronize
	itk::SimpleFastMutexLock m_Mutex;

	/// Flag to indicate proper initialization
	bool m_InitFlag;

	/// The current bounding sphere
	Vec4 m_BoundingSphere;

	/**	@name Current eye position and view center and view up. This defines our camera coordinate system */
	//@{
	Vec3 m_EyePos;
	Vec3 m_ViewCenter;
	Vec3 m_ViewUp;
	//@}

	/// Viewport
	Vec2 m_ViewPort;

	/// The field of view
	float m_FOV;

	/// Current near and farplane
	Vec2 m_ClippingPlanes;

	/**	@name 3D Camera transformation */
	//@{
	RealType	m_Zoom;
	Vec3		m_TranslationVector;
	Mat3x3		m_RotationMatrix;
	Mat4x4		m_OpenGLRotationMatrix;
	//@}

	/**	@name 2D Camera transformation */
	//@{
	Vec2		m_ViewCenter2D;
	RealType	m_Zoom2D;
	RealType	m_RatioTransformationFactor;
	Vec2		m_ImgDimensions;
	//@}
	
	/// The control unit
	OpenGLCameraControlUnit* m_ControlUnit;
};

} // namespace ritk



#endif // OPENGLCAMERA_H__
