/*---------------------------------------------------------------
	FILE: COpenGLScene.h
	USE: See below

   Part of the MRPT Library
   ISA - Universidad de Malaga - http://www.isa.uma.es
  ---------------------------------------------------------------*/
#ifndef COpenGLScene_H
#define COpenGLScene_H

#include <MRPT/UTILS/utils_defs.h>
#include <MRPT/UTILS/CSerializable.h>
#include <MRPT/UTILS/CMRPTImage.h>
#include <MRPT/UTILS/CMRPTImageFloat.h>

namespace MRML
{
	class CPointsMap;
}


namespace UTILS
{
	/** The namespace for 3D scene representation and rendering.
	  */
	namespace OPENGL
	{
		/** The base class of 3D objects that can be directly rendered through OpenGL.
		  *  In this class there are a set of common properties to all 3D objects, mainly:
		  *		- A name (m_name): A name that can be optionally asigned to objects for easing its reference.
		  *		- 6D coordinates (x,y,z,yaw,pitch,roll), relative to the "current" reference framework. By default, any object is referenced to global scene coordinates.
		  *		- A RGB color: This field will be used in simple elements (points, lines, text,...) but is ignored in more complex objects that carry their own color information (triangle sets,...)
		  *  See the main class UTILS::COpenGLScene
		  *  \sa UTILS::COpenGLScene, UTILS::OPENGL
		  */
		class CRenderizable : public CSerializable
		{
			DEFINE_VIRTUAL_SERIALIZABLE( CRenderizable )

 		public:
			std::string				m_name;
			float					m_color_R,m_color_G,m_color_B,m_color_A;
			float					m_x,m_y,m_z;
			float					m_yaw,m_pitch,m_roll;
			bool					m_show_name;

			static void	renderTextBitmap( const char *str );

			/** Default constructor:
			  */
			CRenderizable()
			{
				m_color_R=m_color_G=m_color_B=m_color_A=1;
				m_x=m_y=m_z=0;
				m_yaw=m_pitch=m_roll=0;
				m_show_name = false;
				//m_initCommonFontList = false;
			}

			/** Default virtual destructor
			  */
			virtual ~CRenderizable();

			/** This virtual method in the base class performs common tasks like coordinates transformation,color,...
			  */
			virtual void  render() = 0;

			//This function only are used when show name
			//void  prepareFontLists();

			void  writeToStreamRender(CStream &out) const;
			void  readFromStreamRender(CStream &in);
		};

		/** A solid sphere
		  *  \sa UTILS::COpenGLScene
		  */
		class CSphere : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CSphere )
		public:

			float			m_radius;
			int				m_nDivsLongitude,m_nDivsLatitude;

			/** Constructor
			  */
			CSphere(
				float				radius = 1.0f,
				int					nDivsLongitude = 20,
				int					nDivsLatitude = 20
				)
			{
				m_radius			= radius;
				m_nDivsLongitude	= nDivsLongitude;
				m_nDivsLatitude		= nDivsLatitude;
			}

