/**
*	@author Jiri Zahradka
* 
* Main routine of multi projector calibration program.
*/

#include <iostream>
#include <string>
#include <algorithm>

#include "Helper\Cvh.h"
#include "Helper\GL.h"
#include "Camera\CannonCamera.h"
#include "Detection/CheckerDetector.h"
#include "Calibration/CalibrationUnit.h"

using namespace CALIB;
using namespace PROJ;
using namespace std;

#define WINDOW_CAMERA_NAME "Projector calibration - camera view"
#define WINDOW_PROJECTOR_NAME "Projector calibration - projector view"

#define DEF_WIN_WIDTH 1024
#define DEF_WIN_HEIGHT 768
#define DEF_WIN_POS_X 20
#define DEF_WIN_POS_Y 20

/** Config variables */
/** Number of projectors in horizontal line*/
#define PROJECTOR_HORIZONTAL 2//2
/** Number of projectors in vertical line */
#define PROJECTOR_VERTICAL 2
/** Projector resolution width*/
#define PROJECTOR_WIDTH 800
/** Projector resolution height */
#define PROJECTOR_HEIGHT 600

/** Projectors window shift x*/
#define FULL_WIN_POS_X 1280
/** Projectors window shift y */
#define FULL_WIN_POS_Y 0
#define FULL_WIN_OFFSET_X 0//-8
#define FULL_WIN_OFFSET_Y 0//-30

/** Width of final merged dispaly */
#define DEFAULT_MERGE_DISPLAY_WIDTH 1400
/** Height of final merged display*/
#define DEFAULT_MERGE_DISPLAY_HEIGHT 900

/** Chackerboard number of columns */
#define CHACKERBOARD_COLS 32
/** Chackerboadr number of rows*/
#define CHACKERBOARD_ROWS 24
#define CHACKERBOARD_ROWS_MIN 3
#define CHACKERBOARD_BORDER_X 1
//#define CHACKERBOARD_BORDER_Y 1 //vypocte se automaticky
#define GRID_STEP_SIZE 50

/** Config variables - end*/
int projectorHorizontalCount;
int projectorVerticalCount;
int projectorWidth;
int projectorHeight;
int projectorWindowPositionX;
int projectorWindowPositionY;
int chRows;
int chCols;
int displayWidth;
int displayHeight;
int projectorCount;
int mergeDisplayWidth;
int mergeDisplayHeight;

//CAMERA window setup
int winCamId;
/** Fullscreen mode for camera window */
bool winCamFullscreen = false;
/** Width of the preview camera window */
int winCamWidth = DEF_WIN_WIDTH;
/** Height of the preview camera window */
int winCamHeight = DEF_WIN_HEIGHT;
/** Scale of camera image. */
geometry::Vector2Df imgCamScale = geometry::Vector2Df(1,1);
/** Projector window id.*/
int winProjId;
/** Fullscreen mode for camera window */
bool winProjFullscreen = false;
/** Width of the projector output window */
int winProjWidth;
/** Height of the projector output window */
int winProjHeight;
/** OpenGL main window */
int window = 0;
/**Current proejctor index.*/
int projectorIndex = /*/FULLSCREEN;*/UPPER_LEFT;
/** Camera image texture. */
GLuint cameraTextureId = -1;
/** Chackerboard texture id. */
GLuint chessTextureId = -1;

CannonCamera * camera;
CheckerDetector * detector;
CalibrationUnit * calibrationUnit;
CvMat * projectedCorners;
vector<geometry::Vector2Df> points[ProjectorIndex::FULLSCREEN];
vector<geometry::Vector2Df> displayCorners;
bool pointsChanged = true;
bool takePicture = false;
bool projectorChaneged = true;
bool projectorRepaint = false;
bool testMode = false;
bool testHomographyMode = false;
bool keystoneCorrection = false;
bool showGrid = false;

char * testImages[] = {"testImage1.jpg","testImage2.jpg","testImage3.jpg"};
GLuint testTextureIds[] = {-1,-1,-1};
int testImageMaxIndex = 2;
int testImageIndex = 0;
bool prepareLigthMaps = false;
bool showLightMaps = false;

