#include "MojeOkno.h"
#include "Varia.h" //JednostkowyWektorNormalny3fv
#include "Modele.h" //Rysuj...
//#include "glext.h"
#include "resource.h"

#include "Testy.cpp"
#include "Wektor.h"
#include <time.h>

CMojeOknoGL::CMojeOknoGL()
{	
	#ifdef _DEBUG
	int odpowiedz=MessageBox(this->uchwytOkna,"Czy przeprowadzić testy klas wektora, macierzy i kwaternionu","Test klas",MB_YESNO | MB_ICONQUESTION);
	if(odpowiedz==IDYES)
		try
		{
			unsigned int ziarno=(unsigned int)time(NULL);
			bool wynik=true;
			for(int i=0;i<10000;++i) if(!(TestWektora<double>(ziarno++))) wynik=false;
			MessageBox(this->uchwytOkna,wynik?"OK":"Błąd!","Test klasy Wektor",MB_OK);
	
			wynik=true;
			for(int i=0;i<10000;++i) if(!(TestMacierzy<double>(ziarno++))) wynik=false;
			MessageBox(this->uchwytOkna,wynik?"OK":"Błąd!","Test klasy Macierz",MB_OK);
	
			wynik=true;
			for(int i=0;i<10000;++i) if(!(TestKwaternionu<double>(ziarno++))) wynik=false;
			MessageBox(this->uchwytOkna,wynik?"OK":"Błąd!","Test klasy Kwaternion",MB_OK);
		}
		catch(std::exception& exc)
		{
			MessageBox(this->uchwytOkna,exc.what(),"Błąd podczas testów",MB_OK);
		}
	#endif	
}

bool TestKonwersjiKwaternionuDoMacierzy(unsigned int ziarno)
{	
	bool wynik=true;
	const double tolerancjaBledu=1E-5;
	double zakres=100.0;

	srand(ziarno);
	int nieUzywany=rand();
	double wspolrzedne[4];
	for(int i=0;i<4;++i) wspolrzedne[i]=zakres*rand()/RAND_MAX;

	//dane
	double katObrotu=wspolrzedne[0];
	Wektor osObrotu(wspolrzedne[1],wspolrzedne[2],wspolrzedne[3]);
	osObrotu.Normuj();

	//bufory
	double macierz1[16],macierz2[16],roznica[16];

	//OpenGL
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
	glRotated(-360.0*katObrotu/(2*M_PI),osObrotu.X,osObrotu.Y,osObrotu.Z);
	glGetDoublev(GL_MODELVIEW_MATRIX,macierz1);
	glPopMatrix();

	//Kwaternion
	Kwaternion k=Kwaternion::TworzZKataIOsiObrotu(katObrotu,osObrotu);
	Macierz m=k.MacierzObrotu();
	m.KopiaElementow4x4(macierz2);

	//porownanie
	for(int i=0;i<16;++i) 
	{
		roznica[i]=macierz1[i]-macierz2[i];
		if(fabs(roznica[i])>tolerancjaBledu) wynik=false;
	}

	return wynik;
}

