#ifndef PROJ_HELPER_OPENGL_H
#define PROJ_HELPER_OPENGL_H

#include <freeglut.h>
#include <freeglut_ext.h>
#include <glext.h>
#include <string>
#include <iostream>
#include <highgui.h>
#include <Plane.h>
#include "Helper\Cvh.h"


/** Index of column, where is saved position vector of plane defined in matrix */
#define POSITION_VEC_COLUMN_INDEX 3
/** Index of column, where is saved normal vector of plane defined in matrix */
#define NORMAL_VEC_COLUMN_INDEX 2
/** CV matrix first diagonal member index */
#define CV_DIAGONAL_1_INDEX 1
/** CV matrix second diagonal member index */
#define CV_DIAGONAL_2_INDEX 2
/** OpenCV to OpenGL transformation matrix value*/
#define INVERT_VALUE -1
/** GL matrix first diagonal member index */
#define GL_DIAGONAL_1_INDEX 5
/** GL matrix second diagonal member index */
#define GL_DIAGONAL_2_INDEX 10
/** Multiplication constant for projection matrix creation.*/
#define MULT_CONST 2
/** Rectangle corner points count. */
#define CORNERS_COUNT 4
/** Float intensity 1 from 256 **/
#define DEF_PROJECTOR_WEIGHT 0.00390625f

namespace PROJ
{
	/** Size of OpenGL transformation matrix. */
	const int matrixMembers = 16; 
	/** Rows of OpenGL transformation matrix. */	
	const int matrixRows = 4;
	/** Columns of OpenGL transformation matrix. */	
	const int matrixCols = 4;
	/** Z offset for drawing camera frame */
	const float zOffset = -100.0f;  
	const int defaultMarkSize = 10;
	const int defaultPointSize = 1;
	const int RGBAchannels = 4;
	


	class GL
	{
		public: 
			static void DrawChackerboard(int x, int y, int size,int rows,int cols);
			static void DrawChackerboard(int rows,int cols,std::vector<geometry::Vector2Df> corners);
			static void DrawRectangle(int x1,int y1,int x2,int y2,float intensity);
			static void DrawRectangle(int x1,int y1,int x2,int y2,float r,float g,float b);
			/**
			 * Loads texture from input OpenCV image.
			 * @param image Texture source image.
			 * @param texId Texture id.
			 * @return True, when texture is loaded.
			 */
			static void DrawQuad(float x1,float y1,float x2,float y2);

			/**
			 * Draws a textured quad. 
			 * @param points Four points in order Upper left,Upper right,Bottom right and Bottom left corner
			 */
			static void DrawQuad(std::vector<geometry::Vector2Df>* points);
			static void DrawMesh(int rows, int cols,std::vector<geometry::Vector2Df>* meshPoints,std::vector<geometry::Vector2Df>* texturePoints);

			static bool LoadTextureFromCvImage(IplImage *image,GLuint * textId,GLint internalFormat = GL_RGB,GLenum format = GL_BGR_EXT);
			static void DrawTexture(GLuint *texId,float x1, float y1, float x2,float y2);
			static void DrawTexture(GLuint *texId,std::vector<geometry::Vector2Df>* points);
			static void DrawTextureOnMesh(GLuint *texId,int rows,int cols,std::vector<geometry::Vector2Df>* meshPoints,std::vector<geometry::Vector2Df>* texturePoints);
			/**
			 * Prints matrix to standard output.	
			 * @param mat Matrix to print out.
			 * @param name Matrix name to assign in output.
			 */
			static void PrintGLMatrix(GLenum pname,std::string name);
			static void PrintGLMatrix(GLfloat * mat,std::string name);
			