char * chackerboardImageName = "chess.png";
bool cameraOffline = false;
int baseProjectorIndex = 0;

enum PatternIndex
{
	CHACKERBOARD = 0,
	BLACK = 1,
	WHITE,
	TEXTURE = 4
};
/** Current projected pattern.*/
int patternIndex = CHACKERBOARD;

std::pair<geometry::Vector2Df*,geometry::Vector2Df*> getInnerPolygon(vector<geometry::Vector2Df> points,int vertexHorizontal,int vertexVertical)
{
	vector<float> valuesX;
	vector<float> valuesY;
	int i;
	for(i = 0;i < points.size();i++)
	{
		valuesX.push_back(points.at(i).GetX());
		valuesY.push_back(points.at(i).GetY());
	}

	sort(valuesX.begin(),valuesX.end());
	sort(valuesY.begin(),valuesY.end());

	int offsetXMin = vertexHorizontal-1;
	int offsetXMax = points.size() - vertexHorizontal;
	int offsetYMin = vertexHorizontal-1;
	int offsetYMax = points.size() - vertexHorizontal;
	geometry::Vector2Df * position = new geometry::Vector2Df(valuesX.at(offsetXMin),valuesY.at(offsetYMin));
	geometry::Vector2Df * size = new geometry::Vector2Df(valuesX.at(offsetXMax)-position->GetX(),valuesY.at(offsetYMax)-position->GetY());
	pair<geometry::Vector2Df*,geometry::Vector2Df*> rect;
	rect.first = position;
	rect.second = size;
	return rect;
}

bool goOnece = true;

void CalculateDisplayPoints()
{
	vector<geometry::Vector2Df> pointsDisplayP1;
	std::vector<geometry::Vector2Df> * p2Points = Cvh::ConvertMatToVector2D(calibrationUnit->GetPoints(1,DISPLAY));

	float div = 6.0f/16.0f;

	geometry::Vector2Df a = geometry::Vector2Df(0,0);
	geometry::Vector2Df b = p2Points->at(1);
	geometry::Vector2Df c = b.GetNormalVector()*div;
	c.Mirror();
	geometry::Vector2Df d = b+c;

	pointsDisplayP1.push_back(a);
	pointsDisplayP1.push_back(b);
	pointsDisplayP1.push_back(c);
	pointsDisplayP1.push_back(d);
}

/**
 * Switch on/off fullscreen mode for camera or projector window.
 */
void winSwitchProjFullscreen()
{
	if(winProjFullscreen)
	{
		glutReshapeWindow(DEF_WIN_WIDTH, DEF_WIN_HEIGHT);
		glutPositionWindow(DEF_WIN_POS_X,DEF_WIN_POS_Y);
	}
	else
	{
		glutPositionWindow(projectorWindowPositionX + FULL_WIN_OFFSET_X,projectorWindowPositionY + FULL_WIN_OFFSET_Y);
		glutReshapeWindow(displayWidth ,displayHeight);
	}
	winProjFullscreen = !winProjFullscreen;
}

/**
 * Switch on/off fullscreen mode for camera window
 */
void winSwitchCamFullscreen()
{
	if(winCamFullscreen)
	{
		glutReshapeWindow(DEF_WIN_WIDTH,DEF_WIN_HEIGHT);
		glutPositionWindow(DEF_WIN_POS_X,DEF_WIN_POS_Y);
	}
	else
	  glutFullScreen();
	winCamFullscreen = !winCamFullscreen;
}

/**
 * Switch on/off fullscreen mode for camera or projector window
 */
void switchFullscreen()
{
	if(glutGetWindow() == winProjId)
		winSwitchProjFullscreen();
	else
		winSwitchCamFullscreen();
}

/**
 * Draws pattern made by coded markers.
 */
void DrawTexture(float x1,float y1,float x2,float y2)
{
	if(cameraTextureId != -1)
	{
		GL::DrawTexture(&cameraTextureId,x1,y1,x2,y2);
	}
	return;
}