void CMojeOknoGL::RysujAktorow()
{	
	/*
	#ifdef _DEBUG
	bool wynik=true;
	unsigned int ziarno=(unsigned int)time(NULL);
	for(int i=0;i<10000;++i) if(!(TestKonwersjiKwaternionuDoMacierzy(ziarno++))) wynik=false;
	MessageBox(this->uchwytOkna,wynik?"OK":"Błąd!","Test konwersji kwaternionu do macierzy",MB_OK);
	#endif
	*/

	const float x0=1.0; 
	const float y0=1.0; 
	const float z0=1.0; 		

	float poziomMorza=-1.75f*y0;

	//przygotowanie szablonu
	glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE); //wylaczenie rysowania do bufora pikseli
	glDisable(GL_DEPTH_TEST); //wyłaczenie testu głebokości
	glEnable(GL_STENCIL_TEST); //właczenie testu szablonu	
	glStencilFunc(GL_ALWAYS,1,~0); //funkcja testu szablonu: tworzenie szablonu
	glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
	glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); //ukrywanie w szablonie dolnej powierzchni lustra	
	Modele::RysujOcean(x0,poziomMorza,z0); //rysowanie powierzchni lustra do szablonu
	glDisable(GL_CULL_FACE); //wylaczenie ukrywania powierzchni
	glDisable(GL_STENCIL_TEST); //wylaczenie testu szablonu
	glEnable(GL_DEPTH_TEST); //przywrocenie bufora glebii	
	glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE); //przywrocenie rysowania do bufora pikseli
	
	//przygotowanie bufora szablonowego do korzystania z szablonu
	glStencilFunc(GL_EQUAL,1,~0); //funkcja testu szablonu: uzycie szablonu
	glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP); //szablon pozostawiany bez zmian

	//plaszczyzny obcinajace
	double rownanie_plaszczyzny_obcinajacej_odbicie[4]={0.0,-1,0.0,poziomMorza};
	glClipPlane(GL_CLIP_PLANE0,rownanie_plaszczyzny_obcinajacej_odbicie);
	double rownanie_plaszczyzny_obcinajacej_latarnie[4]={0.0,1,0.0,-poziomMorza};
	glClipPlane(GL_CLIP_PLANE1,rownanie_plaszczyzny_obcinajacej_latarnie);

	//odbicie		
	glEnable(GL_STENCIL_TEST);
	glEnable(GL_CLIP_PLANE0);
	glPushMatrix();
	glTranslatef(-0.25f*x0,poziomMorza-2*x0,0);
	glScalef(1,-1,1);	
	Modele::RysujLatarnie(2.5f*x0);
	glPopMatrix();
	glDisable(GL_CLIP_PLANE0);
	glDisable(GL_STENCIL_TEST);
	
	//ocean
	Modele::RysujOcean(x0,poziomMorza,z0);

	//latarnia	
	glEnable(GL_CLIP_PLANE1);
	glPushMatrix();
	glTranslatef(-0.25f*x0,poziomMorza+2*x0,0);
	Modele::RysujLatarnie(2.5f*x0);
	glPopMatrix();
	glDisable(GL_CLIP_PLANE1);	
}

#pragma region Aktorzy
//Wiekszosc aktorow jest w klasie Modele (Modele.h/Modele.cpp)

void CMojeOknoGL::RysujTrojkat(float x0,float y0,float z0) const
{
	//Rysowanie trojkata 
	glBegin(GL_TRIANGLES); 
	//ustalanie trzech wierzcholkow trojkata (werteksow (x,y,z)) 
	//(0,0,z) jest mniej wiecej w srodku ekranu 
	glColor3f(1.0,0.0,0.5);
	glVertex3f(-x0, -y0, 0); //dolny lewy 
	glColor3f(0.5,0.0,1.0);
	glVertex3f(x0, -y0, 0); //dolny prawy 
	glColor3f(0.0,1.0,0.5);
	glVertex3f(0, y0, 0); //gorny 
	//koniec rysowania figury 
	glEnd();
}
#pragma endregion


//lista wyswietlania
unsigned int CMojeOknoGL::TworzListyWyswietlania(float dx,float dy,float dz,bool koloruj) const
{
	GLuint listyWyswietlania=glGenLists(2);

	glNewList(listyWyswietlania,GL_COMPILE); //EXECUTE - bo mamy dowolne parametry potencjalnie nieznane w momencie kompilacji
	Modele::RysujSzescian(dx,koloruj,true,indeksTekstury);
	glEndList();

	glNewList(listyWyswietlania+1,GL_COMPILE); //EXECUTE - bo mamy dowolne parametry potencjalnie nieznane w momencie kompilacji
	Modele::RysujNiszczycielaGwiazd(3*dx,3*dy,3*dz);
	glEndList();

	return listyWyswietlania;
}

#pragma region Animacja
//timer animacji
#include <mmsystem.h>

