/*
Copyright (C) 2009-2014 Baker

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// vid.c -- common video


#include "quakedef.h"

viddef_t	vid;				// global video state

#pragma message ("Are we reseting hardware gamma on start?  Verify  We shoud")




//
// set mode and restart
//

/*
================
VID_SetMode
================
*/

static void VID_Activate (cbool active);
int VID_SetMode (int modenum)
{
// so Con_Printfs don't mess us up by forcing vid and snd updates
	int temp = scr_disabled_for_loading;
	cbool re_setupgl;

	scr_disabled_for_loading = true;  // temped
	vid.canalttab = false;

	VID_Activate (0); // Deactivate, stops sounds, releases key

	// Platform specific stuff
	re_setupgl = (VID_Local_SetMode (modenum) == false);

	// Assignment
	{
		vid.modenum_screen = modenum;
		vid.screen = vid.modelist[vid.modenum_screen];

		// keep cvars in line with actual mode
		VID_Cvars_Sync_To_Mode (&vid.modelist[vid.modenum_screen]);

	// Refresh console
#ifdef GLQUAKE_RENDERER_SUPPORT
		vid.consize_stale = true;
		vid.numpages = 2;
#else // WinQuake ..

#pragma message ("Baker: Blitting in winquake will break these calcs, should be clwidth")
	// Moved conwidth, conheight, aspect to CreateDIB in WinQuake
	// Mac must mirror or adapt it
		VID_Local_SetPalette (vid.curpal);
		vid.numpages = 1;
#endif // !GLQUAKE_RENDERER_SUPPORT

		Sbar_Changed ();
		vid.recalc_refdef = 1;
	}

#ifdef GLQUAKE_RENDERER_SUPPORT
	// GL break-in
	GL_Evaluate_Renderer ();
	GL_SetupState ();

	if (re_setupgl)
	{
		if (host_initialized)
			Con_DPrintf ("VID_SetMode: Couldn't reuse context.  Reuploading images ...");
		TexMgr_ReloadImages (1); // SAFE?
		Con_DPrintf (" completed.");
	} else Con_DPrintf ("Reused context no reupload images\n");

	// Resolution dependent settings need refreshed
	vid.warp_stale = true;
	vid.consize_stale = true;

	// ensure swap settings right
	VID_Local_Vsync ();

#ifdef GLQUAKE_HARDWARE_GAMMA
	VID_Gamma_Think ();
	VID_Gamma_Clock_Set (); // Baker: This sets a clock to restore the gamma
#endif // GLQUAKE_HARDWARE_GAMMA


#endif // GLQUAKE_RENDERER_SUPPORT

#ifdef WINQUAKE_VSYNC_SUPPORT
	VID_Local_Vsync ();
#endif // WINQUAKE_VSYNC_SUPPORT

	// Restore sound

	VID_Activate (1);  // Activate, restores sound

	vid.canalttab = true;
	scr_disabled_for_loading = temp;

	return true;
}

cbool VID_Restart (int flags /* favorite vs. temp*/)
{

	vmode_t		newmode = VID_Cvars_To_Mode ();
	vmode_t		oldmode = vid.screen;
	int			newmodenum;

	// No change scenario
	if ( memcmp (&newmode, &oldmode, sizeof(vmode_t)) == 0
#ifdef WINQUAKE_RENDERER_SUPPORT
		&& vid.stretch_old_cvar_val == (int)vid_sw_stretch.value
#endif //WINQUAKE_RENDERER_SUPPORT
		) // End paren
	{
		Con_SafePrintf ("Video mode request is same as current mode.\n");
		return false;
	}

	// Fullscreen must check existing modes, window must set it instead.
	switch (newmode.type)
	{
	case MODE_WINDOWED:
		memcpy (&vid.modelist[MODE_WINDOWED], &newmode, sizeof (vmode_t) );
		newmodenum = 0;
		break;

	case MODE_FULLSCREEN:
		if (VID_Mode_Exists (&newmode, &newmodenum) == false)
		{
			Con_SafePrintf ("%d x %d (%i) is not a valid fullscreen mode\n",
				(int)vid_width.value,
				(int)vid_height.value,
				(int)vid_fullscreen.value);
			return false;
		}
		break;
	}

	// Determine if the mode is invalid.

	VID_SetMode (newmodenum);

	if (flags == USER_SETTING_FAVORITE_MODE)
		vid.modenum_user_selected = vid.modenum_screen;

	return true;
}

/*
================
VID_Test -- johnfitz -- like vid_restart, but asks for confirmation after switching modes
================
*/
void VID_Test (lparse_t *unused)
{
	vmode_t		newmode = VID_Cvars_To_Mode ();
	vmode_t		oldmode = vid.screen;
	cbool	mode_changed = memcmp (&newmode, &vid.screen, sizeof(vmode_t) );

	if (!mode_changed)
		return;
//
// now try the switch
//
	VID_Restart (USER_SETTING_FAVORITE_MODE);

	//pop up confirmation dialog
	if (!SCR_ModalMessage("Would you like to keep this\nvideo mode? (y/n)\n", 5, false))
	{
		// User rejected new mode: revert cvars and mode
		VID_Cvars_Sync_To_Mode (&oldmode);
		VID_Restart (USER_SETTING_FAVORITE_MODE);
	}
}

