#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 "loadOBJ.h"
#include "lazik.hpp"
#include "plane.hpp"
//#include <vector>
#include "GL/glm/glm.hpp"
#include "GL/glfw3.h"
#include <ctime>
#include "timeh.hpp"
#include "texture.hpp"
#include "shader.hpp"
#include "FPSCounter.cpp"
#include <thread>

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 storage
static LPCTSTR lpszAppName = "grafikaKBT";
static HINSTANCE hInstance;
GLFWwindow* window;

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


static GLsizei lastHeight;
static GLsizei lastWidth;

// Opis tekstury
BITMAPINFOHEADER    bitmapInfoHeader;    // nagłówek obrazu
unsigned char*      bitmapData;          // dane tekstury
unsigned int        texture[4];          // 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;
std::time_t lastTime = std::time(nullptr);
int monitormode = 1;
int monitormodecounter = 0;
std::time_t monitormodehelper;



//Zmienne do ruchu ##############################################^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FPSCounter fpsCounter;
static const int targetFPS = 144; // Celowany FPS
//Fps counter
void LimitFPS(int targetFPS) {
	static auto lastTime = std::chrono::high_resolution_clock::now();
	auto currentTime = std::chrono::high_resolution_clock::now();
	std::chrono::duration<double> elapsed = currentTime - lastTime;

	// Obliczamy czas na jedną klatkę
	double frameTime = 1.0 / targetFPS; // Czas na jedną klatkę w sekundach
	double timeToWait = frameTime - elapsed.count(); // Obliczamy czas do czekania

	if (timeToWait > 0.0) {
		// Jeśli czas do czekania jest większy niż 0, to śpimy przez tę wartość
		std::this_thread::sleep_for(std::chrono::duration<double>(timeToWait));
	}

	lastTime = currentTime; // Zaktualizuj czas dla następnej iteracji
}

bool Kolizja = false;
bool keyWPressed = false;
bool keySPressed = false;
bool keyAPressed = false;
bool keyDPressed = false;

float Foward = 0.0f;  // Pozycja łazika w przód/tył 
float Sides = 0.0f;    // Pozycja łazika w lewo/prawo
float Rotation = 0.0f; // Rotacja łazika (w stopniach)

float CameraHeight = 50.0f;  // Wysokość kamery
float MoveSpeed = 1.0f;      // Prędkość poruszania się


float velocity = 0.0f;       // Aktualna prędkość łazika
const float friction = 0.01f; // Współczynnik tarcia (μ)
const float maxSpeed = 3.0f; // Maksymalna prędkość łazika
const float acceleration = 0.2f;

float rotationVelocity = 0.0f; // Prędkość obrotu łazika

const float rotationAcceleration = 0.1f; // Przyspieszenie obrotu
const float rotationFriction = 0.1f; // Współczynnik tarcia obrotu
const float maxRotationSpeed = 3.0f; // Maksymalna prędkość obrotu

// Struktura do reprezentacji płotu
struct Plot {
	GLfloat xc;       // Środek płotu w osi X
	GLfloat yc;       // Środek płotu w osi Y (nieużywane w kolizji, ale może być pomocne)
	GLfloat zc;       // Środek płotu w osi Z
	GLfloat length;   // Długość płotu
	GLfloat gruboscY; // Grubość płotu
	bool mod_x;       // 0 - płot pionowy, 1 - płot poziomy
};

// Funkcja sprawdzająca kolizję z płotem
// Funkcja sprawdzająca kolizję z płotem, uwzględniając wymiary łazika
bool CheckFenceCollision(float roverXMin, float roverXMax, float roverZMin, float roverZMax, const Plot& plot) {
	if (plot.mod_x == 0) { // Płot pionowy (równoległy do osi Z)
		float xMin = plot.xc - plot.gruboscY / 2.0f;
		float xMax = plot.xc + plot.gruboscY / 2.0f;
		float zMin = plot.zc - plot.length / 2.0f;
		float zMax = plot.zc + plot.length / 2.0f;

		// Sprawdzenie, czy którykolwiek fragment łazika wchodzi w obszar płotu
		if (roverXMax >= xMin && roverXMin <= xMax && // Kolizja w osi X
			roverZMax >= zMin && roverZMin <= zMax) { // Kolizja w osi Z
			return true;
		}
	}
	else { // Płot poziomy (równoległy do osi X)
		float xMin = plot.xc - plot.length / 2.0f;
		float xMax = plot.xc + plot.length / 2.0f;
		float zMin = plot.zc - plot.gruboscY / 2.0f;
		float zMax = plot.zc + plot.gruboscY / 2.0f;

		// Sprawdzenie, czy którykolwiek fragment łazika wchodzi w obszar płotu
		if (roverXMax >= xMin && roverXMin <= xMax && // Kolizja w osi X
			roverZMax >= zMin && roverZMin <= zMax) { // Kolizja w osi Z
			return true;
		}
	}
	return false;
}

// Funkcja ogólna do sprawdzania kolizji ze wszystkimi płotami
bool CheckAllFencesCollision(float roverXMin, float roverXMax, float roverZMin, float roverZMax, const std::vector<Plot>& fences) {
	for (const auto& fence : fences) {
		if (CheckFenceCollision(roverXMin, roverXMax, roverZMin, roverZMax, fence)) {
			return true; // Kolizja wykryta z którymś płotem
		}
	}
	return false; // Brak kolizji
}

