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

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkOpenGLPointPickerEntity.h
	Version:		$Revision: 2774 $
	Date:			$Date: 2012-03-13 10:52:35 +0100 (Di, 13 Mrz 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 OPENGLPOINTPICKERENTITY_H__
#define OPENGLPOINTPICKERENTITY_H__

#include "ritkVisualization.h"


#include <itkObject.h>
#include <itkObjectFactory.h>
#include <itkFastMutexLock.h>
#include <QVector3D>
#include <QMouseEvent>
#include <QLinkedList>

#include "itkFastMutexLock.h"
#include "ritkOpenGLEntity.h"
#include "ritkOpenGLPointPickerLinearControlUnit.h"


#include <GL/glu.h>

namespace ritk
{

/**	@ingroup	Visualization
 *	@class		OpenGLPointPickerEntity
 *	@author		Martin Stumpf
 *	@brief		OpenGL based point picker Entity for ritkOpenGLVisualizationWidget
 *
 *	@details
 */



class VISUALIZATION_EXPORT OpenGLPointPickerEntity : public OpenGLEntity, public itk::Object
{
	Q_OBJECT

public:
	struct PickedPoint{
		QVector3D coord3D;
		QVector2D coord2D;
	};
	struct PickedLine{
		PickedLine(){ p1 = NULL; p2 = NULL;}
		PickedLine(PickedPoint *np1, PickedPoint *np2){ p1 = np1; p2 = np2;}
		PickedPoint *p1;
		PickedPoint *p2;
	};

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

	/// Object creation
	//itkNewMacro(Self); // removed due to full virtual class

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

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

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

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

	/// Ensure that PaylodaDimensions is at least 1, to avoid possible errors
	virtual QVector2D GetPayloadDimensions(){ return QVector2D(1.0f, 1.0f); };

	virtual void set2DPickInvalidPoints(bool value);

public slots:

	/// sets the sphere size of the picked points
	void SetSphereSize(float size);
	/// Delete all Entries
	void Reset();

signals:


	// Signal being called when a new point got picked
	void PointPickerChangedSignal();

protected:
	/** @name Functions for subclasses */
	//@{
	virtual bool Event_NewPoint(PickedPoint p) = 0;
	virtual bool Event_LineClicked(int index) = 0;
	virtual bool Event_PointClicked(int index) = 0;
	virtual bool Event_PointDragged(int index, PickedPoint newPos) = 0;
	virtual bool Event_DeletePoint(int index) = 0;
	virtual bool Event_DeleteLine(int index) = 0;
	virtual bool Event_PointReleased(int index) = 0;
	virtual bool Event_ResetAll() = 0;
	//@}

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

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

	bool RightMousePressed(QMouseEvent *e);
	bool RightMouseReleased(QMouseEvent *e);
	bool MouseMoved(QMouseEvent *e);

	/** @name	PointPicker */
	//@{
	/// deletes all points
	virtual void DeletePoints();
	/// returns 3D point coordinate, where 0 is the point picked last (linear mode)
	virtual QVector3D GetPoint(int p);
	/// returns 2D point coordinate, where 0 is the point picked last (linear mode)
	virtual QVector2D GetPoint2D(int p);
	/// calculates distance of two points, where 0 is the point picked last
	virtual float GetDistance(int p0, int p1);
	/// returns the number of points that are active
	virtual int GetNumPoints();
	//@}

	/// calculates the points/lines that would be clickable by the mouse
	virtual void calculateMouseOver();
	/// stores the active mouse position
	virtual void setMousePos(QPoint pos);
	


protected:

	/** @name Picks a point at coordinate mousePos */
	//@{
	bool PickPoint(QPoint mousePos, PickedPoint *res);
	bool unProject(QPoint mousePos, QVector3D *res);
	bool Project(QVector3D vec, QVector3D *res);
	bool Project2D(QVector2D vec, QVector2D *res);
	//@}

	/// The control unit
	QDockWidget* m_ControlUnit;

	/// General purpose mutex to synchronize
	itk::FastMutexLock::Pointer m_Mutex;
	
	/** @name	list of picked points */
	//@{
	QList<PickedPoint> m_PointList;
	QList<PickedLine> m_LineList;
	//@}

	/// size of pointpicker spheres
	float m_SphereSize;

	/// if set, 2D PointPicker will also pick invalid points
	bool m_2DPickInvalidPoints;
	/// if false, 2D PointPicker will store coords as pixel coords. If true, they will be stored as values from 0.0f to 1.0f.
	bool m_2DRelativeCoords;


	/// used to draw spheres
	GLUquadric *m_QuadricPointer;
	
	/**	@name Internal performance measurement */
	//@{
	int m_Counters[10];
	float m_Timers[10];
	//@}	

	/** @name Mouse Parameters */
	//@{
	QPoint m_MousePos;
	int m_mouseOverPoint;
	int m_mouseOverLine;
	int m_mousePointGrabbed;
	//@}

	/// Stores the active Image Dimensions
	QVector2D m_ImgDimensions;

};

} // namespace ritk

#endif // OPENGLPOINTPICKERENTITY_H__