void VID_Alt_Enter_f (void)
{
	if (vid.modenum_screen != vid.modenum_user_selected)
	{
		// Go to favorite mode
		VID_Cvars_Sync_To_Mode ( &vid.modelist[vid.modenum_user_selected] );
		VID_Restart (USER_SETTING_FAVORITE_MODE);
		return;
	}

// ALT-ENTER to a temp mode
	if (vid.screen.type == MODE_WINDOWED)
		VID_Cvars_Set_Autoselect_Temp_Fullscreen_Mode (vid.modenum_screen);
	else  VID_Cvars_Set_Autoselect_Temp_Windowed_Mode (vid.modenum_screen);

	VID_Restart (ALT_ENTER_TEMPMODE);
}


void VID_Restart_f (void)
{
	VID_Restart (USER_SETTING_FAVORITE_MODE);
}

//
// in-game
//

#ifdef GLQUAKE_RESIZABLE_WINDOW
// Baker: Optional resize on-the-fly start ...
void VID_Resize_Check (void)
{
	// Perform resize check
	vid.resized = true;
	vid.recalc_refdef = 1;
	vid.warp_stale = true;
}

void VID_Resize_Think (void)
{
	if (vid.resized || vid.client_window.width == 0)
	{
		VID_Local_Resize_Act ();

		// Future: Let's center the screen?
		eglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_ACCUM_BUFFER_BIT);
		vid.resized = false;
	}
}
// Baker: Optional resize on-the-fly-finished

#endif // GLQUAKE_RESIZABLE_WINDOW


static void VID_Activate (cbool active)
{

	if (active)
	{
		if (vid.sound_suspended)
		{
			S_UnblockSound ();
			CDAudio_Resume ();
#ifdef SUPPORTS_NEHAHRA
			FMOD_Resume ();
#endif // SUPPORTS_NEHAHRA
			vid.sound_suspended = false;
		}
#pragma message ("Baker: This needs to return to control the mouse a little")
		Input_Think ();
	}

	if (!active)
	{
		if (!vid.sound_suspended)
		{
			S_BlockSound ();
#ifdef PLATFORM_OSX
			S_StopAllSounds(true); // On a Mac, S_BlockSound alone doesn't suffice
#endif // PLATFORM_OSX
			CDAudio_Pause ();
#ifdef SUPPORTS_NEHAHRA
			FMOD_Pause ();
#endif // SUPPORTS_NEHAHRA
			vid.sound_suspended = true;
		}
		Input_Local_Deactivate (); // Stops drag flag
		Key_Release_Keys (NULL); // We might not get all key messages during this process.

	}
}


void VID_AppActivate(cbool fActive, cbool minimize, cbool hide)
{
	vid.ActiveApp = fActive;
	vid.Minimized = minimize;
	vid.Hidden = hide;

#if 0
	Con_Printf ("App activate occurred %i\n", vid.ActiveApp);
#endif

	if (vid.ActiveApp)
	{
		VID_Activate (1); // Baker: This activates sound, gives input a think

		if (vid.screen.type == MODE_FULLSCREEN && vid.canalttab && vid.wassuspended)
		{
			VID_Local_Suspend (false);
			vid.wassuspended = false;

#ifdef DIRECT3D_WRAPPER
		Sbar_Changed ();
#endif // DIRECT3D_WRAPPER

		}
	}

	if (!vid.ActiveApp)
	{
		if (vid.screen.type == MODE_FULLSCREEN && vid.canalttab)
		{
			VID_Local_Suspend (true);
			vid.wassuspended = true;
		}

		VID_Activate (0); // Baker: This deactivates sound, releases keys
	}
}

void VID_SwapBuffers (void)
{
	if (scr_skipupdate)
		return;

#ifdef GLQUAKE_RENDERER_SUPPORT
	VID_Local_SwapBuffers ();
#else // WinQuake
	{
	// update one of three areas
		vrect_t		vrect;

		if (winquake_scr_copyeverything)
		{
			// Copies entire screen
		vrect.x = 0;
		vrect.y = 0;
			vrect.width = clwidth;
			vrect.height = clheight;
			vrect.pnext = 0;

			VID_Update (&vrect);
		}
		else if (winquake_scr_copytop)
		{
			// Copies entire screen except sbar
			vrect.x = 0;
			vrect.y = 0;
			vrect.width = clwidth;
			vrect.height = clheight - sb_lines;
			vrect.pnext = 0;

			VID_Update (&vrect);
		}
		else
		{
			// Copies only view area
			vrect.x = scr_vrect.x;
			vrect.y = scr_vrect.y;
			vrect.width = scr_vrect.width;
			vrect.height = scr_vrect.height;
			vrect.pnext = 0;

			VID_Update (&vrect);
		}
	}
#endif // WINQUAKE_RENDERER_SUPPORT
}


//
// functions to match modes to cvars or reverse
//