void GetProjectorCornerPoints()
{
	if(!pointsChanged)
		return;
	pointsChanged = false;
	int i =0;
	for(i = 0;i < projectorCount;i++)
	{
		//if(i != projectorIndex)
		//	continue;

		vector<geometry::Vector2Df> * p = &(points[i]);
		CvMat * cornerPoints = calibrationUnit->GetPoints(i,CAMERA);
		p->clear();
		Cvh::ConvertMatToVector2D(cornerPoints,p);
		cvReleaseMat(&cornerPoints);
	}
}

/**
 * Draw result of camera image processing.
 */
void DrawProcessingResult()
{
	GetProjectorCornerPoints();

	int i =0;
	for(i = 0;i < projectorCount;i++)
	{
		glColor3f(0.0f,1.0f,0.0f);
		GL::DrawVector(points[i],imgCamScale,GL_LINE_LOOP);
	}
	//CalculateDisplayPoints();
	glColor3f(0.0f,0.0f,1.0f);
	GL::DrawVector(displayCorners,imgCamScale,GL_LINE_LOOP);
	glColor3f(1.0f,1.0f,1.0f);
}

/**
 * Draw camera image.
 */
void DrawCam()
{
	DrawTexture(0.0f,0.0f,(float)winCamWidth,(float)winCamHeight);
	DrawProcessingResult();
}

int GetNextProjectorIndex()
{
	int index = projectorIndex;
	if(index+1 == projectorCount)
	{
		index = 0;
	}
	else
	{
		index++;
	}
	return index;
}

void nextProjector()
{
	int index = GetNextProjectorIndex();
	if(index != 0)
		projectorChaneged = true;

	projectorIndex = index;
}

int GetColumnsProjectorIndex()
{
	int index = 0;
	if(projectorIndex == 0)
		index = chCols-2;
	return index;
}

void CalculateHomography()
{
	prepareLigthMaps = calibrationUnit->CalibrateDisplay(baseProjectorIndex);
		return;
}

/**
 *
 */
void ProcessImage()
{
	IplImage * img = NULL;
	if((img = camera->GetImage()) != NULL)
	{
		if(projectorIndex != projectorCount)
		{
			IplImage * grayImage = Cvh::convertRGBtoGray(img);
			if(detector->Detect(grayImage) == true)
			{
				detector->DrawCorners(img);

				pointsChanged = true;

				Projector * projector = calibrationUnit->GetProjector(projectorIndex);
				if(projector != NULL)
				{
					projector->SetPoints(PointsType::DETECTED,detector->GetPoints());
					projector->CalculateHomographyCamera(NONE_INDEX,!keystoneCorrection);
					CalculateHomography();
				}
			}
			//set camera image scale
			imgCamScale.SetX((float)winCamWidth/(float)img->width);
			imgCamScale.SetY((float)winCamHeight/(float)img->height);
			cvReleaseImage(&grayImage);
			goOnece = true;
		}
		cameraTextureId = 0;
		GL::LoadTextureFromCvImage(img,&cameraTextureId);
		cvReleaseImage(&img);
	}
}

void takPicture()
{
	if(projectorIndex!= projectorCount && projectorChaneged && projectorRepaint)
	{
		projectorChaneged = false;
		projectorRepaint = false;
	}
}

void DrawChackerborad(int x,int y,int width,int height,bool withKeystoneCorrecrion = false)
{
	int originX = x;
	int originY = y;

	GL::DrawRectangle(x,y,x+width,y+height,false);

	int borderX = CHACKERBOARD_BORDER_X;
	width -= 2*borderX;
	//border Y
	int size = width/chCols;
	int borderY = (height-(chRows*size))/2;
	//border X
	int border = width%chCols;
	if(border != 0)
	{
		borderX += (border/2);
		width -= border;
	}

	x += borderX;
	y += borderY;

	if(!withKeystoneCorrecrion)
	{
		GL::DrawChackerboard(x,y, size,chRows,chCols);

		//projectedCorners = Cvh::GetChackerboardInnerCorners(x-originX,y-originY, size,chRows,chCols);
	}
	else
	{
		CvMat * pProjector = Cvh::GetChackerboardCorners(x,y, size,chRows,chCols);
		//Cvh::PrintCvMatrix(pProjector,"pProjector");

		CvMat * pKeystone = calibrationUnit->TransformPoints(projectorIndex,PK,pProjector);
		//Cvh::PrintCvMatrix(pKeystone,"pKeystone");
		std::vector<geometry::Vector2Df> * pkv = Cvh::ConvertMatToVector2D(pKeystone);
		GL::DrawChackerboard(chRows,chCols,*pkv);
	}
	//comment
	if(projectedCorners != NULL)
		cvReleaseMat(&projectedCorners);

	projectedCorners = Cvh::GetChackerboardInnerCorners(x,y, size,chRows,chCols);

	Projector * projector = calibrationUnit->GetProjector(projectorIndex);
	projector->SetPoints(PointsType::PROJECTED,Cvh::ConvertMatToVector2D(projectedCorners));
	cvReleaseMat(&projectedCorners);
}