void UpdateRover(const std::vector<Plot>& fences) {
	// Przyspieszanie w przód
	if (keyWPressed) {
		velocity += acceleration;
		if (velocity > maxSpeed) velocity = maxSpeed;
	}
	// Przyspieszanie w tył
	else if (keySPressed) {
		velocity -= acceleration;
		if (velocity < -maxSpeed) velocity = -maxSpeed;
	}
	// Hamowanie (wytracanie prędkości z powodu tarcia)
	else {
		if (velocity > 0) {
			velocity -= friction;
			if (velocity < 0) velocity = 0;
		}
		else if (velocity < 0) {
			velocity += friction;
			if (velocity > 0) velocity = 0;
		}
	}

	// Obracanie
	if (keyAPressed) {
		rotationVelocity += rotationAcceleration;
		if (rotationVelocity > maxRotationSpeed) rotationVelocity = maxRotationSpeed;
	}
	else if (keyDPressed) {
		rotationVelocity -= rotationAcceleration;
		if (rotationVelocity < -maxRotationSpeed) rotationVelocity = -maxRotationSpeed;
	}
	else {
		// Hamowanie obrotu (wytracanie prędkości z powodu tarcia)
		if (rotationVelocity > 0) {
			rotationVelocity -= rotationFriction;
			if (rotationVelocity < 0) rotationVelocity = 0;
		}
		else if (rotationVelocity < 0) {
			rotationVelocity += rotationFriction;
			if (rotationVelocity > 0) rotationVelocity = 0;
		}
	}

	// Wyliczenie nowej pozycji na podstawie prędkości
	float radRotation = Rotation * GL_PI / 180.0f; // Przeliczamy rotację na radiany
	float newSides = Sides - velocity * cos(radRotation); // Nowa pozycja w osi X
	float newFoward = Foward - velocity * sin(radRotation); // Nowa pozycja w osi Z

	// Wymiary łazika (połówki w osi X i Z)
	const float roverHalfWidthX = 19.0f; // 38/2
	const float roverHalfLengthZ = 12.0f; // 24/2

	// Wyliczenie obszaru zajmowanego przez łazik
	float roverXMin = newSides - roverHalfWidthX;
	float roverXMax = newSides + roverHalfWidthX;
	float roverZMin = newFoward - roverHalfLengthZ;
	float roverZMax = newFoward + roverHalfLengthZ;

	// Sprawdzanie kolizji przed aktualizacją pozycji
	if (Kolizja == true) {
		if (CheckAllFencesCollision(roverZMin, roverZMax, roverXMin, roverXMax, fences)) {
			// Jeśli jest kolizja, zatrzymujemy łazik
			velocity = 0.0f;
			cout << "Kolizja podczas ruchu\n";
		}
		else {
			// Jeśli brak kolizji, aktualizujemy pozycję
			Sides = newSides;
			Foward = newFoward;
		}

		// Sprawdzanie kolizji podczas obrotu
		if (rotationVelocity != 0.0f) {
			// Wyliczamy nową rotację
			float newRotation = Rotation + rotationVelocity;
			float radNewRotation = newRotation * GL_PI / 180.0f;

			// Wyliczamy narożniki łazika po obrocie
			float offsetX = roverHalfWidthX * cos(radNewRotation) + roverHalfLengthZ * sin(radNewRotation);
			float offsetZ = roverHalfLengthZ * cos(radNewRotation) + roverHalfWidthX * sin(radNewRotation);

			// Wyznaczamy nowe granice łazika
			float rotatedXMin = Sides - offsetX;
			float rotatedXMax = Sides + offsetX;
			float rotatedZMin = Foward - offsetZ;
			float rotatedZMax = Foward + offsetZ;

			// Sprawdzamy kolizję dla granic po obrocie
			if (CheckAllFencesCollision(rotatedXMin, rotatedXMax, rotatedZMin, rotatedZMax, fences)) {
				rotationVelocity = 0.0f; // Zatrzymujemy obrót w przypadku kolizji
				cout << "Kolizja podczas obrotu\n";
			}
			else {
				// Aktualizujemy rotację tylko, jeśli nie ma kolizji
				Rotation = newRotation;
				if (Rotation >= 360.0f) Rotation -= 360.0f;
				if (Rotation < 0.0f) Rotation += 360.0f;
			}
		}
	}
	else {
		// Jeśli kolizje są wyłączone, aktualizujemy wszystko bez sprawdzania
		Sides = newSides;
		Foward = newFoward;
		Rotation += rotationVelocity;
		if (Rotation >= 360.0f) Rotation -= 360.0f;
		if (Rotation < 0.0f) Rotation += 360.0f;
	}
}


std::vector<Plot> fences = {
	{-550.0f, 3.0f,   50.0f, 1310.0f, 4.0f, 0}, // 0 - pionowo
	{  50.0f, 3.0f, -600.0f, 1200.0f, 4.0f, 1}, // 1 - poziomo
	{ 650.0f, 3.0f,   50.0f, 1310.0f, 4.0f, 0}, // 0 - pionowo
	{  50.0f, 3.0f,  695.0f, 1200.0f, 4.0f, 1}  // 1 - poziomo
};

// 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 clipping volume (left, right, bottom, top, near, far)
	if (w <= h) glOrtho(-nRange, nRange, -nRange * h / w, nRange * h / w, -20 * nRange, 20 * nRange);
	else glOrtho(-nRange * w / h, nRange * w / h, -nRange, nRange, -20 * nRange, 20 * 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);
}