void VID_Cvars_Sync_To_Mode (vmode_t* mymode)
{
	// Don't allow anything exiting to call this.  I think we are "ok"
	Cvar_SetValueQuick (&vid_width, (float)mymode->width);
	Cvar_SetValueQuick (&vid_height, (float)mymode->height);
	Cvar_SetValueQuick (&vid_fullscreen, mymode->type == MODE_FULLSCREEN ? 1 : 0);
}

vmode_t VID_Cvars_To_Mode (void)
{
	vmode_t retmode;

	retmode.type	= vid_fullscreen.value ? MODE_FULLSCREEN : MODE_WINDOWED;
	retmode.width	= (int)vid_width.value;
	retmode.height	= (int)vid_height.value;
	retmode.bpp		= vid.desktop.bpp;

	if (retmode.type == MODE_WINDOWED)
	{
		retmode.width	= CLAMP (MIN_WINDOWED_MODE_WIDTH, retmode.width, vid.desktop.width);
		retmode.height	= CLAMP (MIN_WINDOWED_MODE_HEIGHT, retmode.height, vid.desktop.height);
		retmode.bpp		= vid.desktop.bpp;
	}

	return retmode;
}



cbool VID_Read_Early_Non_Mode_Cvars (void)
{
// Main difference is that we don't have to bail like we do with video mode.
// These must all be read.

	const cvar_t* cvarslist[] = {
#ifdef GLQUAKE_SUPPORTS_VSYNC
		&vid_vsync, // Baker: Important to read here for Direct3D
#endif // GLQUAKE_SUPPORTS_VSYNC
		&vid_gamma,
		&vid_contrast,
		&in_system_enhanced_keys,
#ifdef GLQUAKE_RENDERER_SUPPORT
		&vid_hardwaregamma,
#endif // GLQUAKE_RENDERER_SUPPORT
#ifdef WINQUAKE_RENDERER_SUPPORT
		&vid_sw_stretch,
#endif // WINQUAKE_RENDERER_SUPPORT
		&hd_folder,
	NULL};
	cbool found_in_config, found_in_autoexec;

	found_in_config = Read_Early_Cvars_For_File (CONFIG_CFG, cvarslist);
	found_in_autoexec = Read_Early_Cvars_For_File (AUTOEXEC_CFG, cvarslist);

	return (found_in_config || found_in_autoexec);
}


cbool VID_Read_Early_Cvars (void)
{

	// Any of these found and we bail
	char *video_override_commandline_params[] = {"-window", "-width", "-height", "-current", NULL } ;
	const cvar_t *cvarslist[] = {
		&vid_fullscreen,
		&vid_width,
		&vid_height,
#ifdef GLQUAKE_RENDERER_SUPPORT
		&vid_multisample,  // Baker: multisample support
#endif // GLQUAKE_RENDERER_SUPPORT
	NULL};
	cbool found_in_config, found_in_autoexec;
	int i;

	for (i = 0; video_override_commandline_params[i]; i++)
		if (COM_CheckParm (video_override_commandline_params[i]))
		{
#ifdef GLQUAKE_RENDERER_SUPPORT
			// Baker: We have to read multisample early
			found_in_config = Read_Early_Cvars_For_File (CONFIG_CFG, &cvarslist[3]);
			found_in_autoexec = Read_Early_Cvars_For_File (AUTOEXEC_CFG, &cvarslist[3]);
#endif // GLQUAKE_RENDERER_SUPPORT
			return false;
		}

	found_in_config = Read_Early_Cvars_For_File (CONFIG_CFG, cvarslist);
	found_in_autoexec = Read_Early_Cvars_For_File (AUTOEXEC_CFG, cvarslist);

	return (found_in_config || found_in_autoexec);
}


cbool VID_Mode_Exists (vmode_t* test, int *outmodenum)
{
	int i;

	for (i = 0; i < vid.nummodes; i++)
	{
//		if (memcmp (&vid.modelist[i], test, sizeof(vmode_t)))
		if (test->width != vid.modelist[i].width)
			continue; // No match

		if (test->height != vid.modelist[i].height)
			continue; // No match

		if (test->type != vid.modelist[i].type)
			continue; // No match

		// dup
		if (outmodenum) *outmodenum = i;
		return true; // Duplicate
	}
	return false;
}

void VID_MakeMode (modestate_t mode_type, vmode_t *new_mode)
{
	new_mode->type	= mode_type;
	new_mode->width	= 640;
	new_mode->height= 480;
	new_mode->bpp	= vid.desktop.bpp;

	// !!(int)vid_fullscreen.value --> turn into an int and "NOT" it twice
	// so must have 0 or 1 value.  In case vid_fullscreen is 2 or something weird
	if ( (!!(int)vid_fullscreen.value) == mode_type)
	{
		new_mode->width = (int)vid_width.value;
		new_mode->height= (int)vid_height.value;
	}
	vid.nummodes ++;
}

void VID_Cvars_Set_Autoselect_Temp_Windowed_Mode (int favoritemode)
{
	// Pencil in last windowed mode, but set the bpp to the desktop bpp
	VID_Cvars_Sync_To_Mode (&vid.modelist[MODE_WINDOWED]);
}

