#define  _CRT_SECURE_NO_WARNINGS
#ifdef _MSC_VER                         // Check if MS Visual C compiler
#  pragma comment(lib, "opengl32.lib")  // Compiler-specific directive to avoid manually configuration
#  pragma comment(lib, "glu32.lib")     // Link libraries
#endif
#ifdef _MSC_VER
#   ifndef _MBCS
#      define _MBCS
#   endif
#   ifdef _UNICODE
#      undef _UNICODE 
#   endif
#   ifdef UNICODE
#      undef UNICODE 
#   endif
#endif
// #define GLEW_STATIC
#include <windows.h>            // Window defines
#include "GL/glew.h"            // Dołączony jako pliki glew.c i glew.h
//#include <gl/gl.h>            // OpenGL
#include <gl/glu.h>             // biblioteka GLU; program kompiluje się bez niej, ale w celu uniknięcia w przyszłości niespodzianek to została dołączona
#include <math.h>				// Define for sqrt
//#include <stdio.h>
#include <iostream>
#include "RESOURCE.H"           // About box resource identifiers.
#include "szescian.h"
#include "loadOBJ.h"
//#include <vector>
#include "GL/glm/glm.hpp"
//#include "GL/glfw3.h"
#include <ctime>

using namespace glm;

#define glRGB(x, y, z)	glColor3ub((GLubyte)x, (GLubyte)y, (GLubyte)z)
#define BITMAP_ID 0x4D42		// identyfikator formatu BMP
#define GL_PI 3.1415
#define getTime lastTime = std::time(nullptr);
#define timestampedCout(msg) {getTime; std::cout << "( " << lastTime << ") " << msg << "\n";}

//using namespace std;

HPALETTE hPalette = NULL;

// Application name and instance storeage
static LPCTSTR lpszAppName = "grafikaKBT";
static HINSTANCE hInstance;

// Rotation amounts
static GLfloat xRot = 0.0f;
static GLfloat yRot = 0.0f;
static GLfloat zRot = 0.0f;


static GLsizei lastHeight;
static GLsizei lastWidth;


int Foward=0;
int Sides=0;


// Opis tekstury
BITMAPINFOHEADER    bitmapInfoHeader;    // nagłówek obrazu
unsigned char*      bitmapData;          // dane tekstury
unsigned int        texture[2];          // obiekt tekstury

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

BOOL APIENTRY AboutDlgProc(HWND hDlg, UINT message, UINT wParam, LONG lParam);

void SetDCPixelFormat(HDC hDC);

int polygonmode = 0;

// Change viewing volume and viewport. Called when window is resized
void ChangeSize(GLsizei w, GLsizei h) {
	GLfloat nRange = 100.0f;
	GLfloat fAspect;
	// Prevent a divide by zero
	if (h == 0) h = 1;

	lastWidth = w;
	lastHeight = h;

	fAspect = (GLfloat)w / (GLfloat)h;
	// Set Viewport to window dimensions
	glViewport(0, 0, w, h);

	// Reset coordinate system
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	// Establish clipping volume (left, right, bottom, top, near, far)
	if (w <= h) glOrtho(-nRange, nRange, -nRange * h / w, nRange * h / w, -nRange, nRange);
	else glOrtho(-nRange * w / h, nRange * w / h, -nRange, nRange, -nRange, nRange);

	// Establish perspective: 
	/*
	gluPerspective(60.0f, fAspect, 1.0, 400);
	*/

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

// LoadBitmapFile
// opis: ładuje mapę bitową z pliku i zwraca jej adres.
//       Wypełnia strukturę nagłówka.
//	 Nie obsługuje map 8-bitowych.
unsigned char* LoadBitmapFile(char* filename, BITMAPINFOHEADER* bitmapInfoHeader) {
	FILE* filePtr;                              // wskaźnik pozycji pliku
	BITMAPFILEHEADER	bitmapFileHeader;       // nagłówek pliku
	unsigned char*      bitmapImage;            // dane obrazu
	int	                imageIdx = 0;           // licznik pikseli
	unsigned char       tempRGB;                // zmienna zamiany składowych

	// otwiera plik w trybie "read binary"
	filePtr = fopen(filename, "rb");
	if (filePtr == NULL)
		return NULL;

	// wczytuje nagłówek pliku
	fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);

	// sprawdza, czy jest to plik formatu BMP
	if (bitmapFileHeader.bfType != BITMAP_ID)
	{
		fclose(filePtr);
		return NULL;
	}

	// wczytuje nagłówek obrazu
	fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);

	// ustawia wskaźnik pozycji pliku na początku danych obrazu
	fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

	// przydziela pamięć buforowi obrazu
	bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);

	// sprawdza, czy udało się przydzielić pamięć
	if (!bitmapImage) {
		free(bitmapImage);
		fclose(filePtr);
		return NULL;
	}

	// wczytuje dane obrazu
	fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);

	// sprawdza, czy dane zostały wczytane
	if (bitmapImage == NULL) {
		fclose(filePtr);
		return NULL;
	}

	// zamienia miejscami składowe R i B 
	for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) {
		tempRGB = bitmapImage[imageIdx];
		bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
		bitmapImage[imageIdx + 2] = tempRGB;
	}

	// zamyka plik i zwraca wskaźnik bufora zawierającego wczytany obraz
	fclose(filePtr);
	return bitmapImage;
}