LRESULT CMojeOknoGL::WndProc(HWND hWnd, UINT message, WPARAM wParam,LPARAM lParam)
{	
	const bool ANIMACJA=false;
	const int IDT_TIMER_ANIMACJI=1;

	long wynik=COknoGL::WndProc(hWnd,message,wParam,lParam);

	if(!ANIMACJA) 
	{
		przesuniecie=10;
		return wynik;
	}	

	switch (message)
	{
		case WM_CREATE: //pole uchwyt okna nie jest zainicjowane w WM_CREATE -> uzywac hWnd
			if (SetTimer(hWnd,IDT_TIMER_ANIMACJI,50,NULL)==0)
				MessageBox(hWnd,"Nie udało się ustawić timera","",MB_OK | MB_ICONERROR);
			przesuniecie=0;
			poprzedniCzas=GetTickCount();
			PlaySound("M:\\MUZYKA\\Imperial March.wav",NULL,SND_FILENAME | SND_ASYNC);						
			break;
		case WM_TIMER:
			switch(wParam)
			{
				case IDT_TIMER_ANIMACJI:					
					long biezacyCzas=GetTickCount();
					przesuniecie+=0.0005f*(biezacyCzas-poprzedniCzas);
					poprzedniCzas=biezacyCzas;
					RysujScene();
					break;
			}
			wynik=0;
			break;
		case WM_DESTROY:
			KillTimer(uchwytOkna,IDT_TIMER_ANIMACJI);
			break;
	}
	
	return wynik;
}
#pragma endregion

#pragma region Zrodla swiatla
//zrodla swiatla
void CMojeOknoGL::ZrodlaSwiatla()
{
	natezenie_swiatla_tla=0.5f;
	MlecznaZarowka(0.5f);
	ZoltaIZielonaMleczneZarowki();
	Reflektor(1.0f,0.0f);

	/*
	//mgla
	glEnable(GL_FOG);
	const float biel[4]={1.0,1.0,1.0,1.0};
	glFogfv(GL_FOG_COLOR,biel);
	glFogf(GL_FOG_START,0.0);
	glFogf(GL_FOG_END,100.0);
	glFogf(GL_FOG_MODE,GL_LINEAR);
	*/	
}


void CMojeOknoGL::MlecznaZarowka(float jasnosc)
{	
	const float kolor[4]={jasnosc,jasnosc,jasnosc,1.0f};
	const float pozycja[4]={5.0f,0.0f,5.0f,1.0f};	
	glLightfv(GL_LIGHT1,GL_POSITION,pozycja);
	glLightfv(GL_LIGHT1,GL_DIFFUSE,kolor);
	glEnable(GL_LIGHT1);
}

void CMojeOknoGL::ZoltaIZielonaMleczneZarowki()
{
	//zolta mleczna zarowka
	const float kolor_zolta[4]={1.0f,1.0f,0.0f,1.0f};
	const float pozycja_zolta[4]={-2.0f,0.0f,1.0f,1.0f};
	glLightfv(GL_LIGHT2,GL_POSITION,pozycja_zolta);
	glLightfv(GL_LIGHT2,GL_DIFFUSE,kolor_zolta);
	//glEnable(GL_LIGHT2);

	//zielona mleczna zarowka
	const float kolor_zielony[4]={0.0f,1.0f,0.0f,1.0f};
	const float pozycja_zielony[4]={2.0f,0.0f,1.0f,1.0f};
	glLightfv(GL_LIGHT3,GL_POSITION,pozycja_zielony);
	glLightfv(GL_LIGHT3,GL_DIFFUSE,kolor_zielony);
	//glEnable(GL_LIGHT3);
}

