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

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkOpenGLRImageEntity.h
	Version:		$Revision: 1817 $
	Date:			$Date: 2011-09-30 16:52:28 +0200 (Fri, 30 Sep 2011) $

	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 OPENGLRIMAGEENTITY_H__
#define OPENGLRIMAGEENTITY_H__

#include "ritkVisualization.h"


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

#include "ritkRImage.h"
#include "ritkGenericImportImageContainer.h"

#include "ritkOpenGLEntity.h"
#include "ritkOpenGLRImageControlUnit.h"
#include "ritkImagePayloadVisualizationConfig.h"


#include <GL/glu.h>

namespace ritk
{

/**	@ingroup	Visualization
 *	@class		OpenGLRImageEntity
 *	@author		Jakob Wasza
 *	@brief		OpenGL based visualization entity for range images
 *
 *	@details
 */
class VISUALIZATION_EXPORT OpenGLRImageEntity : public OpenGLEntity, public itk::Object
{
	Q_OBJECT

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

	/// Object creation
	itkNewMacro(Self);

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

	/**	@brief	Set the range data to render 
	 *	@param	Data	The data to render
	 *
	 *	@details
	 *	A call to this method will set the range data to render.
	 */
	void SetRangeData(RImageF2::ConstPointer Data);
	void SetRangeData(RImageF2::Pointer Data);

	/// Render
	virtual void Render();

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

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

protected slots:
	/**	@brief	Update the primary payload to visualize
	 *	@param	Config	the payload configuration
	 *
	 *	@details
	 *	Calling this method will update the internal primary payload config
	 */
	void UpdatePrimaryPayloadVisualizationConfiguration(ImagePayloadVisualizationConfig Config);
	/// Update the secondary payload to visualize. @sa UpdatePrimaryPayloadVisualizationConfiguration
	void UpdateSecondaryPayloadVisualizationConfiguration(ImagePayloadVisualizationConfig Config);

	/// Set the alpha value for blending
	void SetLUTAlpha(int value);

	/// Set the render type of the data
	void SetRenderTypePoints();

	/// Set the render type of the data
	void SetRenderTypeTriangles();

	/// Primary payload clamping
	void ClampPrimaryPayloadToCurrentInterval();

	/// Secondary payload clamping
	void ClampSecondaryPayloadToCurrentInterval();

	/// Only valid flags
	void SetShowValidFlagsOny(bool b);

signals:
	/**	@name Signals connected to the control unit. !*/
	//@{
	void AvailableRangeImagePayloadsChanged(std::vector<ImagePayloadVisualizationConfig> Data);
	//@}

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

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

	/**	@brief	Determine the payloads that can be visualized
	 *	@param	RI	the range image
	 *
	 *	@details
	 *	This method will determine the payloads that can be visualized by this entity.
	 *	The payloads will be wrapped and eventually emitted via AvailableRangeImagePayloadsChanged()
	 */
	void DetermineAvailableRangeImagePayloads(RImageF2::ConstPointer RI);

	/// Initialize all OpenGL related members
	void InitGL();
	/// Release all OpenGL related members
	void CloseGL();

	/**	@brief	Update the primary payload texture
	 *	@param	Config	The visualization configuration that is to be used
	 *	@param	TexID	OpenGL texture identifier for the payload texture
	 *
	 *	@details
	 *	This method will update the payload texture as specified in the given payload visualization configuration. \n
	 *	@sa UpdatePrimaryPayloadVisualizationConfiguration()
	 *	@sa UpdateSecondaryPayloadVisualizationConfiguration
	 */
	void UpdatePayloadTexture(ImagePayloadVisualizationConfig Config, GLuint PayloadTexID);
	
	/**	@brief	Update the internal VBOs 
	 *
	 *	@details
	 *	Calling this method will update the internal VBOs to match the current frame. 
	 *	Note that this method is supposed to be called within the widgets main thread
	 *	in order to comply with the OpenGL render context!
	 *	@sa SetRangeData
	 */
	void UpdateVBO();

	/// Load a shader
	GLuint LoadShader(const char *FileName, bool VertexFlag);

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

	/// Mutex used to synchronize access to the current frame member variable and payload configurations
	QMutex m_CurrentFrameMutex;

	/// Flag that indicates whether this entity (in particular the OpenGL extensions are initialized)
	bool m_InitFlag;

	/// The control unit
	OpenGLRImageControlUnit* m_ControlUnit;

	/// Flag to signal the VBO that the size of the input data changed
	bool m_SizeDirtyFlag;

	/// Flag that indicates whether only valid fragments should be rendered
	bool m_DiscardInvalidFragments;

	/// true if points instead of triangles
	bool m_RenderPoints;


	/// The current frame
	RImageF2::ConstPointer m_CurrentFrame;

	/**	@name Texture coords, and vertex indices containers */
	//@{
	RImageF2::SizeType m_CurrentSize;
	GenericImportImageContainer<unsigned long, itk::Vector<float,2> >::Pointer	m_TextureCoordsContainer;
	GenericImportImageContainer<unsigned long, unsigned int>::Pointer			m_TriangleStripsIndicesContainer;
	GenericImportImageContainer<unsigned long, unsigned int>::Pointer			m_PointsIndicesContainer;
	//@}

	/**	@name Mandatory textures for world coords and flags */
	//@{
	/// Texture for world coords
	GLuint m_WorldCoordTexture;
	/// The flag data texture
	GLuint m_FlagTexture;
	//@}

	/**	@name Primary payload visualization configuration and textures */
	//@{
	ImagePayloadVisualizationConfig		m_PrimaryPayloadVisualizationConfig;
	GLuint								m_PrimaryPayloadLUTTexture;
	GLuint								m_PrimaryPayloadTexture;
	//@}

	/**	@name Secondary payload texture */
	//@{
	ImagePayloadVisualizationConfig		m_SecondaryPayloadVisualizationConfig;
	GLuint								m_SecondaryPayloadLUTTexture;
	GLuint								m_SecondaryPayloadTexture;
	//@}

	/**	@name VBOs */
	//@{
	/// Flag to control whether the VBOs are ready for use
	bool m_VBOInitialized;
	/// The VBO for the vertex coords
	GLuint m_VBOVertices;
	/// VBO for triangle strip indices
	GLuint m_VBOTriangleStripsIndices;
	/// The VBO for the texture coords
	GLuint m_VBOTexCoords;
	//@}

	/**	@name Shader */
	//@{
	/// The shader program
	GLuint m_ShaderProgram;
	/// Handles to the shader objects
	std::vector<GLuint> m_ShaderHandles;
	//@}

	/// The current alpha value for blending
	float m_Alpha;

	/**	@name Internal performance measurement */
	//@{
	int m_Counters[10];
	float m_Timers[10];
	//@}	
};

} // namespace ritk

#endif // OPENGLRIMAGEENTITY_H__
