// PBuffer only for win32

#include "quakedef.h"
#include "gl_BufferPBO.h"

static	boolean	PBO_Already_Init = false;

pbuffer_t		PBuffer;
AttrListint_t	intAttrs;
AttrListfloat_t	floatAttrs;
AttrListint_t	props;

/*
=========== 
	INT
=========== 
*/
void Clear_AttrListint (AttrListint_t input)
{
	int i;
	input.numAttrs = 0;
	for ( i = 0; i < 2*MAX_ATTRIBS; i++ )
		input.attrs [i] = 0;
}

void add ( int attr, int value, AttrListint_t input)
{
	input.attrs [2*input.numAttrs    ] = attr;
	input.attrs [2*input.numAttrs + 1] = value;
	input.numAttrs++;
}

/*
=========== 
   FLOAT
=========== 
*/
void Clear_AttrListfloat (AttrListfloat_t input)
{
	int i;
	input.numAttrs = 0;
	for ( i = 0; i < 2*MAX_ATTRIBS; i++ )
		input.attrs [i] = 0;
}

void addfloat ( int attr, int value, AttrListfloat_t input)
{
	input.attrs [2*input.numAttrs    ] = attr;
	input.attrs [2*input.numAttrs + 1] = value;
	input.numAttrs++;
}


void PBO_Set  (int theWidth, int theHeight, int theMode, boolean theShareObjects)
{
	PBuffer.width        = theWidth;
	PBuffer.height       = theHeight;
	PBuffer.mode         = theMode;
	PBuffer.shareObjects = theShareObjects;
	PBuffer.hBuffer      = NULL;
	PBuffer.hdc          = NULL;
	PBuffer.hGlRc        = NULL;
	PBuffer.saveHdc      = NULL;
	PBuffer.saveHglrc    = NULL;
}

boolean	PBO_Create (void)
{
	HDC			hCurDc = wglGetCurrentDC();
	HGLRC		hCurRc = wglGetCurrentContext();
	int			format;
	int			pixelFormats [MAX_PFORMATS];
	unsigned	numFormats = 0;

	// check for extension support & initialization
	if ( wglChoosePixelFormatARB == NULL || wglCreatePbufferARB  == NULL || wglGetPbufferDCARB == NULL || wglQueryPbufferARB   == NULL  || wglReleasePbufferDCARB  == NULL || wglDestroyPbufferARB == NULL )
		return false;

	if ((PBuffer.mode & modeTexture1D) || (PBuffer.mode & modeTexture2D) || (PBuffer.mode & modeTextureCubeMap))
		if (wglBindTexImageARB == NULL || wglReleaseTexImageARB == NULL || wglSetPbufferAttribARB == NULL)
			return false;
	
	// query for pixel format
	Clear_AttrListint	(intAttrs);
	Clear_AttrListfloat	(floatAttrs);

	add ( WGL_SUPPORT_OPENGL_ARB,  GL_TRUE, intAttrs );
	add ( WGL_DRAW_TO_PBUFFER_ARB, GL_TRUE, intAttrs );
	add ( WGL_PIXEL_TYPE_ARB,      WGL_TYPE_RGBA_ARB, intAttrs );
	add ( WGL_RED_BITS_ARB,        8, intAttrs );
	add ( WGL_GREEN_BITS_ARB,      8, intAttrs );
	add ( WGL_BLUE_BITS_ARB,       8, intAttrs );
	add ( WGL_DOUBLE_BUFFER_ARB, (PBuffer.mode & modeDouble ? GL_TRUE : GL_FALSE ), intAttrs );

	if (PBuffer.mode & modeAlpha  )	add ( WGL_ALPHA_BITS_ARB,	 8,	intAttrs );
	if (PBuffer.mode & modeDepth  )	add ( WGL_DEPTH_BITS_ARB,	24, intAttrs );
	if (PBuffer.mode & modeStencil)	add ( WGL_STENCIL_BITS_ARB,	 8,	intAttrs );
	if (PBuffer.mode & modeAccum  )	add ( WGL_ACCUM_BITS_ARB,	32, intAttrs );
	
	if ((PBuffer.mode & modeTexture1D) || (PBuffer.mode & modeTexture2D) || (PBuffer.mode & modeTextureCubeMap))
		if (PBuffer.mode & modeAlpha) add (WGL_BIND_TO_TEXTURE_RGBA_ARB, GL_TRUE, intAttrs);
		else add (WGL_BIND_TO_TEXTURE_RGB_ARB, GL_TRUE, intAttrs);

	if ( !wglChoosePixelFormatARB ( hCurDc, intAttrs.attrs, floatAttrs.attrs, MAX_PFORMATS, pixelFormats, &numFormats ) )
		return false;
	if ( numFormats < 1 )
		return false;
	format = pixelFormats [0];

	if ( (PBuffer.mode & modeTexture1D) || (PBuffer.mode & modeTexture2D) )
		if ( PBuffer.mode & modeAlpha )	add ( WGL_TEXTURE_FORMAT_ARB,  WGL_TEXTURE_RGBA_ARB, props );
		else add ( WGL_TEXTURE_FORMAT_ARB,  WGL_TEXTURE_RGB_ARB, props );

	if ( PBuffer.mode & modeTexture1D )	add ( WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_1D_ARB, props );
	else if ( PBuffer.mode & modeTexture2D ) add ( WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_2D_ARB, props );
	else if ( PBuffer.mode & modeTextureCubeMap ) add ( WGL_TEXTURE_TARGET_ARB, WGL_TEXTURE_CUBE_MAP_ARB, props );

	if ( PBuffer.mode & modeTextureMipmap )	add ( WGL_MIPMAP_TEXTURE_ARB, GL_TRUE, props );

	PBuffer.hBuffer = wglCreatePbufferARB ( hCurDc, format, PBuffer.width, PBuffer.height, props.attrs );

	if ( PBuffer.hBuffer == NULL )
		return false;

	PBuffer.hdc = wglGetPbufferDCARB ( PBuffer.hBuffer );

	if ( PBuffer.hdc == NULL )
		return false;

	PBuffer.hGlRc = wglCreateContext ( PBuffer.hdc );

	if ( PBuffer.shareObjects )	wglShareLists ( hCurRc, PBuffer.hGlRc );

	// get real size
	wglQueryPbufferARB ( PBuffer.hBuffer, WGL_PBUFFER_WIDTH_ARB,  &PBuffer.width );
	wglQueryPbufferARB ( PBuffer.hBuffer, WGL_PBUFFER_HEIGHT_ARB, &PBuffer.height );

	return true;
}