int VID_Find_Best_Fullscreen_Modenum (int request_width, int request_height)
{
	int best = -1;
	int	bestscore = -1;
	int i;

	// Look through the video modes.

	// If an exact matching fullscreen mode of same resolution
	// exists, pick that.  Try to go for same bpp.

	// Attempt 1: Match resolution

	// Baker: Locate matching mode
	for (i = 1; i < vid.nummodes; i ++)
	{
		vmode_t *mode = &vid.modelist[i];
		int size_match		= (mode->width == request_width && mode->height == request_height);

		int score = size_match * 20;

		if (score <= bestscore)
			continue;  // Didn't beat best

		// New best
		best = i;
		bestscore = score;
	}

	if (bestscore < 20)
	{
		// No size match ... try again
		// If fails, pick something with width/height both divisble by 8
		// so charset doesn't look stretched.  Go for largest mode with
		// desktop bpp and desktop refreshrate and desktop width/height
		// Unless those are stupid.

		best = -1;
		bestscore = -1;

		for (i = 1; i < vid.nummodes; i ++)
		{
			vmode_t *mode = &vid.modelist[i];

#if 0 // Baker: There are now laptops with 1440 x 900 (not divisible by 8) and 1366 x 768 (1366 not divisible by 8) resolutions.
			if (mode->width & 7)
				continue; // Skip stupid resolutions
			if (mode->height & 7)
				continue; // Skip stupid resolutions
#endif

			if (mode->width >= vid.desktop.width - 7)
				if (mode->height >= vid.desktop.height - 7)
				{
					// Take it
					best = i;
					break;
				}

			// Not an automatic winner.  If largest ...
			if (mode->width >= vid.modelist[best].width || mode->height >= vid.modelist[best].height)
			{
				best = i;
			}
		}

		if (best == -1)
			System_Error ("Couldn't find suitable video mode");

	}

	return best;
}

int VID_Cvars_To_Best_Fullscreen_Modenum (void)
{
	vmode_t preference = VID_Cvars_To_Mode ();
	return VID_Find_Best_Fullscreen_Modenum (preference.width, preference.height);
}


void VID_Cvars_Set_Autoselect_Temp_Fullscreen_Mode (int favoritemode)
{
	vmode_t *fave = &vid.modelist[favoritemode];
	int best = VID_Find_Best_Fullscreen_Modenum (fave->width, fave->height);

	// Set cvars
	VID_Cvars_Sync_To_Mode (&vid.modelist[best]);
}


//
// startup / shutdown
//

void	VID_Init (void)
{
	int		i;

	cbool videos_cvars_read;

	// We need to set this super early.
	if (COM_CheckParm ("-nomouse"))
		vid.nomouse = true;

    VID_Local_Init ();

	vid.desktop = VID_Local_GetDesktopProperties (); // Good time to get them.


	Cmd_AddCommands (VID_Init);




	// Now, if we have -window or anything we don't bother to read the cvars early
	// But we will still read them later.

	VID_Read_Early_Non_Mode_Cvars ();
	videos_cvars_read = VID_Read_Early_Cvars ();

	// The horror!!!  
	com_hdfolder_count = 0;
	if (!isDedicated && hd_folder.string[0] && HD_Folder_Ok (/* editing it maybe !*/(char *) hd_folder.string)) {

		char		this_qpath[MAX_QPATH_64];
		const char	*cursor = hd_folder.string;
			
		while (  (cursor = String_Get_Word (cursor, ",", this_qpath, sizeof(this_qpath)))  ) {
			char folder_url[MAX_OSPATH];
			// Construct the full url
			FS_FullPath_From_Basedir (folder_url, this_qpath);
			if (File_Exists (folder_url) && File_Is_Folder(folder_url)) {
				// It's going to put screenshots and configs and demos in there.
				// Needs to be a read only folder, though.  I think we are fine.  We write to com_gamedir.
				
				com_modified = true;
				COM_AddGameDirectory (this_qpath, true /*hd only*/);
			}
			// No verbose print here.  Too early to print.
			// else Con_VerbosePrintf ("Folder \"%s\" does not exist.\n", folder_url); 
		}
	}



	vid.system_enhanced_keys = !!in_system_enhanced_keys.value;

	if ((i = COM_CheckParm("-width")) && i + 1 < com_argc)
		Cvar_SetValueQuick (&vid_width, (float)atoi(com_argv[i+1]) );

	if ((i = COM_CheckParm("-height")) && i + 1 < com_argc)
		Cvar_SetValueQuick (&vid_height, (float)atoi(com_argv[i+1]) );

	if (COM_CheckParm("-current"))
	{
		VID_Cvars_Sync_To_Mode (&vid.desktop); // Use desktop sizes.
	}

	if (COM_CheckParm("-window"))
		Cvar_SetValueQuick (&vid_fullscreen, 0);

	VID_Local_Window_PreSetup (); // Window: Registers frame class, Mac: Gets display

// Add the windowed mode
	VID_MakeMode (MODE_WINDOWED, &vid.modelist[MODE_WINDOWED]);

// Add the fullscreen modes

	VID_Local_AddFullscreenModes ();

//    System_Alert ("Modes done");

	vid.initialized = true;

// Now we set the video mode

	VID_SetMode (vid_fullscreen.value ? VID_Cvars_To_Best_Fullscreen_Modenum() : MODE_WINDOWED);

	clwidth = vid_width.value;
	clheight = vid_height.value;

	vid.modenum_user_selected = vid.modenum_screen; // Default choice
	VID_Cvars_Sync_To_Mode (&vid.modelist[vid.modenum_user_selected]);

#ifdef GLQUAKE_HARDWARE_GAMMA
	VID_Gamma_Init ();
#endif // GLQUAKE_HARDWARE_GAMMA

	VID_Menu_Init(); //johnfitz
}


