#include "quakedef.h"

static unsigned short s_oldHardwareGamma[3][256];
static unsigned char s_gammatable[256];
qboolean deviceSupportsGamma;

void GL_CheckHardwareGamma( void )
{
	HDC			hDC;

	deviceSupportsGamma = false;
	hDC = GetDC( GetDesktopWindow() );
	deviceSupportsGamma = GetDeviceGammaRamp( hDC, s_oldHardwareGamma );
	ReleaseDC( GetDesktopWindow(), hDC );

	if ( deviceSupportsGamma )
	{
		// do a sanity check on the gamma values
		if ( ( HIBYTE( s_oldHardwareGamma[0][255] ) <= HIBYTE( s_oldHardwareGamma[0][0] ) ) ||
			( HIBYTE( s_oldHardwareGamma[1][255] ) <= HIBYTE( s_oldHardwareGamma[1][0] ) ) ||
			( HIBYTE( s_oldHardwareGamma[2][255] ) <= HIBYTE( s_oldHardwareGamma[2][0] ) ) )
		{
			deviceSupportsGamma = false;
		}
		//
		// make sure that we didn't have a prior crash in the game, and if so we need to
		// restore the gamma values to at least a linear value
		//
		if ( ( HIBYTE( s_oldHardwareGamma[0][181] ) == 255 ) )
		{
			int g;
			//ri.Printf( PRINT_WARNING, "WARNING: suspicious gamma tables, using linear ramp for restoration\n" );
			for ( g = 0; g < 255; g++ )
			{
				s_oldHardwareGamma[0][g] = g << 8;
				s_oldHardwareGamma[1][g] = g << 8;
				s_oldHardwareGamma[2][g] = g << 8;
			}
		}
	}
}

void GLimp_SetGamma( unsigned char red[256], unsigned char green[256], unsigned char blue[256] ) {
	unsigned short table[3][256];
	int		i, j;
	int		ret;
	OSVERSIONINFO	vinfo;

	if (!deviceSupportsGamma) 
		return;

	for ( i = 0; i < 256; i++ ) {
		table[0][i] = ( ( ( unsigned short ) red[i] ) << 8 ) | red[i];
		table[1][i] = ( ( ( unsigned short ) green[i] ) << 8 ) | green[i];
		table[2][i] = ( ( ( unsigned short ) blue[i] ) << 8 ) | blue[i];
	}

	// Win2K puts this odd restriction on gamma ramps...
	vinfo.dwOSVersionInfoSize = sizeof(vinfo);
	GetVersionEx( &vinfo );
	if ( vinfo.dwMajorVersion == 5 && vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) {
		//Com_Printf( "performing W2K gamma clamp.\n" );
		for ( j = 0 ; j < 3 ; j++ ) {
			for ( i = 0 ; i < 128 ; i++ ) {
				if ( table[j][i] > ( (128+i) << 8 ) ) {
					table[j][i] = (128+i) << 8;
				}
			}
			if ( table[j][127] > 254<<8 ) {
				table[j][127] = 254<<8;
			}
		}
	} else {
		//Con_Printf( "skipping W2K gamma clamp.\n" );
	}

	// enforce constantly increasing
	for ( j = 0 ; j < 3 ; j++ ) {
		for ( i = 1 ; i < 256 ; i++ ) {
			if ( table[j][i] < table[j][i-1] ) {
				table[j][i] = table[j][i-1];
			}
		}
	}

	ret = SetDeviceGammaRamp( wglGetCurrentDC(), table );
	if ( !ret ) {
		Con_Printf( "SetDeviceGammaRamp failed.\n" );
	}
}

void GL_RestoreGamma( void )
{
	if ( deviceSupportsGamma )
	{
		HDC hDC;
		hDC = GetDC( GetDesktopWindow() );
		SetDeviceGammaRamp( hDC, s_oldHardwareGamma );
		ReleaseDC( GetDesktopWindow(), hDC );
	}
}

void GL_SetGamma( void )
{
	int		i;
	float	g;
	float	inf;
	
	if (!deviceSupportsGamma)
		GL_CheckHardwareGamma();

	if ( v_gamma.value < 0.5f )
		Cvar_Set( "gamma", "0.5" );
	else if ( v_gamma.value > 2.0f )
		Cvar_Set( "gamma", "2.0" );

	if ( gl_contrast_level.value < 0.5f )
		Cvar_Set( "gl_contrast_level", "0.5" );
	else if ( gl_contrast_level.value > 2 )
		Cvar_Set( "gl_contrast_level", "2" );

	if ( gl_overbright_level.value < 10.0f )
		Cvar_Set( "gl_overbright_level", "10.0" );
	else if ( gl_overbright_level.value > 40.0f )
		Cvar_Set( "gl_overbright_level", "40.0" );

	g = v_gamma.value;

	for ( i = 0; i < 256; i++ ) {
		if ( g == 1 )
			inf = i;
		else
			inf = 255 * pow ( i/255.0f, 1.0f / g ) + 0.5f;

		inf *= gl_overbright_level.value*0.1*(1+(gl_contrast_level.value-1)*(1-abs(128-i)/128)*(1-abs(128-i)/128));
		if (inf < 0)
			inf = 0;
		if (inf > 255)
			inf = 255;
		s_gammatable[i] = (int)inf;
	}

	if ( deviceSupportsGamma )
		GLimp_SetGamma( s_gammatable, s_gammatable, s_gammatable );
}