void SetDCPixelFormat(HDC hDC) {
	int nPixelFormat;

	static PIXELFORMATDESCRIPTOR pfd = {
		sizeof(PIXELFORMATDESCRIPTOR),          // Size of this structure
		1,                                      // Version of this structure    
		PFD_DRAW_TO_WINDOW |                    // Draw to Window (not to bitmap)
		PFD_SUPPORT_OPENGL |                    // Support OpenGL calls in window
		PFD_DOUBLEBUFFER,                       // Double buffered
		PFD_TYPE_RGBA,                          // RGBA Color mode
		24,                                     // Want 24bit color 
		0,0,0,0,0,0,                            // Not used to select mode
		0,0,                                    // Not used to select mode
		0,0,0,0,0,                              // Not used to select mode
		32,                                     // Size of depth buffer
		0,                                      // Not used to select mode
		0,                                      // Not used to select mode
		PFD_MAIN_PLANE,                         // Draw in main plane
		0,                                      // Not used to select mode
		0,0,0 };                                // Not used to select mode

	// Choose a pixel format that best matches that described in pfd
	nPixelFormat = ChoosePixelFormat(hDC, &pfd);

	// Set the pixel format for the device context
	SetPixelFormat(hDC, nPixelFormat, &pfd);
}

std::vector <glm::vec3> vertices;
std::vector <glm::vec2> uvs;
std::vector <glm::vec3> normals; // Won't be used at the moment.
GLuint vertexbuffer;
GLuint uvbuffer;
std::time_t lastTime = std::time(nullptr);

