void checkError()
{
	long a = glGetError();
	if( a == GL_INVALID_ENUM );// quitMessage("checkError", "HAAA, bad value passed to opengl");
	else if( a != 0)  quitMessage("checkError", "HAAA, error in opengl :(");
}

#ifndef PLATFORM_LINUX
void setupPixelFormat(HDC hDC);
void setupPalette(HDC hDC);
LRESULT CALLBACK mainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void swapGL();

void initGLWindow()
{
/* initialize OpenGL rendering */
	hDC = GetDC(hwndMain);
	if(hDC==NULL) quitMessage("initGLWindow","can't get device context");
	setupPixelFormat(hDC);
	setupPalette(hDC);
	hGLRC = wglCreateContext(hDC);
	wglMakeCurrent(hDC, hGLRC);
/*
	if(!Windowed())
	{
		if (ChangeDisplaySettings(&devmodes[choosen_mode], CDS_TEST) == 
		DISP_CHANGE_SUCCESSFUL)
		{
			ChangeDisplaySettings(&devmodes[choosen_mode], 0);
		} else PostMessage(hwndMain, WM_PAINT, 0, 0);
		SetWindowPos( hwndMain, HWND_TOP, -borders.left, -borders.top, 0, 0, SWP_NOSIZE );
	}
*/
}


void releaseGL()
{
 /* finish OpenGL rendering */
 if (hGLRC) {
   wglMakeCurrent(NULL, NULL);
   wglDeleteContext(hGLRC);
 }
 if (hPalette) {
   DeleteObject(hPalette);
 }
 ReleaseDC(hwndMain, hDC);
}        

void swapGL()
{
	if((appFlags & AF_FSCREEN))
	{
		if (ChangeDisplaySettings(&devmodes[choosen_mode], CDS_TEST) == DISP_CHANGE_SUCCESSFUL)
		{
			ChangeDisplaySettings(&devmodes[choosen_mode], 0);
		} else PostMessage(hwndMain, WM_PAINT, 0, 0);
//		SetWindowPos( hwndMain, HWND_TOP, -borders.left, -borders.top, 0, 0, SWP_NOSIZE );
		SetWindowPos( hwndMain, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE );
	}
	else
	{
		long cx,cy;
		cx = windowSize.x;
		cy = windowSize.y;

		ChangeDisplaySettings(NULL, 0);
		POINT min, max;
		RECT r;
		r.left = 0;
		r.right = cx;
		r.bottom= cy;
		r.top = 0;
		min.x=0;
		min.y=0;
		max.x=cx;
		max.y=cy;
		WINDOWPLACEMENT  plac;
		plac.length = sizeof(WINDOWPLACEMENT);
		plac.flags = WPF_SETMINPOSITION;
		plac.showCmd = SW_SHOW;
		plac.rcNormalPosition = r;
		plac.ptMinPosition = min;
		plac.ptMaxPosition = max;
		SetWindowPlacement(hwndMain,&plac);
	}
}

// ------------- INITS -------------------

void
setupPixelFormat(HDC hDC)
{
    PIXELFORMATDESCRIPTOR pfd = {
        sizeof(PIXELFORMATDESCRIPTOR),  /* size */
        1,                              /* version */
        PFD_SUPPORT_OPENGL |
        PFD_DRAW_TO_WINDOW |
        PFD_DOUBLEBUFFER,               /* support double-buffering */
        PFD_TYPE_RGBA,                  /* color type */
        16,                             /* prefered color depth */
        0, 0, 0, 0, 0, 0,               /* color bits (ignored) */
        0,                              /* no alpha buffer */
        0,                              /* alpha bits (ignored) */
        0,                              /* no accumulation buffer */
        0, 0, 0, 0,                     /* accum bits (ignored) */
        32,                             /* depth buffer */
        0,                              /* no stencil buffer */
        0,                              /* no auxiliary buffers */
        PFD_MAIN_PLANE,                 /* main layer */
        0,                              /* reserved */
        0, 0, 0,                        /* no layer, visible, damage masks */
    };
    int pixelFormat;

    pixelFormat = ChoosePixelFormat(hDC, &pfd);
    if (pixelFormat == 0) {
        MessageBox(WindowFromDC(hDC), "ChoosePixelFormat failed.", "Error",
                MB_ICONERROR | MB_OK);
        exit(1);
    }

    if (SetPixelFormat(hDC, pixelFormat, &pfd) != TRUE) {
        MessageBox(WindowFromDC(hDC), "SetPixelFormat failed.", "Error",
                MB_ICONERROR | MB_OK);
        exit(1);
    }
}