void skrzynka(GLfloat k) {
	glColor3d(0.8, 0.7, 0.3);


	glEnable(GL_TEXTURE_2D); // Włącz teksturowanie

	glBindTexture(GL_TEXTURE_2D, texture[0]);
	glBegin(GL_QUADS);
		glNormal3d(0, 0, 1);
		glTexCoord2d(1.0, 1.0); glVertex3d( k,  k, k);
		glTexCoord2d(0.0, 1.0); glVertex3d(-k,  k, k);
		glTexCoord2d(0.0, 0.0); glVertex3d(-k, -k, k);
		glTexCoord2d(1.0, 0.0); glVertex3d( k, -k, k);
	glEnd();
	glBindTexture(GL_TEXTURE_2D, texture[1]);
	glBegin(GL_QUADS);
		glNormal3d(1, 0, 0);
		glTexCoord2d(1.0, 1.0); glVertex3d(k,  k,  k);
		glTexCoord2d(0.0, 1.0); glVertex3d(k, -k,  k);
		glTexCoord2d(0.0, 0.0); glVertex3d(k, -k, -k);
		glTexCoord2d(1.0, 0.0); glVertex3d(k,  k, -k);
	glEnd();

	glDisable(GL_TEXTURE_2D); // Wyłącz teksturowanie


	glBegin(GL_QUADS);
		glNormal3d(0, 0, -1);
		glVertex3d( k,  k, -k);
		glVertex3d( k, -k, -k);
		glVertex3d(-k, -k, -k);
		glVertex3d(-k,  k, -k);

		glNormal3d(-1, 0, 0);
		glVertex3d(-k,  k, -k);
		glVertex3d(-k, -k, -k);
		glVertex3d(-k, -k,  k);
		glVertex3d(-k,  k,  k);

		glNormal3d(0, 1, 0);
		glVertex3d( k, k,  k);
		glVertex3d( k, k, -k);
		glVertex3d(-k, k, -k);
		glVertex3d(-k, k,  k);

		glNormal3d(0, -1, 0);
		glVertex3d( k, -k,  k);
		glVertex3d(-k, -k,  k);
		glVertex3d(-k, -k, -k);
		glVertex3d( k, -k, -k);
	glEnd();
}

void platforma(GLfloat xc, GLfloat yc, GLfloat zc, GLfloat xlen, GLfloat zlen) {

	glColor3d(0.729, 0.91, 0.51); // jasnozielony, dla grass02.bmp (https://rgbcolorpicker.com/0-1)
	glEnable(GL_TEXTURE_2D);

	glBindTexture(GL_TEXTURE_2D, texture[1]);
	glBegin(GL_QUADS);
		glTexCoord2d(1.0, 1.0); glVertex3d(xc - xlen, yc, zc - zlen);
		glTexCoord2d(1.0, 0.0); glVertex3d(xc + xlen, yc, zc - zlen);
		glTexCoord2d(0.0, 0.0); glVertex3d(xc + xlen, yc, zc + zlen);
		glTexCoord2d(0.0, 1.0); glVertex3d(xc - xlen, yc, zc + zlen);
	glEnd();

	glDisable(GL_TEXTURE_2D);

}

void stodola(GLfloat xc, GLfloat yc, GLfloat zc, GLfloat krawedz) {

	glEnable(GL_TEXTURE_2D);

	// ściany stodoły z bruku (brickwall)
	glColor3d(0.612f, 0.573f, 0.478f); // ciemny szary popadający w brąz (https://rgbcolorpicker.com/0-1)
	glBindTexture(GL_TEXTURE_2D, texture[3]);
	glBegin(GL_QUADS);

		glTexCoord2d(1.0, 1.0); glVertex3d(xc - krawedz/2,           yc, zc - krawedz/2);
		glTexCoord2d(1.0, 0.0); glVertex3d(xc - krawedz/2, yc + krawedz, zc - krawedz/2);
		glTexCoord2d(0.0, 0.0); glVertex3d(xc + krawedz/2, yc + krawedz, zc - krawedz/2);
		glTexCoord2d(0.0, 1.0); glVertex3d(xc + krawedz/2,           yc, zc - krawedz/2);

		glTexCoord2d(1.0, 1.0); glVertex3d(xc - krawedz/2,           yc, zc - krawedz/2);
		glTexCoord2d(1.0, 0.0); glVertex3d(xc - krawedz/2, yc + krawedz, zc - krawedz/2);
		glTexCoord2d(0.0, 0.0); glVertex3d(xc - krawedz/2, yc + krawedz, zc + krawedz/2);
		glTexCoord2d(0.0, 1.0); glVertex3d(xc - krawedz/2,           yc, zc + krawedz/2);

		glTexCoord2d(1.0, 1.0); glVertex3d(xc - krawedz/2,           yc, zc + krawedz/2);
		glTexCoord2d(1.0, 0.0); glVertex3d(xc - krawedz/2, yc + krawedz, zc + krawedz/2);
		glTexCoord2d(0.0, 0.0); glVertex3d(xc + krawedz/2, yc + krawedz, zc + krawedz/2);
		glTexCoord2d(0.0, 1.0); glVertex3d(xc + krawedz/2,           yc, zc + krawedz/2);

	glEnd();

	// przód i tył dachu
	glColor3d(0.612f, 0.573f, 0.478f);
	glBindTexture(GL_TEXTURE_2D, texture[3]);
	glBegin(GL_TRIANGLES);

		glTexCoord2f(1.0, 1.0); glVertex3f(xc - krawedz/2,       yc + krawedz, zc - krawedz/2);
		glTexCoord2f(1.0, 0.0); glVertex3f(xc - krawedz/2, yc + 1.5 * krawedz,             zc);
		glTexCoord2f(0.0, 0.0); glVertex3f(xc - krawedz/2,       yc + krawedz, zc + krawedz/2);

		glTexCoord2f(1.0, 1.0); glVertex3f(xc + krawedz/2,       yc + krawedz, zc - krawedz/2);
		glTexCoord2f(1.0, 0.0); glVertex3f(xc + krawedz/2, yc + 1.5 * krawedz,             zc);
		glTexCoord2f(0.0, 0.0); glVertex3f(xc + krawedz/2,       yc + krawedz, zc + krawedz/2);

	glEnd();


	// dach stodoły (barnroof)
	glColor3d(0.639, 0.553, 0.322); // wyblakły brązowy
	glBindTexture(GL_TEXTURE_2D, texture[2]);
	glBegin(GL_QUADS);

		glTexCoord2d(1.0, 1.0); glVertex3d(xc - krawedz/2,       yc + krawedz, zc + krawedz/2);
		glTexCoord2d(1.0, 0.0); glVertex3d(xc - krawedz/2, yc + 1.5 * krawedz,             zc);
		glTexCoord2d(0.0, 0.0); glVertex3d(xc + krawedz/2, yc + 1.5 * krawedz,             zc);
		glTexCoord2d(0.0, 1.0); glVertex3d(xc + krawedz/2,       yc + krawedz, zc + krawedz/2);

		glTexCoord2d(1.0, 1.0); glVertex3d(xc - krawedz/2,       yc + krawedz, zc - krawedz/2);
		glTexCoord2d(1.0, 0.0); glVertex3d(xc - krawedz/2, yc + 1.5 * krawedz,             zc);
		glTexCoord2d(0.0, 0.0); glVertex3d(xc + krawedz/2, yc + 1.5 * krawedz,             zc);
		glTexCoord2d(0.0, 1.0); glVertex3d(xc + krawedz/2,       yc + krawedz, zc - krawedz/2);

	glEnd();

	glDisable(GL_TEXTURE_2D);

}