/**
 * Draws pattern made by coded markers.
 */
void DrawPattern(int x,int y,int width,int height)
{
	int x1 = x + width;
	int y1 = y + height;
	int borderX,borderY;
	switch(patternIndex)
	{
		case WHITE:
			GL::DrawRectangle(x,y,x1,y1,false);
			break;
		case BLACK:
			GL::DrawRectangle(x,y,x1,y1,true);
			break;
		case CHACKERBOARD:
			DrawChackerborad(x,y,width,height);
			break;
		case TEXTURE:
			DrawTexture(x,y,x1,y1);
			break;
	}
	return;
}



void DrawProjectorImages()
{
	int luCornerX = 0; //left upper corner X
	int luCornerY = 0; //left upper corner Y
	int dW = projectorWidth;
	int dH = projectorHeight;

	if(projectorIndex == projectorCount)
	{
		dW = displayWidth;
		dH = displayHeight;
	}
	else
		Common::countCoordinates(projectorIndex,dW,dH,&luCornerX,&luCornerY,false);

	if(!keystoneCorrection)
		DrawPattern(luCornerX,luCornerY,dW,dH);
	else if(keystoneCorrection && projectorIndex != projectorCount)
	{
		if(chessTextureId == -1)
		{
			IplImage * img = NULL;
			img = Cvh::LoadImage(chackerboardImageName);
			GL::LoadTextureFromCvImage(img,&chessTextureId);
		}
		CvMat * pKeystoneCv = calibrationUnit->GetPoints(projectorIndex,PROJECTOR_KEYSTONE);
		vector<geometry::Vector2Df> * pKeystone = Cvh::ConvertMatToVector2D(pKeystoneCv);
		GL::DrawTexture(&chessTextureId,pKeystone);
	}
	glDisable(GL_BLEND);
}

void DrawTestMarkers()
{
	int width = projectorWidth;
	int height = projectorHeight;

	int nextIndex = GetNextProjectorIndex();
	Projector * p1 = calibrationUnit->GetProjector(projectorIndex);
	Projector * p2 = calibrationUnit->GetProjector(nextIndex);
	std::vector<geometry::Vector2Df> * projectedPointsP1 = p1->GetPoints(PROJECTED,nextIndex);
	std::vector<geometry::Vector2Df> * detectedPointsP1 = p1->GetPoints(DETECTED,nextIndex);
	std::vector<geometry::Vector2Df> * projectedPointsP2 = p2->TranformPoints(CPO,detectedPointsP1);

	glColor3f(1,0.5,0);
	GL::DrawMarks(projectedPointsP1);
	glColor3f(0,0.5,1);
	GL::DrawMarks(projectedPointsP2);
}

/**
 * Grabs the current openGL error and displays it
 * on the console (if any).
 */
void DisplayGLError()
{
	GLenum error = glGetError();
	if(error != GL_NO_ERROR)
	{
		printf("GL Error: %s\n", gluErrorString(error));
	}
}

/**
 * Setup modelview transformation matrix
 * @param transformMat Transformation matrix 4x4
 */
void setupTransformation(GLfloat * transformMat)
{
	//glLoadIdentity();
	//GL::PrintGLMatrix(GL_MODELVIEW_MATRIX,"MODELVIEW");
	//glTranslatef(0.0f, 0.0f, -1.0f);
	//GL::PrintGLMatrix(GL_MODELVIEW_MATRIX,"MODELVIEW");
	glMultMatrixf((Matrix4x4&)*transformMat);
	//glTranslatef(10.0f, 0.0f, 0.0f);
	//GL::PrintGLMatrix(GL_MODELVIEW_MATRIX,"MODELVIEW");
	//glTranslatef(0.0f, 0.0f, 1.0f);
	//GL::PrintGLMatrix(GL_MODELVIEW_MATRIX,"MODELVIEW");
}