void VID_Shutdown (void)
{
   	if (!vid.initialized)
		return;

    VID_Local_Shutdown ();

	vid.canalttab = false;
	vid.initialized = false;
}


#ifdef GLQUAKE_HARDWARE_GAMMA
static void VID_Gamma_Table_Make (float gamma, float contrast, unsigned short* ramps)
{
	int	i, c;
	for (i = 0 ; i < 256 ; i++)		// For each color intensity 0-255
	{
		// apply contrast; limit contrast effect to 255 since this is a byte percent
		c = i * contrast;  if (c > 255)	c = 255;

		// apply gamma
		c = 255 * pow((c + 0.5)/255.5, gamma) + 0.5;  c = CLAMP (0, c, 255);

		ramps[i + 0] = ramps[i + 256] = ramps[i + 512] = c << 8;
	}
#pragma message ("Baker: Refresh gamma local table here?")
}


cbool gamma_available = false;
unsigned short	systemgammaramp[768]; // restore gamma on exit and/or alt-tab

void VID_Gamma_Shutdown (void)
{
	if (vid.ever_set_gamma)
		VID_Local_Gamma_Reset ();

}

void VID_Gamma_Init (void)
{
	gamma_available = VID_Local_IsGammaAvailable (systemgammaramp);
	if (!gamma_available)
	{
		Con_Warning ("Hardware gamma not available (GetDeviceGammaRamp failed)\n");
		return;
	}

	
	if (vid_hardwaregamma.value) {
		VID_Local_Gamma_Reset (); // Sets to system user specified value.
		Con_SafePrintf ("Hardware gamma enabled\n");
	} else {
		Con_SafePrintf ("Hardware gamma available\n");
	}


//	Cvar_RegisterVariable (&vid_hardware_contrast);

	
}



static double hardware_active_enforcer_time_clock = 0;
static int hardware_enforcer_count = 0;

static float vid_texgamma_user_req = 0; // Set back to 0 if changed.  Solely belongs to vid