void
setupPalette(HDC hDC)
{
    int pixelFormat = GetPixelFormat(hDC);
    PIXELFORMATDESCRIPTOR pfd;
    LOGPALETTE* pPal;
    int paletteSize;

    DescribePixelFormat(hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);

    if (pfd.dwFlags & PFD_NEED_PALETTE) {
        paletteSize = 1 << pfd.cColorBits;
    } else {
//        MessageBox(0, "HAAAAA!", "ERROR", MB_OK); 
		return;
    }

    pPal = (LOGPALETTE*)
        malloc(sizeof(LOGPALETTE) + paletteSize * sizeof(PALETTEENTRY));
    pPal->palVersion = 0x300;
    pPal->palNumEntries = paletteSize;

    /* build a simple RGB color palette */
    {
        int redMask = (1 << pfd.cRedBits) - 1;
        int greenMask = (1 << pfd.cGreenBits) - 1;
        int blueMask = (1 << pfd.cBlueBits) - 1;
        int i;

        for (i=0; i<paletteSize; ++i) {
            pPal->palPalEntry[i].peRed =
                    (((i >> pfd.cRedShift) & redMask) * 255) / redMask;
            pPal->palPalEntry[i].peGreen =
                    (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask;
            pPal->palPalEntry[i].peBlue =
                    (((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask;
            pPal->palPalEntry[i].peFlags = 0;
        }
    }

    hPalette = CreatePalette(pPal);
    free(pPal);

    if (hPalette) {
        SelectPalette(hDC, hPalette, FALSE);
        RealizePalette(hDC);
    }
	else MessageBox(0, "ERROR", "Can't Init Palette", MB_OK);
}

LRESULT CALLBACK
FakeWndProc(HWND hWfake, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
		case WM_CREATE:

		/* initialize OpenGL rendering */
	        hDC = GetDC(hWfake);
			if(hDC==NULL) quitMessage("initGLWindow","can't get device context");
			setupPixelFormat(hDC);
			setupPalette(hDC);
			hGLRC = wglCreateContext(hDC);
			wglMakeCurrent(hDC, hGLRC);
			int tSize;
			glGetIntegerv(GL_MAX_TEXTURE_SIZE, &tSize);
			sprintf(glInfos, "Implemented by: %s\n Hardware: %s\n Version: %s\nMax texture size: %ix%i",glGetString(GL_VENDOR), glGetString(GL_RENDERER),glGetString(GL_VERSION),tSize,tSize);
			return 0;

        case WM_DESTROY:
            // Clean up and close the app
			if (hGLRC) 
			{
				wglMakeCurrent(NULL, NULL);
				wglDeleteContext(hGLRC);
			}
			if (hPalette) DeleteObject(hPalette);
			ReleaseDC(hWfake, hDC);
            return 0;
    }
    return DefWindowProc(hWfake, msg, wParam, lParam);
}

void GetGLDisplayModes()
{
	HWND hWfake;

	WNDCLASS	    wc;
	int             cx,cy;
    wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = FakeWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = NULL;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (struct HBRUSH__ *)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "fake";
    RegisterClass(&wc);


	// Calculate the proper size for the window given a client of res_xXres_y
	// first init borders window once :)
	borders.left = GetSystemMetrics(SM_CXSIZEFRAME);
	borders.right = GetSystemMetrics(SM_CXSIZEFRAME);
	borders.top = GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYMENU);
	borders.bottom = GetSystemMetrics(SM_CYSIZEFRAME);
    cx = res_x+borders.left+borders.right;
    cy = res_y+borders.top+borders.bottom;
    // Create and Show the Main Window

	hWfake = CreateWindow(
		"fake", "hop",
		WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
		0, 0, cx, cy,
		NULL, NULL, NULL, NULL);
	if(hWfake==NULL) quitMessage("Ho!", "Can't create window kipu!");
//	---------- Get all display modes... ----------
				DEVMODE devmode;
				num_modes = 0;
				while (EnumDisplaySettings(NULL, num_modes, &devmode))
				num_modes++;
				devmodes = (DEVMODE*)malloc(sizeof(DEVMODE)*num_modes);
				num_modes = 0;
				while (EnumDisplaySettings(NULL, num_modes, &devmodes[num_modes]))
				num_modes++;
//  ----------------------------------------------
	DestroyWindow(hWfake);

//	fill default choosen_mode value
	choosen_mode = -1;
	for(long u=0;u<num_modes;u++)
	{
		if( (devmodes[u].dmBitsPerPel == 16) &&
			(devmodes[u].dmPelsWidth == 640) &&
			(devmodes[u].dmPelsHeight == 480) )
		choosen_mode = u;
	}
	if( choosen_mode == -1) quitMessage("GetGLDisplayModes", "Damn! Can't find default video mode: 640x480x16   :(");
}
#endif

void initProjFor2D()
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-1,1,-1,1,1,2);	// load a orthonormal matrix projection for 2D in opengl
	glMatrixMode(GL_MODELVIEW);
}