void SetupRC() {
	// Light values and coordinates
	//GLfloat  ambientLight[] = { 0.3f,   0.3f,   0.3f, 1.0f };
	//GLfloat  diffuseLight[] = { 0.7f,   0.7f,   0.7f, 1.0f };
	//GLfloat  specular[]     = { 1.0f,   1.0f,   1.0f, 1.0f };
	//GLfloat  lightPos[]     = { 0.0f, 150.0f, 150.0f, 1.0f };
	//GLfloat  specref[]      = { 1.0f,   1.0f,   1.0f, 1.0f };


	glEnable(GL_DEPTH_TEST);    // Hidden surface removal
	glFrontFace(GL_CCW);        // Counter clock-wise polygons face out
	glEnable(GL_CULL_FACE);     // Do not calculate inside of jet // !!! znacząco poprawia wydajność

	// Enable lighting
	//glEnable(GL_LIGHTING);

	// Setup and enable light 0
	//glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
	//glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
	//glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
	//glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
	//glEnable(GL_LIGHT0);

	// Enable color tracking
	//glEnable(GL_COLOR_MATERIAL);

	// Set Material properties to follow glColor values
	//glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

	// All materials hereafter have full specular reflectivity
	// with a high shine
	//glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
	//glMateriali(GL_FRONT, GL_SHININESS, 128);


	// White background
	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
	// Black brush
	glColor3f(0.0, 0.0, 0.0);

	// Initialize GLEW
	timestampedCout("Inicjalizowanie GLEW...");
	glewExperimental = true; // Needed for core profile
	if (glewInit() != GLEW_OK) {
		fprintf(stderr, "Failed to initialize GLEW\n");
		getchar();
		// glfwTerminate();
		return;
	}

	timestampedCout("Zainicjalizowano GLEW.");

	// Załaduj model z pliku .obj
	// TODO: zmierzyć czas ładowania łazika w cyklach procesora/mikrosekundach

	timestampedCout("Ladowanie lazika...");
	bool res = loadOBJ("res/models/lazik3.obj", vertices, uvs, normals);
	// bool res = loadOBJ("res/models/suzanne.obj", vertices, uvs, normals);

	if (res) timestampedCout("Pomyslnie zaladowano model lazika.")
	else timestampedCout("Nie udalo sie zaladowac modelu lazika.");

	glGenBuffers(1, &vertexbuffer);
	glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
	glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
	// timestampedCout("rozmiar vertexbuffer: " << vertices.size() * sizeof(glm::vec3));

	glGenBuffers(1, &uvbuffer);
	glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
	glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(glm::vec2), &uvs[0], GL_STATIC_DRAW);
	// timestampedCout("rozmiar uvbuffer: " << uvs.size() * sizeof(glm::vec2));

	//glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

}

void RenderScene(void) {

	//float normal[3];	// Storage for calculated surface normal

	// Save the matrix state and do the rotations
	glPushMatrix();
	glRotatef(xRot, 1.0f, 0.0f, 0.0f);
	glRotatef(yRot, 0.0f, 1.0f, 0.0f);
	glRotatef(zRot, 0.0f, 0.0f, 1.0f);

	glTranslatef(Foward, Sides, 0);

	gluLookAt(
			  50+Foward,	0+Sides,	50,

			  0+Foward,	0+Sides,	0,

			  0		,	1,	0);

	// gluLookAt(
	// 0, 0, 0, // the position of your camera, in world space
	// 0, 0, 0,   // where you want to look at, in world space
	// 0, 1, 0        // probably glm::vec3(0,1,0), but (0,-1,0) would make you looking upside-down, which can be great too
	// );

	switch (polygonmode) {
		case 1:
			glPolygonMode(GL_FRONT, GL_LINE);
			break;
		default:
			glPolygonMode(GL_FRONT, GL_FILL);
	}


	// Clear the screen
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Use our shader
	//glUseProgram(programID);

	// // Bind our texture in Texture Unit 0
	// glActiveTexture(GL_TEXTURE0);
	// glBindTexture(GL_TEXTURE_2D, Texture);
	// // Set our "myTextureSampler" sampler to use Texture Unit 0
	// glUniform1i(TextureID, 0);

	// 1st attribute buffer : vertices
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
	glVertexAttribPointer(
		0,                  // attribute
		3,                  // size
		GL_FLOAT,           // type
		GL_FALSE,           // normalized?
		0,                  // stride
		(void*)0            // array buffer offset
	);

	// 2nd attribute buffer: UVs
	glEnableVertexAttribArray(1);
	glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
	glVertexAttribPointer(
		1,                                // attribute
		2,                                // size
		GL_FLOAT,                         // type
		GL_FALSE,                         // normalized?
		0,                                // stride
		(void*)0                          // array buffer offset
	);

	// Draw vertices
	glDrawArrays(GL_TRIANGLES, 0, vertices.size());
	
	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);
	
	// Swap buffers
	//glfwSwapBuffers(window);
	//glfwPollEvents();

	/*
	szescian nowy;
	nowy.create(0, 0, 0, 10);
	nowy.create(-10, 20, 0, 10);
	nowy.create(25, 10, 0, 10);
	*/

	//Wyrysowanie prostokata:
	//glRectd(-10.0,-10.0,20.0,20.0);

	glPopMatrix(); // wymagane
	glMatrixMode(GL_MODELVIEW); // zmniejsza zużycie GPU

	// Flush drawing commands
	// glFlush();
}