void CMojeOknoGL::Reflektor(float jasnoscRozblysk,float jasnoscRozproszone)
{
	const float kolor_rozproszone[4]={jasnoscRozproszone,jasnoscRozproszone,jasnoscRozproszone,1.0f};
	const float kolor_rozblysk[4]={jasnoscRozblysk,jasnoscRozblysk,jasnoscRozblysk,1.0};
	const float pozycja[4]={-10.0f,-10.0f,10.0f,1.0f};	
	const float kierunek[4]={1.0,1.0,-1.0,1.0};   
	const float szerokosc_wiazki=30.0f; //w stopniach
	const float wygaszanie=1.0f;
   
	glLightfv(GL_LIGHT4,GL_POSITION,pozycja);
	glLightfv(GL_LIGHT4,GL_DIFFUSE,kolor_rozproszone);

	glLightfv(GL_LIGHT4,GL_SPECULAR,kolor_rozblysk);
	glLightfv(GL_LIGHT4,GL_SPOT_DIRECTION,kierunek);
	glLightf(GL_LIGHT4,GL_SPOT_CUTOFF,szerokosc_wiazki);
	glLightf(GL_LIGHT4,GL_SPOT_EXPONENT,wygaszanie);
	glEnable(GL_LIGHT4);
}
#pragma endregion


#pragma region Teksturowanie
//aktywna moze byc tylko jedna z ponizszych definicji
#define TeksturaDlaSfery //Ziemia
//#define TeksturaDlaNiszczycielaGwiazd
//#define TekstuaCzytanaZPliku //Nefryt
//#define TeksturyGenerowaneZeWzoru

#ifdef TeksturaDlaSfery
void CMojeOknoGL::PrzygotujTekstury()
{
	const int ILOSC_TEKSTUR=1;
	glGenTextures(ILOSC_TEKSTUR,indeksTekstury); //generowanie identyfikatora do tekstur (uchwytow, name to mylna nazwa)

	char* nazwyTekstur[ILOSC_TEKSTUR]={"ziemia.bmp"};

	int teksturaSzer;
	int teksturaWys;
	for(int it=0;it<ILOSC_TEKSTUR;it++)
	{
		//wczytanie tekstury
		unsigned long* tekstura=WczytajTeksture(uchwytOkna,nazwyTekstur[it],teksturaSzer,teksturaWys,false,255);

		glBindTexture(GL_TEXTURE_2D,indeksTekstury[it]); //kolejne polecenia beda dotyczyc tej tekstury
	
		//tworzenie tekstury z tablicy bitow
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		//glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);

		gluBuild2DMipmaps(GL_TEXTURE_2D,3,teksturaSzer,teksturaWys,GL_RGBA,GL_UNSIGNED_BYTE,tekstura);			
		//glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,teksturaSzer,teksturaWys, 0, GL_RGBA, GL_UNSIGNED_BYTE, tekstura);
	
		delete[] tekstura; //oryginalne dane sa usuwane	
	}
}
#endif
#ifdef TeksturaDlaNiszczycielaGwiazd
void CMojeOknoGL::PrzygotujTekstury()
{
	const int ILOSC_TEKSTUR=2;
	glGenTextures(ILOSC_TEKSTUR,indeksTekstury); //generowanie identyfikatora do tekstur (uchwytow, name to mylna nazwa)

	char* nazwyTekstur[ILOSC_TEKSTUR]={"TeksturaNadbudowki.bmp","TeksturaKadluba.bmp"};

	int teksturaSzer;
	int teksturaWys;
	for(int it=0;it<ILOSC_TEKSTUR;it++)
	{
		//wczytanie tekstury
		unsigned long* tekstura=WczytajTeksture(uchwytOkna,nazwyTekstur[it],teksturaSzer,teksturaWys,false,255);
		//unsigned long* tekstura=WczytajTeksture(uchwytOkna,MAKEINTRESOURCE(IDB_DEATHSTAR),teksturaSzer,teksturaWys,true,255);

		glBindTexture(GL_TEXTURE_2D,indeksTekstury[it]); //kolejne polecenia beda dotyczyc tej tekstury
	
		//tworzenie tekstury z tablicy bitow
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
		glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);

		gluBuild2DMipmaps(GL_TEXTURE_2D,4,teksturaSzer,teksturaWys,GL_RGBA,GL_UNSIGNED_BYTE,tekstura);	
	
		delete[] tekstura; //oryginalne dane sa usuwane	
	}
}
#endif