/**
 * Initialize orthographic projection
 * @param width Window width
 * @param height Window height
 */
void initOrthoProjection(int width,int height)
{
	//Reset current setup
	glViewport(0,0,width,height);
	//clears the color and the buffers
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


	//------------- CAMERA PIXEL COORDINATES ----------------------
	//sets the projection matrix
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, width, height, 0, 0.01, 1000.0);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(0.0f, 0.0f, -1.0f);
}

/**
 *  Draw test image into frame given by corner points.
 *  @param cornerPoints Drawing area corner points
 *  @param r Red color value for border lines.
 *  @param g Green color value for border lines.
 *  @param b Blue color value for border lines.
 */
void DrawTestImage(vector<geometry::Vector2Df> * cornerPoints,GLfloat r,GLfloat g,GLfloat b)
{
	GLuint textureId = testTextureIds[testImageIndex];

	if(textureId == -1)
	{
		IplImage * img = NULL;
		img = Cvh::LoadImage(testImages[testImageIndex]);
		GL::LoadTextureFromCvImage(img,&textureId);
		 testTextureIds[testImageIndex] = textureId;
	}
	GL::DrawTexture(&textureId,cornerPoints);

	//draw frame
	glColor3f(r,g,b);
    glBegin(GL_LINE_LOOP);//start drawing a line loop
		int i;
		for(i = 0;i < cornerPoints->size();i++)
		{
			glVertex2d(cornerPoints->at(i).GetX(), cornerPoints->at(i).GetY());
		}

    glEnd();//end drawing of line loop
	glColor3f(1.0f,1.0f,1.0f); //white color
}

void DrawTestGrid(float x1,float x2,float y1,float y2,int step,GLfloat r,GLfloat g,GLfloat b)
{
	if(!showGrid)
		return;

	vector<geometry::Vector2Df> points;
	for(int k = step;k<=x2;k=k+step)
	{
		points.push_back(geometry::Vector2Df(k,y1));
		points.push_back(geometry::Vector2Df(k,y2));
	}

	for(int j = step;j<=y2;j=j+step)
	{
		points.push_back(geometry::Vector2Df(x1,j));
		points.push_back(geometry::Vector2Df(x2,j));
	}

	glColor3f(r,g,b); //blue color
	for(int t=0;t < points.size();t=t+2)
	{
		geometry::Vector2Df point1 = points.at(t);
		geometry::Vector2Df point2 = points.at(t+1);
		GL::DrawLine(point1.GetX(),point1.GetY(),point2.GetX(),point2.GetY());
	}
	glColor3f(1.0f,1.0f,1.0f); //white
}

void ComputeLUpoint(int projectorIndex,int *x,int *y,bool useMyInverseSetup = false,bool myInverseY = false)
{
	*x = 0;
	*y = 0;
	bool inverseY = true;
	if(projectorVerticalCount <= 1)
		inverseY = false;
	if(useMyInverseSetup)
	{
		inverseY = myInverseY;
	}
	Common::countCoordinates(projectorIndex,projectorWidth,projectorHeight,x,y,inverseY);
}

void InitViewport(int index)
{
	int x = 0;
	int y = 0;
	ComputeLUpoint(index,&x,&y);
	glScissor(x,y,projectorWidth,projectorHeight);
}

void DrawLigthMaps(bool drawRect = false)
{
	int x = 0; //left upper corner X
	int y = 0; //left upper corner Y
	int w = projectorWidth;
	int h = projectorHeight;

	for(int i = 0;i < projectorCount;i++)
	{
		Projector * p = calibrationUnit->GetProjector(i);
		ComputeLUpoint(i,&x,&y,true,false);
		GLuint pt = p->GetLightMapTexture();

		if(drawRect)
		{
			glColor3f(1.0f,1.0f,1.0f);
			glRecti(x,y,x+w,y+h);
		}

		glEnable(GL_BLEND);
		 	 glBlendFunc (GL_ONE_MINUS_SRC_ALPHA,GL_SRC_ALPHA);
			//glBlendFunc(GL_SRC_ALPHA, GL_ONE);
			//glBlendFunc(GL_ONE, GL_ONE);
			GL::DrawTexture(&pt,x,y,x+w,y+h);

		glDisable(GL_BLEND);
	}
}