void PBO_Error(void)
{
	LPVOID msgBuf;
    DWORD err = GetLastError();

    switch ( err )
    {
	    case ERROR_INVALID_PIXEL_FORMAT:
    	    Con_Printf ( "Error:  ERROR_INVALID_PIXEL_FORMAT\n" );
	        break;
	    case ERROR_NO_SYSTEM_RESOURCES:
    	    Con_Printf ( "Error:  ERROR_NO_SYSTEM_RESOURCES\n ");
	        break;
	    case ERROR_INVALID_DATA:
    	    Con_Printf ( "Error:  ERROR_INVALID_DATA\n" );
	        break;
	    case ERROR_INVALID_WINDOW_HANDLE:
    	    Con_Printf ( "Error:  ERROR_INVALID_WINDOW_HANDLE\n" );
	        break;
	    case ERROR_RESOURCE_TYPE_NOT_FOUND:
    	    Con_Printf ( "Error:  ERROR_RESOURCE_TYPE_NOT_FOUND\n" );
	        break;
	    case ERROR_SUCCESS:	// no error
	        break;
		default:
    	    FormatMessage ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err,	MAKELANGID ( LANG_NEUTRAL, SUBLANG_DEFAULT ), (LPTSTR) &msgBuf, 0, NULL );
	        Con_Printf  ( "Error %d: %s\n", err, msgBuf );
    	    LocalFree( msgBuf );
        	break;
    }
	SetLastError ( 0 );
}

void	PBO_Init (void)
{
	if (PBO_Already_Init)
		return;

	PBO_Already_Init = true;

	if (!PBO_Create() && developer.value)
	{
		Con_Printf ( "PBuffer create error\n" );
		PBO_Error ();
	}
}

void PBO_Delete (void)
{
	if ( PBuffer.hBuffer != NULL )
	{
		wglDeleteContext       ( PBuffer.hGlRc );
		wglReleasePbufferDCARB ( PBuffer.hBuffer, PBuffer.hdc );
		wglDestroyPbufferARB   ( PBuffer.hBuffer );
	}
	if (developer.value)
		Con_Printf ( "PBuffer deleted\n" );
}

boolean	PBO_Activate (void)
{
	PBuffer.saveHdc   = wglGetCurrentDC      ();	// save previous target
	PBuffer.saveHglrc = wglGetCurrentContext ();

	return wglMakeCurrent ( PBuffer.hdc, PBuffer.hGlRc ) != 0;
}

boolean	PBO_Deactivate (void)
{
	boolean	result;
	if ( PBuffer.saveHdc == NULL || PBuffer.saveHglrc == NULL )
		return false;

	result = wglMakeCurrent ( PBuffer.saveHdc, PBuffer.saveHglrc );

	PBuffer.saveHdc   = NULL;
	PBuffer.saveHglrc = NULL;

	return result;
}

boolean	PBO_BindToTexture ( int buf )
{
	return wglBindTexImageARB ( PBuffer.hBuffer, buf ) != GL_FALSE;
}

boolean	PBO_UnbindFromTexture ( int buf )
{
	return wglReleaseTexImageARB ( PBuffer.hBuffer, buf ) != GL_FALSE;
}


//=========================
const boolean	PBuffer_isLost (void)
{
	int	lost = 0;

	wglQueryPbufferARB ( PBuffer.hBuffer, WGL_PBUFFER_LOST_ARB, &lost );

	return lost != 0;
}


/*

boolean	PBuffer_setCubemapSide ( int side )
{
	boolean result;
	if ( (PBuffer.mode & modeTextureCubeMap) == 0 )
		return false;

	glFlush ();

	result = PBuffer_setAttr ( WGL_CUBE_MAP_FACE_ARB, side );
	return result;
}
*/




	/*
void	initPBuffer ()
{
	if (PBuffer_init)
		return;

	PBuffer_init = true;

	if (!PBO_Create())
	{
		Con_Printf ( "PBuffer create error\n" );
		PBuffer_printLastError ();
	}

	PBuffer_makeCurrent ();


	//init ();

	reshape ( pbuffer.getWidth (), pbuffer.getHeight () );

	pbuffer.restoreCurrent ();


	glGenTextures ( 1, &pbufferTex );
	glBindTexture ( GL_TEXTURE_2D, pbufferTex );

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

	glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 );								// set 1-byte alignment

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

	if ( pbuffer.getMode () & PBuffer :: modeTextureMipmap )
	{
    	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

    					      // Generate mipmap automatically if supported
		if ( isExtensionSupported ( "GL_SGIS_generate_mipmap" ) )
            glTexParameteri ( GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE );
	}

							// reqiured only for glCopyTexSubImage2D
	glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGBA8, pbuffer.getWidth (), pbuffer.getHeight (),
	               0, GL_RGBA, GL_UNSIGNED_BYTE, 0 );

  
}*/