//#pragma message ("We need to make it so a think here can turn it off if !vid_hardware.gamma && hardware_is_active or vid_hardware,gamma && !hardware_is_active")
void VID_Gamma_Think (void)
{
	static cbool	hardware_is_active = false;
	static int		vid_hardwaregamma_old = -1;
	cbool clock_fire = false;
	static unsigned short ramps[768]; // restore gamma on exit and/or alt-tab
	static	float	previous_gamma, previous_contrast;

	// bound cvars to menu range
	if (vid_contrast.value < VID_MIN_CONTRAST) Cvar_SetValueQuick (&vid_contrast, VID_MIN_CONTRAST);
	if (vid_contrast.value > VID_MAX_CONTRAST) Cvar_SetValueQuick (&vid_contrast, VID_MAX_CONTRAST);
	if (vid_gamma.value < VID_MIN_POSSIBLE_GAMMA) Cvar_SetValueQuick (&vid_gamma, VID_MIN_POSSIBLE_GAMMA);
	if (vid_gamma.value > VID_MAX_POSSIBLE_GAMMA) Cvar_SetValueQuick (&vid_gamma, VID_MAX_POSSIBLE_GAMMA);

	{
		cbool hardware_should_be_active = gamma_available && vid.ActiveApp && !vid.Minimized && !vid.Hidden && vid_hardwaregamma.value; // && !shutdown;
		cbool hardware_change = (hardware_is_active != hardware_should_be_active);

		// Because quake.rc is going to be read and it will falsely default away from user preference
		cbool ignore_cvar_change = (host_initialized == true  && host_post_initialized == false);

		// First update the gamma table
		cbool table_change = (vid_gamma.value != previous_gamma || vid_contrast.value  != previous_contrast) && !ignore_cvar_change;
		cbool actionable_change = hardware_change || (table_change && hardware_should_be_active);

		// Baker: We will set the gamma IF
		// 1) There is a hardware change
		// 2) There is a color change *AND* hardware is active
		// 3) If hardware is active and no color change but timer fires.
		if (vid_hardwaregamma_old != -1 && vid_hardwaregamma_old != !!vid_hardwaregamma.value) {
			if (vid_hardwaregamma.value) {
				// Hardware gamma is becoming active.
				TexMgr_Gamma_Execute (1.0);
			} else {
				// Texture gamma is becoming active
				TexMgr_Gamma_Execute (vid_gamma.value);
			}
		} else if (vid_hardwaregamma_old == 0 && vid_texgamma_user_req) {
			// We were "texture gamma" last frame.  And the user has type "txgamma something" in the console.
			Cvar_SetValueQuick (&vid_gamma, vid_texgamma_user_req); // Match up the gamma cvar
			TexMgr_Gamma_Execute (vid_texgamma_user_req);
			vid_texgamma_user_req = 0;
		}

		vid_hardwaregamma_old = !!vid_hardwaregamma.value; // We use this for the texture based control.
		//if (vid_hardwaregamma_old != -1 && vid_hardwaregamma_old vid_hardwaregamma. &&  vid_texgamma_user_req) {
		//	TexMgr_Gamma_Execute (vid_texgamma_user_req);
		//	Cvar_SetValueQuick (&vid_gamma, vid_texgamma_user_req);
		//	vid_texgamma_user_req = 0; // Unconditionally should have been handled
		//}

		//if (hardware_change && hardware_should_be_active) {
		//	TexMgr_Gamma_Execute (1.0); // Turn off texture gamma by setting to 1
		//}

		//if (hardware_change && !hardware_should_be_active) {
		//	TexMgr_Gamma_Execute (vid_gamma.value); // Turn on texture gamma by setting it.
		//}

		if (vid_texgamma_user_req)
			vid_texgamma_user_req = 0; // breakpoint me.  Should be hard to happen.

		if (!hardware_change && hardware_should_be_active && hardware_active_enforcer_time_clock)
		{
			if (realtime > hardware_active_enforcer_time_clock)
			{
				clock_fire = true;
				hardware_enforcer_count --;
				if (hardware_enforcer_count)
					hardware_active_enforcer_time_clock = realtime + 1;
				else hardware_active_enforcer_time_clock = 0; // Disable
				Con_DPrintf ("Gamma protector clock fired  ... ");
			}
		}

		if (hardware_change)
			Con_DPrintf ("Hardware change detected ... ");

		if (!actionable_change && !clock_fire)
			return;

		if (!hardware_change && table_change)
			Con_DPrintf ("Color change with hardware active ... ");

		// If we hit here, a change occurred.
		switch (hardware_should_be_active)
		{
		case true:
			{
				previous_gamma = vid_gamma.value;
				previous_contrast = vid_contrast.value;

				if (clock_fire) // Nudge table a little
				{
					static unsigned short tempramps[768]; // restore gamma on exit and/or alt-tab
					VID_Gamma_Table_Make (vid_gamma.value + 0.01, vid_contrast.value - 0.01, tempramps);
					VID_Local_Gamma_Set (tempramps); // Table nudge
				}
				VID_Gamma_Table_Make (vid_gamma.value, vid_contrast.value, ramps);
				Image_Build_Gamma_Table (vid_gamma.value, vid_contrast.value, vid.gammatable);
				VID_Local_Gamma_Set (ramps);
			}
			Con_DPrintf ("Custom Gamma set: contrast = %g\n", vid_contrast.value);
			hardware_is_active = true;
			break;

		case false:
			VID_Local_Gamma_Set (systemgammaramp);
			Con_DPrintf ("Gamma system set\n");
			hardware_is_active = false;
			hardware_enforcer_count = 0;
			hardware_active_enforcer_time_clock = 0;
			break;
		}
	}

}

void Vid_Gamma_TextureGamma_f (lparse_t *line)
{
	float val = atof(line->args[1]), clamped_value = CLAMP(VID_MIN_POSSIBLE_GAMMA, val, VID_MAX_POSSIBLE_GAMMA);
	extern float texmgr_texturegamma_current;

	if (vid_hardwaregamma.value) {
		Con_Printf ("Not applicable while vid_hardwaregamma is on.  Current level = %g\n", texmgr_texturegamma_current);
		return;
	}

	switch (line->count) {
	default:	Con_Printf ("Usage: %s <gamma %0.2f to %0.2f> - set gamma level when vid_hardwaregamma is off by baking gamma into textures.\n"
					"Current text is %g\n", line->args[0], (float)VID_MIN_POSSIBLE_GAMMA, (float)VID_MAX_POSSIBLE_GAMMA, texmgr_texturegamma_current);
				return;				
			
	case 2:		if (vid_texgamma_user_req) {
					Con_Printf ("Last requested texture gamma set never acknowledged\n");
					return;
}
		
				if (val != clamped_value) {
					Con_Printf ("gamma %g is out of range (%0.2f to %0.2f), clamping to range = %g\n", 
						val, (float)VID_MIN_POSSIBLE_GAMMA, (float)VID_MAX_POSSIBLE_GAMMA, clamped_value);
					val = clamped_value;
				}

				vid_texgamma_user_req = val;
				// Now we wait for end of frame for it to take effect.
	}
	// Switch num parms texture gamma request end
}


void VID_Gamma_Clock_Set (void)
{
	Con_DPrintf ("Gamma protector set\n");
	hardware_enforcer_count = 2;
	hardware_active_enforcer_time_clock = realtime + 3;
}
#endif // GLQUAKE_HARDWARE_GAMMA