// If necessary, creates a 3-3-2 palette for the device context listed.
HPALETTE GetOpenGLPalette(HDC hDC) {
	HPALETTE hRetPal = NULL;	// Handle to palette to be created
	PIXELFORMATDESCRIPTOR pfd;	// Pixel Format Descriptor
	LOGPALETTE* pPal;			// Pointer to memory for logical palette
	int nPixelFormat;			// Pixel format index
	int nColors;				// Number of entries in palette
	int i;						// Counting variable
	BYTE RedRange, GreenRange, BlueRange;
	// Range for each color entry (7,7,and 3)


	// Get the pixel format index and retrieve the pixel format description
	nPixelFormat = GetPixelFormat(hDC);
	DescribePixelFormat(hDC, nPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);

	// Does this pixel format require a palette?  If not, do not create a
	// palette and just return NULL
	if (!(pfd.dwFlags & PFD_NEED_PALETTE)) return NULL;

	// Number of entries in palette.  8 bits yeilds 256 entries
	nColors = 1 << pfd.cColorBits;

	// Allocate space for a logical palette structure plus all the palette entries
	pPal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + nColors * sizeof(PALETTEENTRY));

	// Fill in palette header 
	pPal->palVersion = 0x300;		// Windows 3.0
	pPal->palNumEntries = nColors; // table size

	// Build mask of all 1's.  This creates a number represented by having
	// the low order x bits set, where x = pfd.cRedBits, pfd.cGreenBits, and
	// pfd.cBlueBits.  
	RedRange = (1 << pfd.cRedBits) - 1;
	GreenRange = (1 << pfd.cGreenBits) - 1;
	BlueRange = (1 << pfd.cBlueBits) - 1;

	// Loop through all the palette entries
	for (i = 0; i < nColors; i++) {

		// Fill in the 8-bit equivalents for each component
		pPal->palPalEntry[i].peRed = (i >> pfd.cRedShift) & RedRange;
		pPal->palPalEntry[i].peRed = (unsigned char)(
			(double)pPal->palPalEntry[i].peRed * 255.0 / RedRange);

		pPal->palPalEntry[i].peGreen = (i >> pfd.cGreenShift) & GreenRange;
		pPal->palPalEntry[i].peGreen = (unsigned char)(
			(double)pPal->palPalEntry[i].peGreen * 255.0 / GreenRange);

		pPal->palPalEntry[i].peBlue = (i >> pfd.cBlueShift) & BlueRange;
		pPal->palPalEntry[i].peBlue = (unsigned char)(
			(double)pPal->palPalEntry[i].peBlue * 255.0 / BlueRange);

		// pPal->palPalEntry[i].peFlags = (unsigned char) NULL;
		pPal->palPalEntry[i].peFlags = 0;
	}


	// Create the palette
	hRetPal = CreatePalette(pPal);

	// Go ahead and select and realize the palette for this device context
	SelectPalette(hDC, hRetPal, FALSE);
	RealizePalette(hDC);

	// Free the memory used for the logical palette structure
	free(pPal);

	// Return the handle to the new palette
	return hRetPal;
}

int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

	MSG             msg;   // Windows message structure
	WNDCLASS        wc;    // Windows class structure
	HWND            hWnd;  // Storeage for window handle

	hInstance = hInst;

	// Register Window style
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = (WNDPROC)WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = NULL;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);

	// No need for background brush for OpenGL window
	wc.hbrBackground = NULL;

	wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
	wc.lpszClassName = lpszAppName;

	// Register the window class
	if (RegisterClass(&wc) == 0) return FALSE;


	// Create the main application window
	hWnd = CreateWindow(
		lpszAppName,
		lpszAppName,

		// OpenGL requires WS_CLIPCHILDREN and WS_CLIPSIBLINGS
		WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,

		// Window position and size
		50, 50,
		400, 400,
		NULL,
		NULL,
		hInstance,
		NULL);

	// If window was not created, quit
	if (hWnd == NULL) return FALSE;


	// Display the window
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);

	// Process application messages until the application closes
	while (GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}