#ifdef TekstuaCzytanaZPliku
void CMojeOknoGL::PrzygotujTekstury()
{
	glGenTextures(1,indeksTekstury); //generowanie identyfikatora do tekstur (uchwytow, name to mylna nazwa)

	int teksturaSzer;
	int teksturaWys;
	//wczytanie tekstury
	//unsigned long* tekstura=WczytajTeksture(uchwytOkna,"Nefryt.bmp",teksturaSzer,teksturaWys,false,255);	
	unsigned long* tekstura=WczytajTeksture(uchwytOkna,MAKEINTRESOURCE(IDB_NEFRYT),teksturaSzer,teksturaWys,true,255);
	//unsigned long* tekstura=WczytajTeksture(uchwytOkna,"tekstura_24bit.bmp",teksturaSzer,teksturaWys,128);
	//unsigned long* tekstura=WczytajTeksture(uchwytOkna,"tekstura_1bit.bmp",teksturaSzer,teksturaWys,128);
	//unsigned long* tekstura=WczytajTeksture(uchwytOkna,"tekstura_8bit.bmp",teksturaSzer,teksturaWys,128);
	//unsigned long* tekstura=WczytajTeksture(uchwytOkna,"tekstura_4bit.bmp",teksturaSzer,teksturaWys,128);

	glBindTexture(GL_TEXTURE_2D,indeksTekstury[0]); //kolejne polecenia beda dotyczyc tej tekstury
	
	//tworzenie tekstury z tablicy bitow
	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_MAG_FILTER,GL_LINEAR);
	//glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

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

	glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);

	gluBuild2DMipmaps(GL_TEXTURE_2D,4,teksturaSzer,teksturaWys,GL_RGBA,GL_UNSIGNED_BYTE,tekstura);
	
	delete[] tekstura; //oryginalne dane sa usuwane	
}
#endif

#ifdef TeksturyGenerowaneZeWzoru
//Trzy tekstury dla szescianu
void CMojeOknoGL::PrzygotujTekstury()
{
	const int ILOSC_TEKSTUR=3;
	glGenTextures(ILOSC_TEKSTUR,indeksTekstury); //generowanie identyfikatora do tekstur (uchwytow, name to mylna nazwa)

	int teksturaSzer=4;
	int teksturaWys=4;

	unsigned long* tekstura[ILOSC_TEKSTUR];
	for(int it=0;it<ILOSC_TEKSTUR;it++)
	{
		//przygotowanie trzech tablicy pikseli
		tekstura[it]=new unsigned long[teksturaSzer*teksturaWys]; //32-bity na piksel
		for(int ih=0;ih<teksturaWys;ih++)
			for(int iw=0;iw<teksturaSzer;iw++)
			{
				bool ciemniejsza=((iw+ih)%2)?true:false;
				unsigned char R=0,G=0,B=0,A=128;
				switch(it)
				{
					case 0:
						R=0; 
						G=(ciemniejsza)?0:255;
						B=255;
						break;
					case 1:
						R=255; 
						G=0;
						B=(ciemniejsza)?0:255;;
						break;
					case 2:
						R=(ciemniejsza)?0:255; 
						G=255;
						B=0;
						break;
				}
				tekstura[it][iw+(ih*teksturaSzer)]=(A << 24) + (B << 16) + (G << 8) + (R);
			}	
	}
	
	for(int it=0;it<ILOSC_TEKSTUR;it++)
	{
		glBindTexture(GL_TEXTURE_2D,indeksTekstury[it]); //kolejne polecenia beda dotyczyc tej tekstury

		//tworzenie tekstury z tablicy bitow
		//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_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		//float kolorPodkladu[4]={0.0f,1.0f,0.0f,1.0f};
		//glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,kolorPodkladu);
		glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);

		glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,teksturaSzer,teksturaWys, 0, GL_RGBA, GL_UNSIGNED_BYTE, tekstura[it]);
		//gluBuild2DMipmaps(GL_TEXTURE_2D,4,teksturaSzer,teksturaWys,GL_RGBA, GL_UNSIGNED_BYTE, tekstura[it]);

		delete[] tekstura[it]; //oryginalne dane sa usuwane
	}
}
#endif
#pragma endregion