#ifdef GLQUAKE_RENDERER_SUPPORT
void VID_BrightenScreen (void)
{
	float f;
	float current_contrast = CLAMP (VID_MIN_CONTRAST, vid_contrast.value, VID_MAX_CONTRAST);

	if (current_contrast <=1)
		return; // Because it won't do anything

	f = current_contrast;
	f = pow	(f, 1); // Baker: change to vid_hwgamma

	eglMatrixMode(GL_PROJECTION);
	eglLoadIdentity ();

#pragma message ("Baker: I expect this to act up if we ever center the window use less than fullscreen, would need to do it to test it")
//eglDisable (GL_SCISSOR_TEST);
eglDisable (GL_DEPTH_TEST); // It was this!
	if (sbar_drawn || console1.visible_pct == 1 || cls.titledemo || cl.intermission)
		eglViewport (clx, cly, clwidth, clheight); // Full screen
	else {
#pragma message ("Baker: I expect this to act up if we ever center the window use less than fullscreen, would need to do it to test it")
		eglViewport (clx + r_refdef.vrect.x,
					cly + vid.screen.height - r_refdef.vrect.y - r_refdef.vrect.height,
					r_refdef.vrect.width,
					r_refdef.vrect.height);
	}

	eglOrtho (0, 1, 1, 0, -99999, 99999);
	eglMatrixMode(GL_MODELVIEW);
	eglLoadIdentity ();


	eglDisable (GL_TEXTURE_2D);
	eglEnable (GL_BLEND);
	eglBlendFunc (GL_DST_COLOR, GL_ONE);


	eglBegin (GL_QUADS);
	while (f > 1)
	{
		if (f >= 2)
			eglColor3f (1,1,1);
		else
			eglColor3f (f - 1, f - 1, f - 1);

		eglVertex2f (0, 0);
		eglVertex2f (clwidth, 0);
		eglVertex2f (clwidth, clheight);
		eglVertex2f (0, clheight);
		f *= 0.5;
	}
	eglEnd ();

	eglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	eglEnable (GL_TEXTURE_2D);
	eglDisable (GL_BLEND);
	eglColor4f (1,1,1,1);
}


#endif // GLQUAKE_RENDERER_SUPPORT


#ifdef WINQUAKE_RENDERER_SUPPORT

/*
=================
V_CheckGamma
=================
*/


cbool VID_CheckGamma (void)
{
	static float previous_gamma, previous_contrast;

	if (vid_gamma.value == previous_gamma && vid_contrast.value == previous_contrast)
		return false;

	// bound cvars to menu range
	if (vid_contrast.value < VID_MIN_CONTRAST) Cvar_SetValueQuick (&vid_contrast, VID_MIN_CONTRAST);
	if (vid_contrast.value > VID_MAX_CONTRAST) Cvar_SetValueQuick (&vid_contrast, VID_MAX_CONTRAST);
	if (vid_gamma.value < VID_MIN_POSSIBLE_GAMMA) Cvar_SetValueQuick (&vid_gamma, VID_MIN_POSSIBLE_GAMMA);
	if (vid_gamma.value > VID_MAX_POSSIBLE_GAMMA) Cvar_SetValueQuick (&vid_gamma, VID_MAX_POSSIBLE_GAMMA);

	previous_gamma = vid_gamma.value;
	previous_contrast = vid_contrast.value;

	Image_Build_Gamma_Table (vid_gamma.value, vid_contrast.value, vid.gammatable);
	vid.recalc_refdef = 1; // force a surface cache flush

	return true;
}
#endif // WINQUAKE_RENDERER_SUPPORT


/*
=================
VID_BeginRendering -- sets values of clx, cly, clwidth, clheight
=================
*/
void VID_BeginRendering (int *x, int *y, int *width, int *height)
{
#ifdef GLQUAKE_RENDERER_SUPPORT

#pragma message ("Make sure VID_Resize_Think does NOT do anything essential ever")
#ifdef GLQUAKE_RESIZABLE_WINDOW
	if (!vid.Minimized && vid.ActiveApp)
		VID_Resize_Think (); // Optional resize window on-the-fly
#endif // ! GLQUAKE_RESIZABLE_WINDOW

#ifdef DIRECT3D_WRAPPER
//#pragma message ("Baker: I would like to remove this.  I use it for ALT-TAB and the Direct3D to refresh the status bar")
	vid.numpages = 4;
#else
	vid.numpages = (gl_triplebuffer.value) ? 3 : 2;
#endif
	sbar_drawn = 0;

// Baker: Commented out client canvas centering for GL.
// Center these on the client_canvas
//	*x = (client_window.width - vid.screen.width) / 2;
//	*y = (client_window.height - vid.screen.height) / 2;

//	*width = vid.screen.width;
//	*height = vid.screen.height;
	*x = *y = 0;
#ifdef GLQUAKE_RESIZABLE_WINDOW
	*width = vid.client_window.width;
	*height = vid.client_window.height;
#else
	*width = vid.screen.width;
	*height = vid.screen.height;
#endif // GLQUAKE_RESIZABLE_WINDOW


#endif // GLQUAKE_RENDERER_SUPPORT

#ifdef WINQUAKE_RENDERER_SUPPORT
	*x = *y = 0;
	*width = vid.conwidth;   // WinQuake now uses vid.conwidth as buffer width
	*height = vid.conheight; // WinQuake now uses vid.conheight as buffer height
	winquake_scr_copytop = 0;
	winquake_scr_copyeverything = 0;
#endif // WINQUAKE_RENDERER_SUPPORT

}