void plot(GLfloat xc, GLfloat yc, GLfloat zc, GLfloat length, GLfloat gruboscY, bool mod_x)
 {

	GLfloat grubosc = 1.0f;

	// timestampedCout("main.cpp (plot): otrzymano xc=" << xc << " yc=" << yc << " zc=" << zc << " length=" << length << " mod_x=" << mod_x);

	GLfloat gruboscX = grubosc +  mod_x * length;
	GLfloat gruboscZ = grubosc + !mod_x * length;

	// timestampedCout("main.cpp (plot): gruboscX=" << gruboscX << " gruboscY=" << gruboscY << " gruboscZ=" << gruboscZ);

	//   d-----------c   Y
	//  /           /|   ^
	// a-----------b |    \
	// | h         | g     C->X
	// |           |/      |
	// e-----------f       v
	//                     Z (płaszczyzna XZ, Y to trzeci wymiar)
	
	GLfloat a1_x = xc - gruboscX / 2,  a1_y = yc   + 1 + gruboscY,   a1_z = zc + gruboscZ / 2; // 3
	GLfloat b1_x = xc + gruboscX / 2,  b1_y = yc   + 1 + gruboscY,   b1_z = zc + gruboscZ / 2;
	GLfloat c1_x = xc + gruboscX / 2,  c1_y = yc   + 1 + gruboscY,   c1_z = zc - gruboscZ / 2;
	GLfloat d1_x = xc - gruboscX / 2,  d1_y = yc   + 1 + gruboscY,   d1_z = zc - gruboscZ / 2;
	GLfloat e1_x = xc - gruboscX / 2,  e1_y = yc   + 1           ,   e1_z = zc + gruboscZ / 2; // 1
	GLfloat f1_x = xc + gruboscX / 2,  f1_y = yc   + 1           ,   f1_z = zc + gruboscZ / 2;
	GLfloat g1_x = xc + gruboscX / 2,  g1_y = yc   + 1           ,   g1_z = zc - gruboscZ / 2;
	GLfloat h1_x = xc - gruboscX / 2,  h1_y = yc   + 1           ,   h1_z = zc - gruboscZ / 2;

	GLfloat a2_x = a1_x,               a2_y = a1_y - 1 - gruboscY,  a2_z = a1_z; //zc - 1; // -4
	GLfloat b2_x = b1_x,               b2_y = b1_y - 1 - gruboscY,  b2_z = b1_z; //zc - 1;
	GLfloat c2_x = c1_x,               c2_y = c1_y - 1 - gruboscY,  c2_z = c1_z; //zc - 1;
	GLfloat d2_x = d1_x,               d2_y = d1_y - 1 - gruboscY,  d2_z = d1_z; //zc - 1;
	GLfloat e2_x = e1_x,               e2_y = e1_y - 1 - gruboscY,  e2_z = e1_z; //zc - 2;
	GLfloat f2_x = f1_x,               f2_y = f1_y - 1 - gruboscY,  f2_z = f1_z; //zc - 2;
	GLfloat g2_x = g1_x,               g2_y = g1_y - 1 - gruboscY,  g2_z = g1_z; //zc - 2;
	GLfloat h2_x = h1_x,               h2_y = h1_y - 1 - gruboscY,  h2_z = h1_z; //zc - 2;

	glColor3d(0.71, 0.522, 0.067);

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, texture[0]);
	glBegin(GL_QUADS);

		// Góra (1_x, 1_y, 1_z):
		// ABCD
		glTexCoord2d(1.0, 1.0); glVertex3d(a1_x, a1_y, a1_z);
		glTexCoord2d(1.0, 0.0); glVertex3d(b1_x, b1_y, b1_z);
		glTexCoord2d(0.0, 0.0); glVertex3d(c1_x, c1_y, c1_z);
		glTexCoord2d(0.0, 1.0); glVertex3d(d1_x, d1_y, d1_z);

		// EFGH
		glTexCoord2d(1.0, 1.0); glVertex3d(e1_x, e1_y, e1_z);
		glTexCoord2d(1.0, 0.0); glVertex3d(f1_x, f1_y, f1_z);
		glTexCoord2d(0.0, 0.0); glVertex3d(g1_x, g1_y, g1_z);
		glTexCoord2d(0.0, 1.0); glVertex3d(h1_x, h1_y, h1_z);

		// BCGF
		glTexCoord2d(1.0, 1.0); glVertex3d(b1_x, b1_y, b1_z);
		glTexCoord2d(1.0, 0.0); glVertex3d(c1_x, c1_y, c1_z);
		glTexCoord2d(0.0, 0.0); glVertex3d(g1_x, g1_y, g1_z);
		glTexCoord2d(0.0, 1.0); glVertex3d(f1_x, f1_y, f1_z);

		// ADHE
		glTexCoord2d(1.0, 1.0); glVertex3d(a1_x, a1_y, a1_z);
		glTexCoord2d(1.0, 0.0); glVertex3d(b1_x, b1_y, b1_z);
		glTexCoord2d(0.0, 0.0); glVertex3d(c1_x, c1_y, c1_z);
		glTexCoord2d(0.0, 1.0); glVertex3d(d1_x, d1_y, d1_z);

		// ABFE
		glTexCoord2d(1.0, 1.0); glVertex3d(a1_x, a1_y, a1_z);
		glTexCoord2d(1.0, 0.0); glVertex3d(b1_x, b1_y, b1_z);
		glTexCoord2d(0.0, 0.0); glVertex3d(f1_x, f1_y, f1_z);
		glTexCoord2d(0.0, 1.0); glVertex3d(e1_x, e1_y, e1_z);

		// DCGH
		glTexCoord2d(1.0, 1.0); glVertex3d(d1_x, d1_y, d1_z);
		glTexCoord2d(1.0, 0.0); glVertex3d(c1_x, c1_y, c1_z);
		glTexCoord2d(0.0, 0.0); glVertex3d(g1_x, g1_y, g1_z);
		glTexCoord2d(0.0, 1.0); glVertex3d(h1_x, h1_y, h1_z);

		// Dół (2_x, 2_y, 2_z):
		// ABCD
		glTexCoord2d(1.0, 1.0); glVertex3d(a2_x, a2_y, a2_z);
		glTexCoord2d(1.0, 0.0); glVertex3d(b2_x, b2_y, b2_z);
		glTexCoord2d(0.0, 0.0); glVertex3d(c2_x, c2_y, c2_z);
		glTexCoord2d(0.0, 1.0); glVertex3d(d2_x, d2_y, d2_z);

		// EFGH
		glTexCoord2d(1.0, 1.0); glVertex3d(e2_x, e2_y, e2_z);
		glTexCoord2d(1.0, 0.0); glVertex3d(f2_x, f2_y, f2_z);
		glTexCoord2d(0.0, 0.0); glVertex3d(g2_x, g2_y, g2_z);
		glTexCoord2d(0.0, 1.0); glVertex3d(h2_x, h2_y, h2_z);

		// BCGF
		glTexCoord2d(1.0, 1.0); glVertex3d(b2_x, b2_y, b2_z);
		glTexCoord2d(1.0, 0.0); glVertex3d(c2_x, c2_y, c2_z);
		glTexCoord2d(0.0, 0.0); glVertex3d(g2_x, g2_y, g2_z);
		glTexCoord2d(0.0, 1.0); glVertex3d(f2_x, f2_y, f2_z);

		// ADHE
		glTexCoord2d(1.0, 1.0); glVertex3d(a2_x, a2_y, a2_z);
		glTexCoord2d(1.0, 0.0); glVertex3d(b2_x, b2_y, b2_z);
		glTexCoord2d(0.0, 0.0); glVertex3d(c2_x, c2_y, c2_z);
		glTexCoord2d(0.0, 1.0); glVertex3d(d2_x, d2_y, d2_z);

		// ABFE
		glTexCoord2d(1.0, 1.0); glVertex3d(a2_x, a2_y, a2_z);
		glTexCoord2d(1.0, 0.0); glVertex3d(b2_x, b2_y, b2_z);
		glTexCoord2d(0.0, 0.0); glVertex3d(f2_x, f2_y, f2_z);
		glTexCoord2d(0.0, 1.0); glVertex3d(e2_x, e2_y, e2_z);

		// DCGH
		glTexCoord2d(1.0, 1.0); glVertex3d(d2_x, d2_y, d2_z);
		glTexCoord2d(1.0, 0.0); glVertex3d(c2_x, c2_y, c2_z);
		glTexCoord2d(0.0, 0.0); glVertex3d(g2_x, g2_y, g2_z);
		glTexCoord2d(0.0, 1.0); glVertex3d(h2_x, h2_y, h2_z);

		// Pachołki

		//   d-----------c   Y
		//  /           /|   ^
		// a-----------b |    \
		// | h         | g     C->X
		// |           |/      |
		// e-----------f       v
		//                     Z (płaszczyzna XZ, Y to trzeci wymiar)


		GLfloat p_a_x, p_a_y, p_a_z;
		GLfloat p_b_x, p_b_y, p_b_z;
		GLfloat p_c_x, p_c_y, p_c_z;
		GLfloat p_d_x, p_d_y, p_d_z;
		GLfloat p_e_x, p_e_y, p_e_z;
		GLfloat p_f_x, p_f_y, p_f_z;
		GLfloat p_g_x, p_g_y, p_g_z;
		GLfloat p_h_x, p_h_y, p_h_z;

		if (!mod_x) {

			p_a_x = xc + gruboscX / 2,  p_a_y = yc + 1.5f * gruboscY,  p_a_z = zc; // +3 dla y
			p_b_x = xc - gruboscX / 2,  p_b_y = yc + 1.5f * gruboscY,  p_b_z = zc; // +3 dla y
			p_c_x = xc + gruboscX / 2,  p_c_y = yc + 1.5f * gruboscY,  p_c_z = zc; // +3 dla y
			p_d_x = xc - gruboscX / 2,  p_d_y = yc + 1.5f * gruboscY,  p_d_z = zc; // +3 dla y
			p_e_x = xc + gruboscX / 2,  p_e_y = yc - 6              ,  p_e_z = zc;
			p_f_x = xc - gruboscX / 2,  p_f_y = yc - 6              ,  p_f_z = zc;
			p_g_x = xc + gruboscX / 2,  p_g_y = yc - 6              ,  p_g_z = zc;
			p_h_x = xc - gruboscX / 2,  p_h_y = yc - 6              ,  p_h_z = zc;

		} else {

			p_a_x = xc,                 p_a_y = yc + 1.5f * gruboscY,  p_a_z = zc - gruboscZ / 2; // +3 dla y
			p_b_x = xc,                 p_b_y = yc + 1.5f * gruboscY,  p_b_z = zc - gruboscZ / 2; // +3 dla y
			p_c_x = xc,                 p_c_y = yc + 1.5f * gruboscY,  p_c_z = zc + gruboscZ / 2; // +3 dla y
			p_d_x = xc,                 p_d_y = yc + 1.5f * gruboscY,  p_d_z = zc + gruboscZ / 2; // +3 dla y
			p_e_x = xc,                 p_e_y = yc - 6              ,  p_e_z = zc - gruboscZ / 2;
			p_f_x = xc,                 p_f_y = yc - 6              ,  p_f_z = zc - gruboscZ / 2;
			p_g_x = xc,                 p_g_y = yc - 6              ,  p_g_z = zc + gruboscZ / 2;
			p_h_x = xc,                 p_h_y = yc - 6              ,  p_h_z = zc + gruboscZ / 2;

		}

		for (int i = 0; i < 2; i++) {

			//   d-----------c   Y
			//  /           /|   ^
			// a-----------b |    \
			// | h         | g     C->X
			// |           |/      |
			// e-----------f       v
			//                     Z (płaszczyzna XZ, Y to trzeci wymiar)

			// timestampedCout("main.cpp (plot): p_a_x=" << p_a_x << " p_b_x=" << p_b_x << " p_c_x=" << p_c_x << " p_d_x=" << p_d_x);

			// ABCD
			glTexCoord2d(1.0, 1.0); glVertex3d(p_a_x + (2*i-1) * (gruboscX/2 - 8) * mod_x, p_a_y, p_a_z + (2*i-1) * (gruboscZ/2 - 5) * !mod_x);
			glTexCoord2d(1.0, 0.0); glVertex3d(p_b_x + (2*i-1) * (gruboscX/2 - 5) * mod_x, p_b_y, p_b_z + (2*i-1) * (gruboscZ/2 - 5) * !mod_x);
			glTexCoord2d(0.0, 0.0); glVertex3d(p_c_x + (2*i-1) * (gruboscX/2 - 5) * mod_x, p_c_y, p_c_z + (2*i-1) * (gruboscZ/2 - 8) * !mod_x);
			glTexCoord2d(0.0, 1.0); glVertex3d(p_d_x + (2*i-1) * (gruboscX/2 - 8) * mod_x, p_d_y, p_d_z + (2*i-1) * (gruboscZ/2 - 8) * !mod_x);

			// EFGH
			glTexCoord2d(1.0, 1.0); glVertex3d(p_e_x + (2*i-1) * (gruboscX/2 - 8) * mod_x, p_e_y, p_e_z + (2*i-1) * (gruboscZ/2 - 5) * !mod_x);
			glTexCoord2d(1.0, 0.0); glVertex3d(p_f_x + (2*i-1) * (gruboscX/2 - 5) * mod_x, p_f_y, p_f_z + (2*i-1) * (gruboscZ/2 - 5) * !mod_x);
			glTexCoord2d(0.0, 0.0); glVertex3d(p_g_x + (2*i-1) * (gruboscX/2 - 5) * mod_x, p_g_y, p_g_z + (2*i-1) * (gruboscZ/2 - 8) * !mod_x);
			glTexCoord2d(0.0, 1.0); glVertex3d(p_h_x + (2*i-1) * (gruboscX/2 - 8) * mod_x, p_h_y, p_h_z + (2*i-1) * (gruboscZ/2 - 8) * !mod_x);

			// BCGF
			glTexCoord2d(1.0, 1.0); glVertex3d(p_b_x + (2*i-1) * (gruboscX/2 - 5) * mod_x, p_b_y, p_b_z + (2*i-1) * (gruboscZ/2 - 5) * !mod_x);
			glTexCoord2d(1.0, 0.0); glVertex3d(p_c_x + (2*i-1) * (gruboscX/2 - 5) * mod_x, p_c_y, p_c_z + (2*i-1) * (gruboscZ/2 - 8) * !mod_x);
			glTexCoord2d(0.0, 0.0); glVertex3d(p_g_x + (2*i-1) * (gruboscX/2 - 5) * mod_x, p_g_y, p_g_z + (2*i-1) * (gruboscZ/2 - 8) * !mod_x);
			glTexCoord2d(0.0, 1.0); glVertex3d(p_f_x + (2*i-1) * (gruboscX/2 - 5) * mod_x, p_f_y, p_f_z + (2*i-1) * (gruboscZ/2 - 5) * !mod_x);

			// ADHE
			glTexCoord2d(1.0, 1.0); glVertex3d(p_a_x + (2*i-1) * (gruboscX/2 - 8) * mod_x, p_a_y, p_a_z + (2*i-1) * (gruboscZ/2 - 5) * !mod_x);
			glTexCoord2d(1.0, 0.0); glVertex3d(p_d_x + (2*i-1) * (gruboscX/2 - 8) * mod_x, p_d_y, p_d_z + (2*i-1) * (gruboscZ/2 - 8) * !mod_x);
			glTexCoord2d(0.0, 0.0); glVertex3d(p_h_x + (2*i-1) * (gruboscX/2 - 8) * mod_x, p_h_y, p_h_z + (2*i-1) * (gruboscZ/2 - 8) * !mod_x);
			glTexCoord2d(0.0, 1.0); glVertex3d(p_e_x + (2*i-1) * (gruboscX/2 - 8) * mod_x, p_e_y, p_e_z + (2*i-1) * (gruboscZ/2 - 5) * !mod_x);

			// ABFE
			glTexCoord2d(1.0, 1.0); glVertex3d(p_a_x + (2*i-1) * (gruboscX/2 - 8) * mod_x, p_a_y, p_a_z + (2*i-1) * (gruboscZ/2 - 5) * !mod_x);
			glTexCoord2d(1.0, 0.0); glVertex3d(p_b_x + (2*i-1) * (gruboscX/2 - 5) * mod_x, p_b_y, p_b_z + (2*i-1) * (gruboscZ/2 - 5) * !mod_x);
			glTexCoord2d(0.0, 0.0); glVertex3d(p_f_x + (2*i-1) * (gruboscX/2 - 5) * mod_x, p_f_y, p_f_z + (2*i-1) * (gruboscZ/2 - 5) * !mod_x);
			glTexCoord2d(0.0, 1.0); glVertex3d(p_e_x + (2*i-1) * (gruboscX/2 - 8) * mod_x, p_e_y, p_e_z + (2*i-1) * (gruboscZ/2 - 5) * !mod_x);

			// DCGH
			glTexCoord2d(1.0, 1.0); glVertex3d(p_d_x + (2*i-1) * (gruboscX/2 - 8) * mod_x, p_d_y, p_d_z + (2*i-1) * (gruboscZ/2 - 8) * !mod_x);
			glTexCoord2d(1.0, 0.0); glVertex3d(p_c_x + (2*i-1) * (gruboscX/2 - 5) * mod_x, p_c_y, p_c_z + (2*i-1) * (gruboscZ/2 - 8) * !mod_x);
			glTexCoord2d(0.0, 0.0); glVertex3d(p_g_x + (2*i-1) * (gruboscX/2 - 5) * mod_x, p_g_y, p_g_z + (2*i-1) * (gruboscZ/2 - 8) * !mod_x);
			glTexCoord2d(0.0, 1.0); glVertex3d(p_h_x + (2*i-1) * (gruboscX/2 - 8) * mod_x, p_h_y, p_h_z + (2*i-1) * (gruboscZ/2 - 8) * !mod_x);

		}


	glEnd();

	glDisable(GL_TEXTURE_2D);

}