void initProjFor3D()
{
	glMatrixMode(GL_PROJECTION);	// reload 3D projection matrix
	glLoadIdentity();
	glFrustum(	GLPMatrice[0],GLPMatrice[1],
				GLPMatrice[2],GLPMatrice[3],
				1.0F, 100000);
	glMatrixMode(GL_MODELVIEW);
}

void
initViewGL(cam *camera, Color *c)
{
	float ratioxy = (float)res_x/(float)res_y;
	GLPMatrice[0] = - (GL_PROJ_FACTOR*((ratioxy)/camera->LWfocal));
	GLPMatrice[1] =	GL_PROJ_FACTOR*((ratioxy)/camera->LWfocal);
	GLPMatrice[2] = - GL_PROJ_FACTOR*((1)/camera->LWfocal);
	GLPMatrice[3] = GL_PROJ_FACTOR*((1)/camera->LWfocal);
#ifndef PLATFORM_LINUX
	glViewport(0,0,res_x,res_y);
#endif
	glInitNames();
	initProjFor3D();
/*
//	gluPerspective(camera->LWfocal, ratioxy, 1.0F, 100000);
	float *Pmatrix = new float[16];
	float znear = 1;
	float zfar = 100000;
	float focal = camera->LWfocal*100000;
	Pmatrix[0] =  (float)atan( focal / 2 ) / ratioxy;
	Pmatrix[1] = 0;	Pmatrix[2] = 0; Pmatrix[3] = 0;
	Pmatrix[4] = 0; Pmatrix[5] = (float)atan( focal / 2 );Pmatrix[6] = 0;Pmatrix[7] = 0;
	Pmatrix[8] = 0; Pmatrix[9] = 0; Pmatrix[10]= (znear+zfar)/(znear-zfar);Pmatrix[11] = -1;
	Pmatrix[12]= 0; Pmatrix[13]= 0; Pmatrix[14]= 2*zfar*znear/(znear-zfar);Pmatrix[15] = 0;
	glLoadMatrixf( Pmatrix );
*/
	
    glEnable(GL_DEPTH_TEST);
	glEnable(GL_SMOOTH);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_FRONT);
	glDisable(GL_LIGHTING);

	glPixelTransferi(GL_MAP_COLOR, GL_FALSE);
    glPixelTransferi(GL_RED_SCALE, 1);
    glPixelTransferi(GL_RED_BIAS, 0);
    glPixelTransferi(GL_GREEN_SCALE, 1);
    glPixelTransferi(GL_GREEN_BIAS, 0);
    glPixelTransferi(GL_BLUE_SCALE, 1);
    glPixelTransferi(GL_BLUE_BIAS, 0);
    glPixelTransferi(GL_ALPHA_SCALE, 1);
    glPixelTransferi(GL_ALPHA_BIAS, 0);

	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	glClearColor( c->fRedValue, c->fGreenValue, c->fBlueValue, 0.0f);
}
class sprite;
void initTexturesGL()
{
	long i,j,NallT = 0, maxS = -1, format;

	for(j=0;j<scnMan.nbs;j++) 
	{
		if(scene[j].nsurface > maxS) maxS = scene[j].nsurface;
		NallT += scene[j].nsurface;
	}
	// Load Texture
	GLtexture = new GLuint[NallT+256+1+ 2*nsprite+scnMan.nbs];	// total number of GL textures
	long *initied = new long[maxS];
	nGLtexture = 0;

	for(j=0;j<scnMan.nbs;j++)
	{
		textu *tt;
		for(i=0;i<scene[j].nsurface;i++) initied[i] = 0;
		for(i=0;i<scene[j].nsurface;i++)
		{
			long mix = scene[j].surface[i].bitmap;
			if( (mix != -1) && !initied[i] )
			{
				initied[i] = 1;
				tt = &scene[mix>>16].texture[mix & 0x0ffff];
				if( (tt== NULL) || (tt->data==NULL)) quitMessage("ERROR","initializing GL textures");
				scene[j].surface[i].bitmap = nGLtexture;

				glGenTextures(1, &GLtexture[nGLtexture]);
				glBindTexture(GL_TEXTURE_2D, GLtexture[nGLtexture]);
				glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
				glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
				if( tt->flag == 1 ) format = 4;
				else format = 3;
				glTexImage2D(GL_TEXTURE_2D, 0, format, tt->sizex, tt->sizey, 0, GL_RGBA, GL_UNSIGNED_BYTE, tt->data);
//				glTexImage2D(GL_TEXTURE_2D, 0, format, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
				nGLtexture++;
			}
		}

		tt = &scene[j].tPart;
		// init particules sprite
		glGenTextures(1, &GLtexture[nGLtexture]);
		glBindTexture(GL_TEXTURE_2D, GLtexture[nGLtexture]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, tt->sizex, tt->sizey, 0, GL_RGBA, GL_UNSIGNED_BYTE, tt->data);
		scene[j].glPart = nGLtexture;
		nGLtexture++;

		if( scene[j].bgImage != NULL )
		{
			tt = scene[j].bgImage;
			glGenTextures(1, &GLtexture[nGLtexture]);
			glBindTexture(GL_TEXTURE_2D, GLtexture[nGLtexture]);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
			glTexImage2D(GL_TEXTURE_2D, 0, 3, tt->sizex, tt->sizey, 0, GL_RGBA, GL_UNSIGNED_BYTE, tt->data);
			scene[j].bgImageGL = nGLtexture;
			nGLtexture++;
		}
	}
	delete initied;

//	2D effects video
	videoGL = (void*) new long[256*256];
	for(long a=0;a<(256*256);a++) ((long*)videoGL)[a] = 0; // init video
	glGenTextures(1, &GLtexture[nGLtexture]);
	glBindTexture(GL_TEXTURE_2D, GLtexture[nGLtexture]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, videoGL);
	videoGLnum = nGLtexture;
	nGLtexture++;


//	now init sprites;
	for(j=0;j<nsprite;j++)
	{
		for(long k=0;k<(long)sprt[j].nplan;k++)
		{
			glGenTextures(1, &GLtexture[nGLtexture]);
			glBindTexture(GL_TEXTURE_2D, GLtexture[nGLtexture]);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
			glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, sprt[j].datas[k]);
			sprt[j].glT[k] = (ULONG)nGLtexture;
			nGLtexture++;
		}
	}
	if( debug() )
	{
		for(int e=0;e<256;e++)
		{
			glGenTextures(1, &GLtexture[nGLtexture]);
			glBindTexture(GL_TEXTURE_2D, GLtexture[nGLtexture]);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
			// 4 components because text font need alpha
			glTexImage2D(GL_TEXTURE_2D, 0, 4, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, debugPrint->opengl_font_textures[e]);
			debugPrint->opengl_text_numbers[e] = nGLtexture;
			nGLtexture++;
		}
	}

}

void deleteTexturesGL()
{
	for(long i=0;i<nGLtexture;i++)	glDeleteTextures(1,&GLtexture[i]);
}