/*
=================
VID_EndRendering
=================
*/

void VID_EndRendering (void)
{
#pragma message ("Can the following prevent gamma restore on minimize?")
	if (scr_skipupdate )
		return;

#ifdef GLQUAKE_RENDERER_SUPPORT

#ifdef GLQUAKE_HARDWARE_GAMMA
	VID_Gamma_Think (); // Baker: Hardware gamma is smart, will self-help and even turn itself off (or on), only when needed
#endif // GLQUAKE_HARDWARE_GAMMA

	if (!vid_hardwaregamma.value)
		VID_BrightenScreen ();
#endif // GLQUAKE_RENDERER_SUPPORT

	VID_SwapBuffers ();

#ifdef SUPPORTS_AVI_CAPTURE
// WinQuake captures the frame after swap buffers because vid.buffer must be updated first.
#pragma message ("I moved GL AVI capture after swapbuffers.  See if still good.  Would need to test movie making.")
	Movie_UpdateScreen ();
#endif // SUPPORTS_AVI_CAPTURE // WINQUAKE_RENDERER_SUPPORT
}


unsigned *VID_GetBuffer_RGBA_Malloc (int *width, int *height, cbool bgra)
{

#ifdef GLQUAKE_RENDERER_SUPPORT
	int pixels = clwidth * clheight;

	byte *src_buf = (byte *)malloc (clwidth * clheight * 3);
	byte *dst_buf = (byte *)malloc (clwidth * clheight * 4);

	byte *src;
	byte *dst;
	int i;

	unsigned *buffer = (unsigned *)dst_buf;

	eglReadPixels (clx, cly, clwidth, clheight, GL_RGB, GL_UNSIGNED_BYTE, src_buf);

	if (vid_hardwaregamma.value && (vid_contrast.value != 1 || vid_gamma.value != 1))
	{
		for (i = 0, dst = dst_buf, src = src_buf; i < pixels; i++, dst += 4, src += 3)
		{
			if (bgra)
			{
				dst[0] = vid.gammatable[src[2]];
				dst[1] = vid.gammatable[src[1]];
				dst[2] = vid.gammatable[src[0]];
			}
			else
			{
				dst[0] = vid.gammatable[src[0]];
				dst[1] = vid.gammatable[src[1]];
				dst[2] = vid.gammatable[src[2]];
			}
			dst[3] = 255;
		}
	}
	else
	{
		for (i = 0, dst = dst_buf, src = src_buf; i < pixels; i++, dst += 4, src += 3)
		{
			if (bgra)
			{
				dst[0] = src[2];
				dst[1] = src[1];
				dst[2] = src[0];
			}
			else
			{
				dst[0] = src[0];
				dst[1] = src[1];
				dst[2] = src[2];
			}

			dst[3] = 255;
		}
	}

	free (src_buf);
#endif // GLQUAKE_RENDERER_SUPPORT


#ifdef WINQUAKE_RENDERER_SUPPORT
	byte *buffer_ = (byte *)malloc (clwidth * clheight * 4);
	unsigned *buffer = (unsigned *)buffer_;

	{
		int	i, j, rowp;
		byte *p = (byte *)buffer;
		for (i = clheight - 1 ; i >= 0 ; i--)
		{
			rowp = i * vid.rowbytes;
			for (j = 0 ; j < clwidth ; j++)
			{
				if (bgra)
				{
					*p++ = vid.curpal[vid.buffer[rowp]*3+2];
					*p++ = vid.curpal[vid.buffer[rowp]*3+1];
					*p++ = vid.curpal[vid.buffer[rowp]*3+0];
				}
				else
				{
					*p++ = vid.curpal[vid.buffer[rowp]*3+0];
					*p++ = vid.curpal[vid.buffer[rowp]*3+1];
					*p++ = vid.curpal[vid.buffer[rowp]*3+2];
				}
				*p++ = 255;

				rowp++;
			}
		}
	}

#endif // WINQUAKE_RENDERER_SUPPORT

	// We are upside down flip it.
	Image_Flip_Buffer (buffer, clwidth, clheight, 4);

	*width = clwidth;
	*height = clheight;

	return buffer;
}

#ifdef WINQUAKE_RENDERER_SUPPORT
void VID_ShiftPalette (unsigned char *palette)
{
	VID_Local_SetPalette (palette);
	memcpy (vid.curpal, palette, sizeof (vid.curpal));
}

void VID_Palette_NewGame (void)
{
	// Free the old palette
	if (vid.basepal)
		free (vid.basepal);

	vid.basepal = (byte *)COM_LoadMallocFile ("gfx/palette.lmp");
	if (!vid.basepal)
		System_Error ("Couldn't load gfx/palette.lmp");

	if (vid.colormap)
		free (vid.colormap);

	vid.colormap = (byte *)COM_LoadMallocFile ("gfx/colormap.lmp");
	if (!vid.colormap)
		System_Error ("Couldn't load gfx/colormap.lmp");

	VID_Local_Modify_Palette (vid.basepal); // On Mac won't do anything

	View_Blend_Stale ();
}
#endif // WINQUAKE_RENDERER_SUPPORT