			/** Render
			  */
			void  render();
		};

		/** A grid of lines over the XY plane.
		  *  \sa UTILS::COpenGLScene
		  */
		class CGridPlaneXY : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CGridPlaneXY )
		public:
			float	m_xMin, m_xMax;
			float	m_yMin, m_yMax;
			float	m_z;
			float	m_frequency;

			/** Default constructor
			  */
			CGridPlaneXY( )
			{
			}

			/** Constructor
			  */
			CGridPlaneXY(
				float				xMin,
				float				xMax,
				float				yMin,
				float				yMax,
				float				z,
				float				frequency
				)
			{
				m_xMin = xMin; m_xMax = xMax;
				m_yMin = yMin; m_yMax = yMax;
				m_z = z;
				m_frequency = frequency;
			}

			/** Render
			  */
			void  render();
		};

		/** A 2D plane in the XY plane with a texture image.
		  *  \sa UTILS::COpenGLScene
		  */
		class CTexturedPlane : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CTexturedPlane )
		protected:
			unsigned int		m_glTextureName;
			bool				m_init;
			CMRPTImage			m_textureImage;
			CMRPTImage			m_textureImageAlpha;
			bool				m_enableTransparency;

			int					r_width,r_height;

		public:
			float				m_tex_x_min,m_tex_x_max;
			float				m_tex_y_min,m_tex_y_max;

			float			m_xMin, m_xMax;
			float			m_yMin, m_yMax;
			/** Destructor
			  */
			virtual ~CTexturedPlane();

			/** Constructor
			  */
			CTexturedPlane(
				float				x_min = -1,
				float				x_max = 1,
				float				y_min = -1,
				float				y_max = 1
				);

			/** Assigns a texture and a transparency image, and enables transparency (If the images are not 2^N x 2^M, they will be internally filled to its dimensions to be powers of two)
			  */
			void  assignImage(
				const CMRPTImage&	img,
				const CMRPTImage&	imgAlpha );

			/** Assigns a texture image, and disable transparency.
			  */
			void  assignImage(
				const CMRPTImage&	img );

			/** Render
			  */
			void  render();

			/** VERY IMPORTANT: If you use a multi-thread application, you MUST call this from the same thread that will later destruct the object in order to the OpenGL texture memory to be correctly deleted.
			  *  Calling this method more than once has no effects. If you user one thread, this method will be automatically called when rendering, so there is no need to explicitly call it.
			  */
			void 		loadTextureInOpenGL();
		};

		/** A 2D text to be renderized
		  *  \sa UTILS::COpenGLScene
		  */
		class CText : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CText )
		protected:

		public:
			std::string		m_str;
            std::string		m_fontName;
            int				m_fontHeight, m_fontWidth;

			/** Constructor
			  */
			CText( const std::string &str = std::string("") );

			/** Destructor
			  */
			virtual ~CText();

			/** Render
			  */
			void  render();
		};

		/** A simple line
		  *  \sa UTILS::COpenGLScene
		  */
		class CSimpleLine : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CSimpleLine )
		public:
			float	m_x0,m_y0,m_z0;
			float	m_x1,m_y1,m_z1;
            float	m_lineWidth;

			/** Constructor
			  */
			CSimpleLine(
				float x0=0,float y0=0, float z0=0,
				float x1=0,float y1=0, float z1=0, float lineWidth = 1.0 )
			{
				m_x0=x0; m_y0=y0; m_z0=z0;
				m_x1=x1; m_y1=y1; m_z1=z1;
                m_lineWidth = lineWidth;
			}

			/** Render
			  */
			void  render();
		};

		/** A set of independent lines
		  *  \sa UTILS::COpenGLScene
		  */
		class CSetOfLines : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CSetOfLines )
		public:
			vector_float	m_x0,m_y0,m_z0;
			vector_float	m_x1,m_y1,m_z1;

			/** Constructor
			  */
			CSetOfLines( )
			{
			}

			/** Render
			  */
			void  render();
		};


		/** A cloud of points
		  *  \sa UTILS::COpenGLScene
		  */
		class CPointCloud : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CPointCloud )
		public:
			bool			m_colorFromZ;
			vector_float	m_xs,m_ys,m_zs;

			/** Constructor
			  */
			CPointCloud( )
			{
				m_colorFromZ = false;
			}

			/** Load the points from a points map
			  */
			void  loadFromPointsMap( MRML::CPointsMap *map);

			/** Render
			  */
			void  render();
		};

		/** A 3D arrow
		  *  \sa UTILS::COpenGLScene
		  */
		class CArrow : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CArrow )
		public:
			float	m_x0,m_y0,m_z0;
			float	m_x1,m_y1,m_z1;
			float	m_headRatio;
			float	m_smallRadius, m_largeRadius;
			//For version 2 in stream
			float	m_arrow_roll;
			float	m_arrow_pitch;
			float	m_arrow_yaw;

			/** Constructor
			  */
			CArrow(
				float	x0 = 0,
				float	y0 = 0,
				float	z0 = 0,
				float	x1 = 1,
				float	y1 = 1,
				float	z1 = 1,
				float	headRatio = 0.2f,
				float	smallRadius = 0.05f,
				float	largeRadius = 0.2f,
				float	arrow_roll = -1.0f,
				float	arrow_pitch = -1.0f,
				float	arrow_yaw = -1.0f
				)
			{
				m_x0 = x0; m_y0 = y0; m_z0 = z0;
				m_x1 = x1; m_y1 = y1; m_z1 = z1;
				m_headRatio = headRatio;
				m_smallRadius = smallRadius;
				m_largeRadius = largeRadius;
				m_arrow_roll = arrow_roll;
				m_arrow_pitch = arrow_pitch;
				m_arrow_yaw = arrow_yaw;
			}

			/** Render
			  */
			void  render();
		};

		/** A planar disk in the XY plane.
		  *  \sa UTILS::COpenGLScene
		  */
		class CDisk : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CDisk )
		public:
			float	m_radiusIn,m_radiusOut;
			int		m_nSlices, m_nLoops;

			/** Constructor
			  */
			CDisk( )
			{
				m_radiusIn = 0;
				m_radiusOut = 1;
				m_nSlices = 50;
				m_nLoops = 4;
			}

			/** Render
			  */
			void  render();
		};


		/** A definition for a camera.
		  *  In the viewer application it can be chosen the first camera to be used as the default viewpoint.
		  *  \sa UTILS::COpenGLScene
		  */
		class CCamera : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CCamera )
		public:
			float	m_pointingX,m_pointingY,m_pointingZ;
			float	m_distanceZoom;
			float	m_azimuthDeg,m_elevationDeg;

			/** Constructor
			  */
			CCamera( )
			{
				m_pointingX=m_pointingY=m_pointingZ=
				m_distanceZoom=
				m_azimuthDeg=m_elevationDeg = 0;
			}

			/** Render
			  */
			void  render()
			{
				// This does not render to anything!
			}
		};

		/** A set of colored triangles.
		  *  This class can be used to draw any solid, arbitrarily complex object (without textures).
		  *  \sa UTILS::COpenGLScene
		  */
		class CSetOfTriangles : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CSetOfTriangles )
		public:
			struct TTriangle
			{
				float	x[3],y[3],z[3];
				float	r[3],g[3],b[3],a[3];
			};

			std::vector<TTriangle>		m_triangles;
			bool						m_enableTransparency;

			/** Constructor
			  */
			CSetOfTriangles( bool enableTransparency = false )
			{
				m_enableTransparency = enableTransparency;
			}

			/** Render
			  */
			void  render();
		};

		/** A 2D ellipse or 3D ellipsoid, depending on the size of the m_cov matrix (2x2 or 3x3).
		  *  The center of the ellipsoid is the "m_x,m_y,m_z" object's coordinates. In the case of
		  *   a 2D ellipse it will be drawn in the XY plane, for z=0.
		  *  The color is determined by the RGBA fields in the class "CRenderizable". Note that a
		  *   transparent ellipsoid can be drawn for "0<alpha<1" values.
		  *  \sa UTILS::COpenGLScene
		  */
		class CEllipsoid : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CEllipsoid )
		public:
			/** The 2x2 or 3x3 covariance matrix that will determine the aspect of the ellipsoid.
			  */
			CMatrix			m_cov;
			/** If set to true (default), a whole ellipsoid surface will be drawn, or if set to "false" it will be drawn as a "wireframe".
			  */
			bool			m_drawSolid3D;
			/** The number of "sigmas" for drawing the ellipse/ellipsoid (default=3)
			  */
			float			m_quantiles;
			/** The number of segments of a 2D ellipse (default=20)
			  */
			unsigned int	m_2D_segments;
			/** The number of segments of a 3D ellipse (in both "axis") (default=20)
			  */
			unsigned int	m_3D_segments;
			/** The line width for 2D ellipses or 3D wireframe ellipsoids (default=1)
			  */
			float			m_lineWidth;

			/** Constructor
			  */
			CEllipsoid()
			{
				m_drawSolid3D	= true;
				m_quantiles		= 3;
				m_2D_segments	= 20;
				m_3D_segments	= 20;
				m_lineWidth		= 1.0f;
			}

			/** Render
			  */
			void  render();
		};

		/** A set of object, which are referenced to the coordinates framework established in this object.
		  *  It can be established a hierarchy of "CSetOfObjects", where the coordinates framework of each
		  *   one will be referenced to the parent's one.
		  *	The list of child objects is accessed directly as in the class "COpenGLScene"
		  *  \sa UTILS::COpenGLScene
		  */
		class CSetOfObjects : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CSetOfObjects )
		public:
			/** The list of child objects.
			  *  Objects are automatically deleted when calling "clear" or in the destructor.
			  */
			std::deque<UTILS::OPENGL::CRenderizable*>	m_objects;

			/** Render child objects.
			  */
			void  render();

			/** Clear the list of objects in the scene, deleting objects' memory.
			  */
			void  clear();

			/** Default constructor
			  */
			CSetOfObjects( );

			/** Destructor
			  */
			virtual ~CSetOfObjects();

			/** Initializes all textures in the scene (See OPENGL::CTexturedPlane::loadTextureInOpenGL)
			  */
			void  initializeAllTextures();

		};

		/** A grid of lines over the XZ plane.
		  *  \sa UTILS::COpenGLScene
		  */
		class CGridPlaneXZ : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CGridPlaneXZ )
		public:
			float	m_xMin, m_xMax;
			float	m_zMin, m_zMax;
			float	m_y;
			float	m_frequency;

			/** Default constructor
			  */
			CGridPlaneXZ( )
			{
			}

			/** Constructor
			  */
			CGridPlaneXZ(
				float				xMin,
				float				xMax,
				float				zMin,
				float				zMax,
				float				y,
				float				frequency
				)
			{
				m_xMin = xMin; m_xMax = xMax;
				m_zMin = zMin; m_zMax = zMax;
				m_y = y;
				m_frequency = frequency;
			}

			/** Render
			  */
			void  render();
		};

		/** Draw the 3D world axis
		  *  \sa UTILS::COpenGLScene
		  */
		class CAxis : public CRenderizable
		{
			DEFINE_SERIALIZABLE( CAxis )
		protected:

		public:
			float	m_xmin,m_ymin,m_zmin;
			float	m_xmax,m_ymax,m_zmax;
			float	m_frecuency;
            float	m_lineWidth;
			bool	m_marks;

			/** Constructor
			  */
			CAxis(
				float xmin=-1.0f,float ymin=-1.0f, float zmin=-1.0f,
				float xmax=1.0f, float ymax=1.0f,  float zmax=1.0f,
				float frecuency = 0.25f, float lineWidth = 3.0f, bool marks=false)
			{
				m_xmin=xmin; m_ymin=ymin; m_zmin=zmin;
				m_xmax=xmax; m_ymax=ymax; m_zmax=zmax;
				m_frecuency = frecuency;
                m_lineWidth = lineWidth;
				m_marks = marks;
			}

			/** Destructor
			  */
			virtual ~CAxis();

			/** Render
			  */
			void  render();
		};

	} // end namespace OPENGL

	/** This class allows the user to create, load, save, and render 3D scenes through OpenGL
	  *  This class requires the Windows OS and OpenGL's headers and libraries files in compile and link time, respectively.
	  */
	class COpenGLScene : public CSerializable
	{
		DEFINE_SERIALIZABLE( COpenGLScene )
	public:
		/** Constructor
		  */
		COpenGLScene();

		/** Destructor:
		 */
		virtual ~COpenGLScene();

		/** Copy operator:
		  */
		void  operator =( const COpenGLScene &obj );

		/** Copy constructor:
		  */
		COpenGLScene( const COpenGLScene &obj ) : CSerializable()
		{
			(*this) = obj;
		}


		/** The list of objects that comprise the 3D scene.
		  *  Objects are automatically deleted when calling "clear" or in the destructor.
		  */
		std::deque<UTILS::OPENGL::CRenderizable*>	m_objects;

		/** Render this scene.
		  */
		void  render();

		/** Clear the list of objects in the scene, deleting objects' memory.
		  */
		void  clear();

		/** Returns the first object with a given name, or NULL if not found.
		  */
		UTILS::OPENGL::CRenderizable*	getByName( const std::string &str );

		/** Returns the first object of a given class, or NULL if not found.
		  */
		UTILS::OPENGL::CRenderizable*	getByClass( UTILS::TRuntimeClassId * classId );

		/** Initializes all textures in the scene (See OPENGL::CTexturedPlane::loadTextureInOpenGL)
		  */
		void  initializeAllTextures();
	};
} // End of namespace


#endif