// Window procedure, handles all messages for this program
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
	static HGLRC hRC;               // Permenant Rendering context
	static HDC hDC;                 // Private GDI Device context

	switch (message) {
		// Window creation, setup for OpenGL
		case WM_CREATE:
			// Store the device context
			hDC = GetDC(hWnd);

			// Select the pixel format
			SetDCPixelFormat(hDC);

			// Create palette if needed
			hPalette = GetOpenGLPalette(hDC);

			// Create the rendering context and make it current
			hRC = wglCreateContext(hDC);
			wglMakeCurrent(hDC, hRC);
			SetupRC();
			glGenTextures(2, &texture[0]); // tworzy obiekt tekstury			

			// ładuje pierwszy obraz tekstury:
			bitmapData = LoadBitmapFile((char*)"Bitmapy\\checker.bmp", &bitmapInfoHeader);

				glBindTexture(GL_TEXTURE_2D, texture[0]); // aktywuje obiekt tekstury

				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

				// tworzy obraz tekstury
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bitmapInfoHeader.biWidth,
					bitmapInfoHeader.biHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, bitmapData);

				if (bitmapData) free(bitmapData);

				// ładuje drugi obraz tekstury:
				bitmapData = LoadBitmapFile((char*)"Bitmapy\\crate.bmp", &bitmapInfoHeader);
				glBindTexture(GL_TEXTURE_2D, texture[1]); // aktywuje obiekt tekstury

				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

				// tworzy obraz tekstury
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bitmapInfoHeader.biWidth,
					bitmapInfoHeader.biHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, bitmapData);

				if (bitmapData) free(bitmapData);

				// ustalenie sposobu mieszania tekstury z tłem
				glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
				break;

		// Window is being destroyed, cleanup
		case WM_DESTROY:
			// Deselect the current rendering context and delete it
			wglMakeCurrent(hDC, NULL);
			wglDeleteContext(hRC);

			// Delete the palette if it was created
			if (hPalette != NULL) DeleteObject(hPalette);

			// Tell the application to terminate after the window
			// is gone.
			PostQuitMessage(0);
			break;

		// Window is resized.
		case WM_SIZE:
			// Call our function which modifies the clipping
			// volume and viewport
			ChangeSize(LOWORD(lParam), HIWORD(lParam));
			break;

		// The painting function.  This message sent by Windows 
		// whenever the screen needs updating.
		case WM_PAINT:
			// Call OpenGL drawing code
			RenderScene();

			SwapBuffers(hDC);

			// Validate the newly painted client area
			ValidateRect(hWnd, NULL);
			break;

		// Windows is telling the application that it may modify
		// the system palette.  This message in essance asks the 
		// application for a new palette.
		case WM_QUERYNEWPALETTE:
			// If the palette was created.
			if (hPalette) {
				int nRet;

				// Selects the palette into the current device context
				SelectPalette(hDC, hPalette, FALSE);

				// Map entries from the currently selected palette to
				// the system palette.  The return value is the number 
				// of palette entries modified.
				nRet = RealizePalette(hDC);

				// Repaint, forces remap of palette in current window
				InvalidateRect(hWnd, NULL, FALSE);

				return nRet;
			}
			break;

		// This window may set the palette, even though it is not the 
		// currently active window.
		case WM_PALETTECHANGED:
			// Don't do anything if the palette does not exist, or if
			// this is the window that changed the palette.
			if ((hPalette != NULL) && ((HWND)wParam != hWnd)) {
				// Select the palette into the device context
				SelectPalette(hDC, hPalette, FALSE);

				// Map entries to system palette
				RealizePalette(hDC);

				// Remap the current colors to the newly realized palette
				UpdateColors(hDC);
				return 0;
			}
			break;

		// Key press, check for arrow keys to do cube rotation.
		case WM_KEYDOWN:

			switch (wParam) {
				//Strzałki
				case VK_UP:
					xRot -= 5.0f;
					break;

				case VK_DOWN:
					xRot += 5.0f;
					break;

				case VK_LEFT:
					yRot -= 5.0f;
					break;

				case VK_RIGHT:
					yRot += 5.0f;
					break;
				//QQQQQQQQQQ--EEEEEEEEEE---RRRRRRRRRRR ---Spacja
				case 'Q':
					zRot += 5.0f;
					break;

				case 'E':
					zRot -= 5.0f;
					break;

				case 'R':
					xRot = 0;
					yRot = 0;
					zRot = 0;

					Foward = 0;
					Sides = 0;
					break;

				case ' ': // 32
					polygonmode = !polygonmode;
					getTime;
					if (polygonmode) timestampedCout("Uwaga! Tryb wireframe jest niewydajny i powinien sluzyc tylko do debugowania!");
					break;
				////////////////////WASD
				case 'W':
					Foward += 5;
					break;
				case 'S':
					Foward -= 5;
					break;
				case 'A':
					Sides += 5;
					break;
				case 'D':
					Sides -= 5;
					break;


				default:
					getTime;
					timestampedCout("Nacisnieto nierozpoznany klawisz: " << (int)wParam);
			}

			xRot = (const int)xRot % 360;
			yRot = (const int)yRot % 360;
			zRot = (const int)zRot % 360;

			InvalidateRect(hWnd, NULL, FALSE);
			break;

		// A menu command
		case WM_COMMAND:

			switch (LOWORD(wParam)) {

				// Exit the program
				case ID_FILE_EXIT:
					DestroyWindow(hWnd);
					break;

				// Display the about box
				case ID_HELP_ABOUT:
					DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_ABOUT), hWnd, (DLGPROC)AboutDlgProc);
					break;

			}
			break;

		default:   // Passes it on if unproccessed
			return (DefWindowProc(hWnd, message, wParam, lParam));
	}

	return (0L);
}