void CreateProjectorLightMaps(IplImage * referenceImage)
{
	if(referenceImage == NULL)
		return;

	int x = 0; //left upper corner X
	int y = 0; //left upper corner Y

	//cvSaveImage("referenceImage.jpg",referenceImage);
	for(int i = 0;i < projectorCount;i++)
	{
		Projector * p = calibrationUnit->GetProjector(i);
		ComputeLUpoint(i,&x,&y);
		IplImage * image = GL::TakeScreenshot(x,y,projectorWidth,projectorHeight,true);
		//cvSaveImage("p1image.jpg",image);
		IplImage * lightMap = Cvh::DivImages(referenceImage,image);
		p->SetLightMap(lightMap);
	}
}

void PrepareLightMaps()
{
	int x = 0;
	int y = 0;
	IplImage * referenceImage = NULL;
	float maxIntensity = 1.0f;

	glEnable(GL_SCISSOR_TEST);
	for(int i = 0;i < projectorCount;i++)
	{
		InitViewport(i);
		Projector * p = calibrationUnit->GetProjector(i);
		if(p->GetLightMap() != NULL)
			continue;

		//dont invert Y axis -> drawing
		ComputeLUpoint(i,&x,&y,true,false);
		GL::DrawIntensityMap(x,y,projectorWidth,projectorHeight,maxIntensity);
		if(referenceImage == NULL)
		{
			//invert Y axis acording to projector layout -> take a screenshot
			ComputeLUpoint(i,&x,&y);
			referenceImage = GL::TakeScreenshot(x,y,projectorWidth,projectorHeight,true);
		}

		std::vector<CvMat*> * matVector = calibrationUnit->GetAdjacentProjectroHomographies(i);

		//adjactent projector index
		int pIndex = 0;
		for(int j = 0;j < matVector->size();j++)
		{
			if(pIndex == i)
				pIndex++;

			glPushMatrix();
			CvMat * h = matVector->at(j);
			GLfloat * transformMat = GL::MatrixCvToGL(h);
			setupTransformation(transformMat);

			//dont invert Y axis -> drawing
			ComputeLUpoint(pIndex,&x,&y,true,false);
			GL::DrawIntensityMap(x,y,projectorWidth,projectorHeight,maxIntensity);
			glPopMatrix();
			pIndex++;

		}
	}
	glDisable(GL_SCISSOR_TEST);
	CreateProjectorLightMaps(referenceImage);
}



void DrawPrewarpedImage()
{
	int x1,y1,x2,y2,step;
	step = GRID_STEP_SIZE;
	x1 = 0;
	y1 = 0;
	x2 = mergeDisplayWidth;//1400;
	y2 = mergeDisplayHeight;//900;

	vector<geometry::Vector2Df> cornerPoints;
	GL::RectToPoins(x1,y1,x2-x1,y2-y1,&cornerPoints);

	for(int i = 0;i < projectorCount;i++)
	{
		CvMat * homography = calibrationUnit->GetHomography(i,HomogType::DP);

		glEnable(GL_SCISSOR_TEST);
		InitViewport(i);

		glPushMatrix();
		GLfloat * transformMat = GL::MatrixCvToGL(homography);
		setupTransformation(transformMat);
		DrawTestImage(&cornerPoints,0.0f,0.0f,0.9f);
		DrawTestGrid(x1,x2,y1,y2,step,1.0f,1.0f,1.0f);
		glPopMatrix();

		glDisable(GL_SCISSOR_TEST);
	}
	DrawLigthMaps();
}

/**
 * Redisplay projector window
 */
