#include "OknoGL.h"
#include "resource.h"

/*void ShowMessage(LPCSTR Text)
{
	MessageBox(NULL,Text,"",NULL);
}*/

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
	POINT polozenieOkna={100,100};
	POINT rozmiarOkna={800,600};	
	if (!okno.Init(hInstance,polozenieOkna,rozmiarOkna))
	{
		MessageBox(NULL,"Inicjacja okna nie powiodła się","Aplikacja OpenGL",MB_OK | MB_ICONERROR);
		return EXIT_FAILURE;
	}
	else return okno.Run();
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
	return okno.WndProc(hWnd,message,wParam,lParam);
}

bool COkno::Init(HINSTANCE uchwytAplikacji,POINT polozenieOkna,POINT rozmiarOkna)
{
	char nazwaOkna[]="Aplikacja OpenGL";

	WNDCLASSEX wc;
	wc.cbSize=sizeof(wc);
	wc.style= CS_HREDRAW | CS_VREDRAW | CS_OWNDC; //styl okna
	wc.lpfnWndProc= (WNDPROC)::WndProc; //procedura okna
	wc.cbClsExtra= 0; //dodatkowe bajty zarezerwowane za klasą okna
	wc.cbWndExtra= 0; //dodatkowe bajty zarezerwowane za instancją okna
	wc.hInstance= uchwytAplikacji; //instancja aplikacji
	wc.hIcon= LoadIcon(uchwytAplikacji,MAKEINTRESOURCE(IDI_GLICON)); //uchwyt ikony
	wc.hIconSm= LoadIcon(uchwytAplikacji,MAKEINTRESOURCE(IDI_GLICON)); //uchwyt ikony
	wc.hCursor= LoadCursor(NULL, IDC_ARROW); //uchwyt kursora
	wc.hbrBackground= NULL; //uchwyt pędzla tła
	wc.lpszMenuName= NULL; //nazwa menu
	wc.lpszClassName= nazwaOkna; //nazwa klasy okna	

	//Rejestracja klasy okna
	if(RegisterClassEx(&wc)==0) return false;

	bool trybPelnoekranowy=false;

	//ustawienia dla okna
	DWORD stylOkna=WS_OVERLAPPEDWINDOW;

	if (trybPelnoekranowy)
	{
		//ustawienia dla trybu pelnoekranowego
		polozenieOkna.x=0;
		polozenieOkna.y=0;
		//rozmiarOkna.x=1024;
		//rozmiarOkna.y=768;
		RECT rozmiarEkranu;
		GetWindowRect(GetDesktopWindow(),&rozmiarEkranu);
		rozmiarOkna.x=rozmiarEkranu.right-rozmiarEkranu.left;
		rozmiarOkna.y=rozmiarEkranu.bottom-rozmiarEkranu.top;
		stylOkna=WS_POPUP;
		if (!ZmianaRozdzielczosci(rozmiarOkna.x,rozmiarOkna.y)) return false;
	}
			
	//Tworzenie okna
	uchwytOkna = CreateWindow(
		nazwaOkna, //nazwa klasy okna
		nazwaOkna, //nazwa okna
		stylOkna,
		polozenieOkna.x,polozenieOkna.y, //położenie okna (x,y)
		rozmiarOkna.x,rozmiarOkna.y, //rozmiar okna (szerokość, wysokość)
		NULL, //uchwyt okna nadrzędnego (parent)
		NULL, //uchwyt menu
		uchwytAplikacji, //uchwyt instancji aplikacji
		NULL //parametr komunikatu informujacego o utworzeniu okna
	);

	if(uchwytOkna==NULL) return false;

	//Pokazanie i aktualizacja okna
	ShowWindow(uchwytOkna,SW_SHOW);
	UpdateWindow(uchwytOkna);

	return true;
};