// Dialog procedure.
BOOL APIENTRY AboutDlgProc(HWND hDlg, UINT message, UINT wParam, LONG lParam) {

	switch (message) {
		// Initialize the dialog box
		case WM_INITDIALOG:
			int i;
			GLenum glError;

			// glGetString demo
			SetDlgItemText(hDlg, IDC_OPENGL_VENDOR, reinterpret_cast<LPCSTR>(glGetString(GL_VENDOR)));
			SetDlgItemText(hDlg, IDC_OPENGL_RENDERER, (LPCSTR)glGetString(GL_RENDERER));
			SetDlgItemText(hDlg, IDC_OPENGL_VERSION, (LPCSTR)glGetString(GL_VERSION));
			SetDlgItemText(hDlg, IDC_OPENGL_EXTENSIONS, (LPCSTR)glGetString(GL_EXTENSIONS));

			// gluGetString demo
			SetDlgItemText(hDlg, IDC_GLU_VERSION, (LPCSTR)gluGetString(GLU_VERSION));
			SetDlgItemText(hDlg, IDC_GLU_EXTENSIONS, (LPCSTR)gluGetString(GLU_EXTENSIONS));


			// Display any recent error messages
			i = 0;
			do {
				glError = glGetError();
				SetDlgItemText(hDlg, IDC_ERROR1 + i, (LPCSTR)gluErrorString(glError));
				i++;
			} while (i < 6 && glError != GL_NO_ERROR);


			return (TRUE);

			break;

		// Process command messages
		case WM_COMMAND:

			// Validate and Make the changes
			if (LOWORD(wParam) == IDOK) EndDialog(hDlg, TRUE);
			break;

		// Closed from sysbox
		case WM_CLOSE:

			EndDialog(hDlg, TRUE);
			break;

	}

	return FALSE;
}