void displayWinProj(void)
{
	initOrthoProjection(winProjWidth,winProjHeight);
	if(testMode)
		DrawPrewarpedImage();
	else if(testHomographyMode)
	{
		DrawTestMarkers();
	}
	else
	{
		if(prepareLigthMaps)
		{
			PrepareLightMaps(); //calculate light maps
			prepareLigthMaps = false;
		}

		if(showLightMaps)
			DrawLigthMaps(true); //show light maps
		else
			DrawProjectorImages(); //draw chackerboards
	}

	DisplayGLError();
	glutSwapBuffers();
	projectorRepaint = true;
}

/**
 * Redisplay Camera window
 */
void displayWinCam(void)
{
	initOrthoProjection(winCamWidth,winCamHeight);
	DrawCam();
	DisplayGLError();
	glutSwapBuffers();
}

/**
 * Redisplay camera an projector windows
 */
static void redisplayWindows()
{
	int currentWindow = glutGetWindow();
	glutPostRedisplay();
	if(currentWindow == winCamId)
		glutSetWindow(winProjId);
	else
		glutSetWindow(winCamId);
	glutPostRedisplay();
	glutSetWindow(currentWindow);
}

/**
 * GLUT idle function.
 * Updates the room model (frame capture, pose estimation ...)
 * and calls the display function.
 */
static void idle()
{
	//sets texture for camera
	glutSetWindow(winCamId);
	ProcessImage();

	redisplayWindows();
	takPicture();
}

/**
 * GLUT keyboard function.
 * Exits the program on Esc key.
 * @param key The key pressed.
 * @param x The x coordinate of the mouse.
 * @param y The y coordinate of the mouse.
 */
static void keyboard(unsigned char key, int x, int y){

		switch(key){
				case 27:
					exit(0);
					break;
				case 'f':
				case 'F':
					switchFullscreen();
				  break;
				case 'n':
				case 'N':
					nextProjector();
				  break;
			    case 'p':
				case 'P':
					if(!showLightMaps)
						prepareLigthMaps = !prepareLigthMaps;
					showLightMaps = !showLightMaps;
//						if(patternIndex == WHITE)
//						{
//							patternIndex = 0;
//						}
//						else
//							patternIndex++;
				  break;
				case '+':
					if(Common::doubleNum(chRows,true) > projectorWidth)
							break;
						chRows = Common::doubleNum(chRows,true);
						chCols = Common::doubleNum(chCols,true);
						detector->Init(chRows,chCols,0);
						cameraTextureId = -1;
				  break;
				case '-':
						if(chRows <= CHACKERBOARD_ROWS_MIN)
							break;
						chRows = Common::doubleNum(chRows,false);
						chCols = Common::doubleNum(chCols,false);
						detector->Init(chRows,chCols,0);
						cameraTextureId = -1;
				  break;
				case 't':
				case 'T':
					camera->TakePicture();
					break;
				case 'A':
				case 'a':
					testMode = !testMode;
					break;
				case 'H':
				case 'h':
					testHomographyMode = !testHomographyMode;
					break;
				case 'k':  //keystone correction
				case 'K':
					keystoneCorrection = !keystoneCorrection;
					break;
				case 's':
				case 'S':
					showGrid = !showGrid;
					break;
				case 'Q':
				case 'q':
					testImageIndex++;
					if(testImageIndex > testImageMaxIndex)
						testImageIndex = 0;
					break;
				case '1':
					baseProjectorIndex = 0;
					CalculateHomography();
					break;
				case '2':
					baseProjectorIndex = 1;
					CalculateHomography();
					break;
				case '3':
					baseProjectorIndex = 2;
					CalculateHomography();
					break;
				case '4':
					baseProjectorIndex = 3;
					CalculateHomography();
					break;
		}
}

/**
 * Reshapes the scene.
 * @param w The new width of the window.
 * @param h The new height of the window.
 */
static void reshape(int w, int h)
{
	if (h==0)// Zabezpečení proti dělení nulou
	{
		h=1;// Nastaví výšku na jedna
	}
	glViewport(0,0,(GLsizei)w,(GLsizei)h);// Resetuje aktuální nastavení
	glutPostRedisplay();
}

/**
 * Resize projector window.
 * @param w New window width.
 * @param h New window height.
 */
static void reshapeWinProj(int w, int h)
{
	winProjWidth = w;
	winProjHeight = h;
	reshape(w,h);
}

/**
 * Resize camera window.
 * @param w New window width.
 * @param h New window height.
 */