//GLuint programID, VertexArrayID, MatrixID;
lazik user(10.0f, 0.0f, 0.0f, "res/models/lazik4.obj"); // obiekty eksportujemy z Forward Axis Z, Up Axis Y.
plane mapa( 0.0f, 0.0f, 0.0f, "res/models/mapka3_nofence_noplatform.obj");

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[]     = {50.0f, -100.0f,  50.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ść
	glDepthFunc(GL_LESS);

	// 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) {
		timestampedCout("Failed to initialize GLEW");
		return;
	}

	timestampedCout("Zainicjalizowano GLEW.");

	// glfw3 jest w teorii niepotrzebny, ale może się przydać
	// do przepisania kodu na podobny do tego stąd:
	// https://github.com/opengl-tutorials/ogl/blob/master/tutorial07_model_loading/tutorial07.cpp
	timestampedCout("Inicjalizowanie GLFW3...");
	if (!glfwInit()) {
		timestampedCout("Failed to initialize GLFW");
	}
	timestampedCout("Zainicjalizowano GLFW3.");

	// glGenVertexArrays(1, &VertexArrayID);
	// glBindVertexArray(VertexArrayID);

	// Załaduj shadery

	// programID = LoadShaders("res/shaders/TransformVertexShader.vertexshader", "res/shaders/TextureFragmentShader.fragmentshader");
	// MatrixID  = glGetUniformLocation(programID, "MVP");
	// user.passProgramID(programID);

	// Załaduj model z pliku .obj

	timestampedCout("Ladowanie modelu lazika...");
	user.loadModel();
	timestampedCout("Ladowanie modelu mapki...");
	mapa.loadModel();

	glfwSwapInterval(1);

	//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);

	// 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_AND_BACK, GL_LINE);
			break;
		default:
			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}

	

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

	// Use our shader
	//timestampedCout("programID wynosi " << programID);
	//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);
	//glLoadIdentity(); // Zresetowanie macierzy widoku
	// 
	//############################################################################################
	//Sterowanie 
	//############################################################################################
	gluLookAt(
		Foward - 100.0f * sin((Rotation + 180.0f) * GL_PI / 180.0f), // Pozycja kamery wokół łazika (w poziomie)
		CameraHeight,                                             // Wysokość kamery
		Sides - 100.0f * cos((Rotation + 180.0f) * GL_PI / 180.0f),  // Kamera wzdłuż osi X i Z
		Foward, 0.0f, Sides,                                      // Punkt, na który patrzy kamera (łazik)
		0.0f, 1.0f, 0.0f                                         // Wektor "góry"
	);
	
	// Rysowanie mapy (nie porusza się)
	glPushMatrix();
	glColor3f(0.0, 1.0, 0.0);              // Zielony kolor dla mapy
	platforma(50.0f, 0.0f, 45.0f, 600.0f, 650.0f);
	//mapa.draw();
	glPopMatrix();

	// Rysowanie łazika (porusza się i obraca)
	glPushMatrix();

	glTranslatef(Foward, 0.0f, Sides);     // Translacja łazika na jego pozycję
	glRotatef(Rotation, 0.0f, 1.0f, 0.0f); // Obrót łazika wokół własnej osi
	glColor3f(1.0, 0.0, 0.0);              // Czerwony kolor dla łazika
	fpsCounter.update();
	user.draw();
	UpdateRover(fences);
	glPopMatrix();

	//skrzynka(50);
	plot(-550.0f, 3.0f, 50.0f, 1310.0f, 4.0f, 0); // 0 - pionowo
	plot(50.0f, 3.0f, -600.0f, 1200.0f, 4.0f, 1); // 1 - poziomo
	plot(650.0f, 3.0f, 50.0f, 1310.0f, 4.0f, 0);
	plot(50.0f, 3.0f, 695.0f, 1200.0f, 4.0f, 1);



	// stodola(2.0f, 0.0f, -7.5f, 20.0f);
	stodola(10.0f, 0.0f, 2.0f, 40.0f);

	// Zamiana buforów (double buffering)
	//glColor3f(1.0, 0.0, 0.0);
	//user.draw();
	//glColor3f(0.0, 1.0, 0.0);
	//mapa.draw();
	//glColor3f(0.0, 0.0, 0.0);
	

	// Zamiana buforów (double buffering)


	// Swap buffers
	//glfwSwapBuffers(window);
	//glfwPollEvents();

	//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;
}
void CreateConsole() {
	// Tworzenie nowej konsoli
	if (AllocConsole()) {
		// Przekierowanie standardowych strumieni do konsoli
		FILE* conin;
		FILE* conout;
		FILE* conerr;
		freopen_s(&conin, "conin$", "r", stdin);
		freopen_s(&conout, "conout$", "w", stdout);
		freopen_s(&conerr, "conout$", "w", stderr);
	}
	else {
		MessageBox(NULL, "Nie udalo sie utworzyc konsoli.", "Blad", MB_OK | MB_ICONERROR);
	}
}
	int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
		CreateConsole();
		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,
			800, 800,
			NULL,
			NULL,
			hInstance,
			NULL);

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


		const WORD ID_TIMER = 1;
		SetTimer(hWnd, ID_TIMER, 100, NULL);
		// 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

	float radRotation = Rotation * GL_PI / 180.0f;
	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(3, &texture[0]); // tworzy obiekt tekstury			

			// ładuje pierwszy obraz tekstury (płotki):
			bitmapData = LoadBitmapFile((char*)"res/img/woodenTextureHighExposure.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);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

			// 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 (ziemia):
			bitmapData = LoadBitmapFile((char*)"res/img/grass02.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_MIRRORED_REPEAT);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

			// 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 trzeci obraz tekstury (dach stodoły):
			bitmapData = LoadBitmapFile((char*)"res/img/barnroof.bmp", &bitmapInfoHeader);
			glBindTexture(GL_TEXTURE_2D, texture[2]); // 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 czwarty obraz tekstury (kamienista ściana):
			bitmapData = LoadBitmapFile((char*)"res/img/brickwall.bmp", &bitmapInfoHeader);
			glBindTexture(GL_TEXTURE_2D, texture[3]); // 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:
			user.unload();

			//glDeleteProgram(programID);
			//glDeleteVertexArrays(1, &VertexArrayID);

			// 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
			if (!monitormode) ValidateRect(hWnd, NULL);
			else InvalidateRect(hWnd, NULL, FALSE);
			//break;

			// Limit FPS
			// Uaktualniaj FPS
			LimitFPS(targetFPS);  // Ogranicz FPS

			

		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;
		case WM_KEYUP:

			switch (wParam) {

				case 'W':
					keyWPressed = false;
					break;

				case 'S':
					keySPressed = false;
					break;

				case 'A':
					keyAPressed = false;
					break;

				case 'D':
					keyDPressed = false;
					break;

				// Obsługa innych klawiszy

			}
			break;

		case WM_KEYDOWN:

			switch (wParam) {

				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;

				case 'Q':
					zRot += 5.0f;
					break;

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

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

				case ' ': // 32
					polygonmode = !polygonmode;
					if (polygonmode) timestampedCout("Uwaga! Tryb wireframe jest niewydajny i powinien sluzyc tylko do debugowania!");
					break;
				case 'K':
					Kolizja = !Kolizja;
					break;

				case 'W':
					keyWPressed = true;
					break;

				case 'S':
					keySPressed = true;
					break;

				case 'A':
					keyAPressed = true;
					break;

				case 'D':
					keyDPressed = true;
					break;

				// case 114: // F3
				// 	monitormode = !monitormode;
				// 	if (monitormode) {
				// 		monitormodehelper = std::time(nullptr) - 1;
				// 		timestampedCout("Wlaczono tryb monitorowania wydajnosci.");
				// 	}
				// 	if (!monitormode) timestampedCout("Wylaczono tryb monitorowania wydajnosci.");
				// 	break;

				default:
					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;

		case WM_TIMER:

			RenderScene();
			SwapBuffers(hDC);
			ValidateRect(hWnd, NULL);
			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;
}