WPARAM COkno::Run()
{
	//Petla glowna - obsluga komunikatow
	MSG msg;
	while(GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT COkno::WndProc(HWND hWnd, UINT message, WPARAM wParam,LPARAM lParam)
{
	switch (message)
	{
		case WM_DESTROY: //Zamykanie okna => konczenie aplikacji
			//ChangeDisplaySettings(NULL,0); //przywrocenie domyslnej rozdzielczosci ekranu						
			PostQuitMessage(0);
			break;
		case WM_SIZE: //Zmiana rozmiaru okna
			RECT rect;
			GetClientRect(hWnd, &rect);
			szerokoscObszaruUzytkownika = rect.right - rect.left;
			wysokoscObszaruUzytkownika = rect.bottom - rect.top;			
			break;
		default: //Automatyczne przetwarzanie komunikatow
			return (DefWindowProc(hWnd, message, wParam, lParam));
	}

	return 0L;
}

bool COkno::ZmianaRozdzielczosci(long szerokosc,long wysokosc,long glebiaKolorow) const
{
	DEVMODE dmScreenSettings;	//struktura trybu wyswietlania
	memset(&dmScreenSettings,0,sizeof(dmScreenSettings));	//czyszczenie pamieci
	dmScreenSettings.dmSize=sizeof(dmScreenSettings);		//rozmiar struktury
	dmScreenSettings.dmPelsWidth	= szerokosc;			//nowa szerokosc ekranu
	dmScreenSettings.dmPelsHeight	= wysokosc;				//nowa wysokosc ekranu
	dmScreenSettings.dmBitsPerPel	= glebiaKolorow;		//ilosc bitow opisujacych kolor piksela
	dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
	return ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)==DISP_CHANGE_SUCCESSFUL;
}


/* ------------------------------------------- */

LRESULT COknoGL::WndProc(HWND hWnd, UINT message, WPARAM wParam,LPARAM lParam)
{
	long wynik=COkno::WndProc(hWnd,message,wParam,lParam);

	switch (message)
	{
		case WM_CREATE: //Utworzenie okna
			//zmienna uchwytOkna nie jest jeszcze zainicjowana
			InitWGL(hWnd);
			UstawienieSceny();
			//Wersja OpenGL na pasku tytulu				
			{
				char tytul[1024]="OpenGL ";
				strcat_s(tytul,(char*)glGetString(GL_VERSION));
				//SetWindowText(hWnd,tytul);
			}
			break;
		case WM_DESTROY: //Zamkniecie okna
			UsunWGL();
			break;
		case WM_SIZE: //Zmiana rozmiaru okna
			UstawienieSceny();
			break;
		case WM_PAINT: //Okno wymaga odrysowania
			RysujScene();
			ValidateRect(hWnd,NULL);
			break;
	}

	return wynik;
}

// ----------------- WGL -----------------

bool COknoGL::InitWGL(HWND uchwytOkna)
{
	uchwytDC = ::GetDC(uchwytOkna);
	if (!UstalFormatPikseli(uchwytDC)) return false; //Utworzenie kontekstu renderowania i uczynienie go aktywnym
	uchwytRC = wglCreateContext(uchwytDC);
	if (uchwytRC==NULL) return false;
	if (!wglMakeCurrent(uchwytDC, uchwytRC)) return false;			
	return true;
}

void COknoGL::UsunWGL()
{
	wglMakeCurrent(NULL,NULL); 
	wglDeleteContext(uchwytRC); 
	::ReleaseDC(uchwytOkna,uchwytDC);
}

bool COknoGL::UstalFormatPikseli(HDC uchwytDC) const
{ 
	PIXELFORMATDESCRIPTOR opisFormatuPikseli; 
	ZeroMemory(&opisFormatuPikseli,sizeof(opisFormatuPikseli)); 
	opisFormatuPikseli.nVersion=1; 
	opisFormatuPikseli.dwFlags=PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; //w oknie, podwojne buforowanie 
	opisFormatuPikseli.iPixelType=PFD_TYPE_RGBA; //typ koloru RGB 
	opisFormatuPikseli.cColorBits=32; //jakosc kolorów 4 bajty
	opisFormatuPikseli.cDepthBits=16; //glebokosc bufora Z (z-buffer) 
	opisFormatuPikseli.iLayerType=PFD_MAIN_PLANE; 
	int formatPikseli=ChoosePixelFormat(uchwytDC,&opisFormatuPikseli); 
	if (formatPikseli==0) return false; 
	if (!SetPixelFormat(uchwytDC,formatPikseli,&opisFormatuPikseli)) return false; 
	return true;
}

// ----------------- OpenGL -----------------

void COknoGL::UstawienieSceny() 
{ 
	glViewport(0,0,szerokoscObszaruUzytkownika,wysokoscObszaruUzytkownika); //okno OpenGL = wnetrze formy (domyslnie) 
	
	//ustawienie punktu projekcji 
	glMatrixMode(GL_PROJECTION); //przełączenie na macierz projekcji 
	glLoadIdentity(); 
	//left,right,bottom,top,znear,zfar (clipping) 
	float wsp=wysokoscObszaruUzytkownika/(float)szerokoscObszaruUzytkownika;
	glFrustum(-0.1, 0.1, wsp*-0.1, wsp*0.1, 0.3, 100.0); //mnozenie macierzy rzutowania przez macierz perspektywy - ustalanie frustum 
	glMatrixMode(GL_MODELVIEW); //powrót do macierzy widoku modelu 
	glEnable(GL_DEPTH_TEST); //z-buffer aktywny = ukrywanie niewidocznych powierzchni 
}

void COknoGL::RysujScene() 
{ 
	const float x0=1.0; 
	const float y0=1.0; 
	const float z0=1.0; 
	
	//Przygotowanie bufora 
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //czysci bufory 
	glLoadIdentity(); //macierz model-widok = macierz jednostkowa 
	glTranslatef(0.0, 0.0, -10.0); //odsuniecie calosci o 10 
	
	//Rysowanie trojkata 
	glBegin(GL_TRIANGLES); 
	//ustalanie trzech wierzcholkow trojkata (werteksow (x,y,z)) 
	//(0,0,z) jest mniej wiecej w srodku ekranu 
	glVertex3f(-x0, -y0, z0); //dolny lewy 
	glVertex3f(x0, -y0, z0); //dolny prawy 
	glVertex3f(0, y0, z0); //gorny 
	//koniec rysowania figury 
	glEnd(); 
	
	//Z bufora na ekran 
	SwapBuffers(uchwytDC); 
}