			/**
			 * Creates OpenGL projection matrix from camera internal params.
			 * @param internal Internal camera/projector parameters.
			 * @param winHeight View window width.
			 * @param winheight View window height.
			 * @param nearPlane Distance of near frustum plane from camera.
			 * @param farPlane Distance of far frustum plane from camera.
			 * @return OpenGL projection matrix.
			 */
			//static GLfloat * CreateProjectionMatrix(CalibrationData::Internal internal,GLfloat winWidth,GLfloat winHeight,GLfloat nearPlane,GLfloat farPlane);
			
						
			/**
			 * Creates OpenGL modelview matrix from camera external params.
			 * @param externalMatrix External camera/projector parameters matrix. 			 
			 */
			static GLfloat * CreateModelviewMatrix(CvMat * externalMatrix);
			
			/**
			* Converts OpenCV matrix into OpenGL matrix.
			* @param mat Src OpenCV matrix. Size 4x4.
			* @return OpenGL matrix or 0 if src matrix han't expected dimensions.
			*/
			static GLfloat * MatrixCvToGL(CvMat * mat);
			
			/**
			* Converts OpenGL matrix into OpenCV matrix.
			* @param mat Src OpenGL matrix. Size 4x4.
			* @return OpenCV matrix or 0 if src matrix han't expected dimensions or is null.
			*/
			static CvMat * MatrixGLToCv(GLfloat * glMat);
			
			/**
			* Converts OpenCV matrix into OpenGL matrix.
			* @param mat Src OpenCV matrix. Size 4x4.
			* @param m Dst OpenGL matrix. Size 4x4.
			* @return False if src matrix han't expected dimensions.
			*/
			static bool MatrixCvToGL(CvMat * mat,GLfloat * m);
			
			/**
			* Load column from matrix into vector.
			* @param mat Src matrix 4x4.
			* @param colNum Index of loaded column.
			* @param vec Dst vector, where values are loaded.
			*/
			static void GetColumn(Matrix4x4 mat,int colNum,geometry::Vector3Dd * vec);
			
			/**
			* Creates plane from 4x4 transformation matrix.
			* @param m Src matrix.
			* @return Pointer to created plane.
			*/
			static geometry::Planed * CreatePlane(Matrix4x4 m);
			
			/**
			* Loads trasnformation matrix for transformation between OpenGL and OpenCV axis system.
			* @param m Loaded/initialized matrix.
			*/
			static void InitTransformMatrixGLtoCV(Matrix4x4& m);
		
			static void LoadTransformationMatrix(GLenum mode,Matrix4x4& m);
			static void DrawMarks(std::vector<geometry::Vector2Df>* points);
			static void DrawMark(double x, double y, double size);
			static void DrawPoints(std::vector<geometry::Vector2Df>* points);
			static void DrawPoint(double x, double y, double size);
			static void DrawLine(double x1, double y1,double x2,double y2);
			static void DrawVector(std::vector<geometry::Vector2Df> points,geometry::Vector2Df imgCamScale,GLenum mode,bool scaleByCameraImg = true);
			static void DrawIntensityMap(std::vector<geometry::Vector2Df>* cornerPoints,float maxIntensity = CHAR_MAX,float projectorWeight = DEF_PROJECTOR_WEIGHT);
			static void DrawStrips(int x,int y,int width,int height,bool vertical,int stripsInStep,int stripsCount);
			static void DrawIntensityMap(int x,int y,int w,int h,float maxIntensity = 1.0f,float projectorWeight = DEF_PROJECTOR_WEIGHT);
			/**
			 * Setup modelview transformation matrix
			 * @param transformMat Transformation matrix 4x4
			 */
			static void SetupTransformMatrix(GLfloat * mat);
			static void RectToPoins(float x1,float y1,float w,float h,std::vector<geometry::Vector2Df>* points);
			static void DrawGrid(float x1,float x2,float y1,float y2,int step,GLfloat r,GLfloat g,GLfloat b);
			static IplImage * TakeScreenshot(float x,float y,float w,float h,bool asGrayscale = false);
		private: 						
			/**
			* Constructor.
			*/
			GL();
			/**
			* Destructor.
			*/
			~GL(); 			
	};
}

#endif // PROJ_OPENGL_HELPER_H