static void reshapeWinCam(int w, int h)
{
	if(imgCamScale.GetX() != 1)
	{
		imgCamScale.SetX((imgCamScale.GetX()/winCamWidth)*w);
		imgCamScale.SetY((imgCamScale.GetY()/winCamHeight)*h);
	}

	winCamWidth = w;
	winCamHeight = h;
	reshape(w,h);
}

/**
 * Close window procedure.
 */
static void closeWindow()
{
	exit(0);
}

static int createWindow(char* name)
{
	int windowId = glutCreateWindow(name);
	//define a window position for second window
	glutPositionWindow(DEF_WIN_POS_X,DEF_WIN_POS_Y);
	//glutWMCloseFunc(closeWindow);
	glutKeyboardFunc(keyboard);
	return windowId;
}

/**
 * Inits OpenGL, sets the callback functions and general OpenGL settings.
 */
static void myInitGL(int argc, char **argv){

	//init glut
	glutInit(&argc, argv);

	//init display mode
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	//window
	glutInitWindowSize(winCamWidth, winCamHeight);

	//camera window
	winCamId = createWindow(WINDOW_CAMERA_NAME);
	// register callbacks for subwindow, which is now current
	glutReshapeFunc(reshapeWinCam);
	glutDisplayFunc(displayWinCam);
	//glutKeyboardFunc(keyboardWinCam);

	//projector window
	winProjId = createWindow(WINDOW_PROJECTOR_NAME);
	// register callbacks for subwindow, which is now current
	glutReshapeFunc(reshapeWinProj);
	glutDisplayFunc(displayWinProj);
	//glutKeyboardFunc(keyboardWinProj);

	glutIdleFunc(idle);

	//set openGL parameters...
	glDisable(GL_DEPTH_TEST);
	glShadeModel(GL_SMOOTH);

	//viewport
	glViewport(0, 0, winCamWidth, winCamHeight);
}

/**
 * Init basic display parameters.
 */
void initDisplayParams()
{
	projectorHorizontalCount = PROJECTOR_HORIZONTAL;
	projectorVerticalCount = PROJECTOR_VERTICAL;
	projectorWidth = PROJECTOR_WIDTH;
	projectorHeight = PROJECTOR_HEIGHT;
	projectorWindowPositionX = FULL_WIN_POS_X;
	projectorWindowPositionY = FULL_WIN_POS_Y;
	chRows = CHACKERBOARD_ROWS;
	chCols = CHACKERBOARD_COLS;
	mergeDisplayWidth = DEFAULT_MERGE_DISPLAY_WIDTH;
	mergeDisplayHeight = DEFAULT_MERGE_DISPLAY_HEIGHT;

	winCamWidth = DEF_WIN_WIDTH;
	winCamHeight = DEF_WIN_HEIGHT;
	winProjWidth = DEF_WIN_WIDTH;
	winProjHeight = DEF_WIN_HEIGHT;
}

void calculateParams()
{
	displayWidth = projectorWidth*projectorHorizontalCount;
	displayHeight = projectorHeight*projectorVerticalCount;
	projectorCount = projectorHorizontalCount*projectorVerticalCount;
}

int main( int argc, char** argv )
{
	//load variables
	initDisplayParams();

//	projectorHorizontalCount = PROJECTOR_HORIZONTAL;
		//projectorVerticalCount = 1;//PROJECTOR_VERTICAL;
//		projectorWidth = PROJECTOR_WIDTH;
//		projectorHeight = PROJECTOR_HEIGHT;
//		projectorWindowPositionX = FULL_WIN_POS_X;
//		projectorWindowPositionY
//		mergeDisplayWidth = 1300;
//		mergeDisplayHeight = 700;

	calculateParams();
	if(!cameraOffline)
		camera = new CannonCamera();
	detector  = new CheckerDetector();
	calibrationUnit = new CalibrationUnit(projectorCount,projectorHorizontalCount,projectorWidth,projectorHeight);
	calibrationUnit->CalculateOverlapPolygons();

	detector->Init(chRows,chCols,0);
	projectedCorners = NULL;

	myInitGL(argc, argv);
	glutMainLoop();

	return 0;
}
