/*
Copyright (C) 1996-1997 Id Software, Inc.

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.

*/
// screen.c -- master for refresh, status bar, console, chat, notify, etc

#include "quakedef.h"
#include "r_local.h"

// only the refresh window will be updated unless these variables are flagged
int			scr_copytop;
int			scr_copyeverything;
int			reflectavailable;
float		scr_con_current;
float		scr_conlines;		// lines of console to display
float		oldsbar;
float		oldscreensize, oldfov;
cvar_t	*scr_viewsize;
cvar_t	*scr_fov;	// 10 - 170
cvar_t	*scr_fovadapt;// "Hor+" scaling
cvar_t	*scr_conspeed;
cvar_t	*scr_centertime;
cvar_t	*scr_showram;
cvar_t	*scr_showturtle;
cvar_t	*scr_showpause;
cvar_t	*scr_printspeed;
cvar_t	*scr_conheight;	// 2000-01-12 Variable console height by Fett
cvar_t  *scr_scale;		// leilei
cvar_t  *scr_retroscale;		// leilei
cvar_t  *scr_aspectmode;		// leilei
qboolean	scr_initialized;		// ready to draw

qpic_t		*scr_ram;
qpic_t		*scr_net;
qpic_t		*scr_turtle;
int			sb_what_lines;	// leilei - refdef sbar scale hack
int			scr_fullupdate;

extern int	vidmodetweak;	// leilei - 0: normal resolution
							//          1: 16:10= 4:3	(320x200, 640x400)
							//			2: 2:3	= 4:3	(320x480 Tweaked)
							//			3: 8:10 = 4:3	(320x400 Tweaked)
int			clearconsole;
int			clearnotify;
int			retroscalefactor;	// leilei
viddef_t	vid;				// global video state

vrect_t		*pconupdate;
vrect_t		scr_vrect;

qboolean	scr_disabled_for_loading;
qboolean	scr_drawloading;
float		scr_disabled_time;
qboolean	scr_skipupdate;

qboolean	block_drawing;

void SCR_ScreenShot_f (void);

/*
===============================================================================

CENTER PRINTING

===============================================================================
*/

char		scr_centerstring[1024];
float		scr_centertime_start;	// for slow victory printing
float		scr_centertime_off;
int			scr_center_lines;
int			scr_erase_lines;
int			scr_erase_center;

/*
==============
SCR_CenterPrint

Called for important messages that should stay in the center of the screen
for a few moments
==============
*/
void SCR_CenterPrint (char *str)
{
	strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
	scr_centertime_off = scr_centertime->value;
	scr_centertime_start = cl.time;

// count the number of lines for centering
	scr_center_lines = 1;
	while (*str)
	{
		if (*str == '\n')
			scr_center_lines++;
		str++;
	}
}

void SCR_EraseCenterString (void)
{
	int		y;

	if (scr_erase_center++ > vid.numpages)
	{
		scr_erase_lines = 0;
		return;
	}

	if (scr_center_lines <= 4)
		y = vid.height*0.35;
	else
		y = 48;

	scr_copytop = 1;
	Draw_TileClear (0, y,vid.width, 8*scr_erase_lines);
}

void SCR_DrawCenterString (void)
{
	char	*start;
	int		l;
	int		j;
	int		x, y;
	int		remaining;

// the finale prints the characters one at a time
	if (cl.intermission)
		remaining = scr_printspeed->value * (cl.time - scr_centertime_start);
	else
		remaining = 9999;
#ifdef SCALED2D
		scr_erase_center = 0;
	start = scr_centerstring;

	if (scr_center_lines <= 4)
		y = vid.vconheight*0.35;
	else
		y = 48;

	do
	{
	// scan the width of the line
		for (l=0 ; l<40 ; l++)
			if (start[l] == '\n' || !start[l])
				break;
		x = (vid.vconwidth - l*8)/2;
		for (j=0 ; j<l ; j++, x+=8)
		{
			Draw_Character_Scaled (x, y, start[j]);
			if (!remaining--)
				return;
		}

		y += 8;

		while (*start && *start != '\n')
			start++;

		if (!*start)
			break;
		start++;		// skip the \n
	} while (1);
#else
	scr_erase_center = 0;
	start = scr_centerstring;

	if (scr_center_lines <= 4)
		y = vid.height*0.35;
	else
		y = 48;

	do
	{
	// scan the width of the line
		for (l=0 ; l<40 ; l++)
			if (start[l] == '\n' || !start[l])
				break;
		x = (vid.width - l*8)/2;
		for (j=0 ; j<l ; j++, x+=8)
		{
			Draw_Character (x, y, start[j]);
			if (!remaining--)
				return;
		}

		y += 8;

		while (*start && *start != '\n')
			start++;

		if (!*start)
			break;
		start++;		// skip the \n
	} while (1);
#endif
}

void SCR_CheckDrawCenterString (void)
{
	scr_copytop = 1;
	if (scr_center_lines > scr_erase_lines)
		scr_erase_lines = scr_center_lines;

// 2001-10-20 TIMESCALE extension by Tomaz/Maddes  start
//	scr_centertime_off -= host_frametime;
	scr_centertime_off -= host_cpu_frametime;
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes  end

	if (scr_centertime_off <= 0 && !cl.intermission)
		return;
	if (key_dest != key_game)
		return;

	SCR_DrawCenterString ();
}

//=============================================================================

float scalefactor;
float scalefactorv;


// QUAKESPASM CODE BY SEZERO START

/*
====================
AdaptFovx
Adapt a 4:3 horizontal FOV to the current screen size using the "Hor+" scaling:
2.0 * atan(width / height * 3.0 / 4.0 * tan(fov_x / 2.0))
====================
*/
extern cvar_t *temp2;
float AdaptFovx (float fov_x, float width, float height)
{
	float	a, x;
	float ferv;

	if (cl_sbar->value)
	ferv = 0.15 * ((float)sb_lines * 0.02083333); // leilei - fudge around the size of fov
	else
	ferv = 0;

	if (fov_x < 1 || fov_x > 179)
		fov_x = 90; // fallback to 90 if we fail...
	//	Sys_Error ("Bad fov: %f", fov_x);
#ifndef _WIN32
if (vid.aspect > 1.10f)
		return fov_x;			// there's no such thing as a widescreen dos machine	
#endif
//	if (!scr_fov_adapt.value)
//		return fov_x;
	if ((x = height / width) == 0.75)
		return fov_x;
	a = atan((0.75 - ferv)/ x * tan(fov_x / 360 * M_PI));
	a = a * 360 / M_PI;
	return a;
}

/*
====================
CalcFovy
====================
*/
float CalcFovy (float fov_x, float width, float height)
{
	float	a, x;

	if (fov_x < 1 || fov_x > 179)
		fov_x = 90; // fallback to 90 if we fail...
	//	Sys_Error ("Bad fov: %f", fov_x);

	x = width / tan(fov_x / 360 * M_PI);
	a = atan(height / x);
	a = a * 360 / M_PI;
	return a;
}

// QUAKESPASM CODE BY SEZERO END

// leilei - i am a dirty bitch for not figuring this out on my own .-_-.


/*
====================
CalcFov
====================
*/
float CalcFov (float fov_x, float width, float height)
{
	/*
	float	a;
	float	x;

	if (fov_x < 1 || fov_x > 179){
		
		fov_x = 179;		// leilei - don't crash.
	}
	//	Sys_Error ("Bad fov: %f", fov_x);

	x = width/tan(fov_x/360*M_PI);

	a = atan (height/x);

	a = a*360/M_PI;

	return a;
	*/
		float x;
	x = width / tan(fov_x / 360 * M_PI);
	return atan (height / x) * 360 / M_PI;
}


// MH's correct thing

float SCR_CalcFovX (float fov_y, float width, float height)
{
   // bound, don't crash
   if (fov_y < 1) fov_y = 1;
   if (fov_y > 179) fov_y = 179;

   return (atan (width / (height / tan ((fov_y * M_PI) / 360.0f))) * 360.0f) / M_PI;
}


float SCR_CalcFovY (float fov_x, float width, float height)
{
   // bound, don't crash
   if (fov_x < 1) fov_x = 1;
   if (fov_x > 179) fov_x = 179;

   return (atan (height / (width / tan ((fov_x * M_PI) / 360.0f))) * 360.0f) / M_PI;
}


void SCR_SetFOV (float fovvar, int width, int height)
{
   float aspect = (float) height / (float) width;
   float BASELINE_W, BASELINE_H;



   BASELINE_W = 640.0f;
   BASELINE_H = 480.0f - (24 * scalefactor);	// leilei - new baseline



   // horizontalFov = atan (tan (verticalFov) * aspectratio)
   // verticalFov = atan (tan (horizontalFov) / aspectratio)
   if (aspect > (BASELINE_H / BASELINE_W))
   {
      // use the same calculation as GLQuake did (horizontal is constant, vertical varies)
      r_refdef.fov_x = fovvar;
      r_refdef.fov_y = SCR_CalcFovY (r_refdef.fov_x, width, height);
   }
   else
   {
      // alternate calculation (vertical is constant, horizontal varies)
      r_refdef.fov_y = SCR_CalcFovY (fovvar, BASELINE_W, BASELINE_H);
      r_refdef.fov_x = SCR_CalcFovX (r_refdef.fov_y, width, height);
   }
}

float CalcFovOld (float fov_x, float width, float height)
{
        float   a;
        float   x;

        if (fov_x < 1 || fov_x > 179)
                Sys_Error ("Bad fov: %f", fov_x);

        x = width/tan(fov_x/360*M_PI);

        a = atan (height/x);

        a = a*360/M_PI;

        return a;
}
int yeahimconsoled;
int screenfake;	// leilei - forcing 320x200 metrics in any resolution no matter what (HACK HACK HACK!)

void SCR_StretchInit (void)
{

	int		i;
	float eh;



	if ((i = COM_CheckParm("-conwidth")) != 0){
		vid.vconwidth = Q_atoi(com_argv[i+1]);
		scalefactor = vid.width / vid.vconwidth;
		scalefactorv = vid.height / vid.vconheight;
	//	vid.vconwidth = vid.width / scalefactor;
	//	vid.vconheight = vid.height / scalefactor; // leilei - variable height doesn't work right now.
		yeahimconsoled = 1;
		sb_scaled		= 1;
		menu_scaled		= 1;
		console_scaled	= 1;	// currently bugged
	}
	
	else if ((i = COM_CheckParm("-youpickedupaclip")) != 0){
		vid.vconwidth = 32;
		vid.vconheight = 32;
		scalefactor = vid.width / vid.vconwidth;
		scalefactorv = vid.height / 32;
	//	vid.vconwidth = vid.width / scalefactor;
	//	vid.vconheight = vid.height / scalefactor; // leilei - variable height doesn't work right now.
		yeahimconsoled	= 0;
		sb_scaled		= 1;
		menu_scaled		= 0;
		console_scaled	= 0;	// currently bugged
	}
	else if ((i = COM_CheckParm("-conheight")) != 0){
		vid.vconheight = Q_atoi(com_argv[i+1]);
			scalefactor = vid.width / vid.vconwidth;
		scalefactorv = vid.height / vid.vconheight;
	//	vid.vconwidth = vid.width / scalefactor;
	//	vid.vconheight = vid.height / scalefactor; // leilei - variable height doesn't work right now.
		yeahimconsoled  = 1;
		sb_scaled		= 1;
		menu_scaled		= 1;
		console_scaled	= 1;	// currently bugged
	}
	else{
		scalefactor = 0;	// we don't stretch.
	}

		if (scalefactor > (vid.height / 200))
				scalefactor = vid.height / 200;
	
//	scr_scale->value = scalefactor + scalefactorv / 2; // transfer are cvar's ovar.
	

	/*
		int		i;
	float eh;
	if ((i = COM_CheckParm("-conwidth")) != 0){
		vid.vconwidth = Q_atoi(com_argv[i+1]);
		eh = vid.width / vid.vconwidth;
		vid.vconwidth = vid.width / eh;
		vid.vconheight = vid.height / eh; // leilei - variable height doesn't work right now.
		yeahimconsoled = 1;
		sb_scaled		= 1;
		menu_scaled		= 1;
		console_scaled	= 0;	// currently bugged
	}

	else
		vid.vconwidth = vid.width;
		*/
/*
	if (yeahimconsoled){
	vid.vconwidth &= 0xfff8; // make it a multiple of eight

	// pick a conheight that matches with correct aspect
//	vid.vconheight = vid.vconwidth*3 / 4;

	if ((i = COM_CheckParm("-conheight")) != 0)
	//	vid.vconheight = Q_atoi(com_argv[i+1]);

		vid.vconheight = vid.height / (vid.width / vid.vconwidth); // leilei - variable height doesn't work right now.
	if (vid.vconheight < 200)
		vid.vconheight = 200;
	}
	else
	{
		vid.vconwidth = vid.width;
		vid.vconheight = vid.height;
	}
	*/
	
	
//	if (vid.vconheight > vid.height)
//		vid.vconheight = 200; // clamping it crashes, so fall back to 320x200 anyway

//	if (yeahimconsoled)
//			vid.vconheight = vid.height / (vid.width / vid.vconwidth); // leilei - variable height doesn't work right now.


};

void SCR_CvarCheck (void)
{
		scalefactor = scr_scale->value;
		scalefactorv = scr_scale->value;

		if (scr_retroscale->value){
			
			scalefactorv = vid.height / 200;	// force old metrics etc!!

			scalefactor = scalefactorv;
			retroscalefactor = 1;
		}
		else
			retroscalefactor = 0;
		if (scalefactorv > (vid.height / 200))
				scalefactorv = vid.height / 200;  // if we go beyond 320x200, then clamp!
//		if (scalefactor > (vid.width / 320))
//				scalefactor = vid.width / 320;  // if we go beyond 320x200, then clamp!
		if (scalefactor < 1)
				scalefactor = 1;

		if (scalefactorv < 1)
			scalefactorv = 1;



		SCR_StretchRefresh();

};

void SCR_StretchRefresh (void)
{

	int		i;
	float eh;
	if (!scalefactor)
			return; // don't even try
	if (!scalefactorv)
			return; // don't even try
	screenfake = 0;
	if (screenfake == 1){
		
		vid.vconwidth = 320;
		vid.vconheight = 200;
		scalefactor = vid.width / vid.vconwidth;
		scalefactorv = vid.height / vid.vconheight;
		yeahimconsoled = 1;
		sb_scaled		= 1;
		menu_scaled		= 1;
		console_scaled	= 1;	// currently bugged
			vid.recalc_refdef = 1;		// yeah
		return;

	}

	if (screenfake == 2){
		
		vid.vconwidth = 320;
		vid.vconheight = 400;
		scalefactor = vid.width / vid.vconwidth;
		scalefactorv = vid.height / vid.vconheight;
		yeahimconsoled = 1;
		sb_scaled		= 1;
		menu_scaled		= 1;
		console_scaled	= 1;	// currently bugged
			vid.recalc_refdef = 1;		// yeah
		return;

	}

	if (screenfake == 3){
		
		vid.vconwidth = 360;
		vid.vconheight = 480;
		scalefactor = vid.width / vid.vconwidth;
		scalefactorv = vid.height / vid.vconheight;
		yeahimconsoled = 1;
		sb_scaled		= 1;
		menu_scaled		= 1;
		console_scaled	= 1;	// currently bugged
			vid.recalc_refdef = 1;		// yeah
		return;

	}

	if (screenfake == 4){
		
		vid.vconwidth = 640;
		vid.vconheight = 400;
		scalefactor = vid.width / vid.vconwidth;
		scalefactorv = vid.height / vid.vconheight;
		yeahimconsoled = 1;
		sb_scaled		= 1;
		menu_scaled		= 1;
		console_scaled	= 1;	// currently bugged
			vid.recalc_refdef = 1;		// yeah
		return;

	}
	if (scalefactorv < 1){
		vid.vconheight = vid.height;
		yeahimconsoled	= 0;
		sb_scaled		= 0;
		menu_scaled		= 0;
		console_scaled	= 0;	// currently bugged
	}
	if (scalefactor < 1){
		vid.vconwidth = vid.width;
		yeahimconsoled	= 0;
		sb_scaled		= 0;
		menu_scaled		= 0;
		console_scaled	= 0;	// currently bugged
			return;	// avoid div0 at all costs

	}
//		if (scalefactor > (vid.height / 200))
//				scalefactor = vid.height / 200;
//	if (scalefactor < (vid.vconheight / vid.height))
//		scalefactor = vid.vconheight / vid.height;

	vid.vconwidth = vid.width / scalefactor;
	vid.vconheight = vid.height / scalefactorv; // leilei - variable height doesn't work right now.
		yeahimconsoled	= 1;
		sb_scaled		= 1;
		menu_scaled		= 1;
		console_scaled	= 0;	// currently bugged

	// minimum safeguards so we don't make a size too small to enter
	// the evil land of crash city
	if (vid.vconwidth	< 320)	vid.vconwidth	= 320;
	if (vid.vconheight	< 200)	vid.vconheight	= 200;


	if (vid.vconwidth > vid.width && vid.vconheight > vid.height){
		vid.vconwidth = vid.width;
		vid.vconheight = vid.height;
		yeahimconsoled = 0;
		sb_scaled		= 0;
		menu_scaled		= 0;
		console_scaled	= 0;	
		scalefactor		= 1;
		scalefactorv	= 1;
	}

	vid.recalc_refdef = 1;		// yeah
};


/*
=================
SCR_CalcRefdef

Must be called whenever vid changes
Internal use only
=================
*/

static void SCR_CalcRefdef (void)
{
	vrect_t		vrect;
	float		size;
	float		stretchf = 1;
	
	scr_fullupdate = 0;		// force a background redraw
	vid.recalc_refdef = 0;

	if (vid.stretched == 2)
			stretchf = 2;
	else if (vid.stretched == 3)
			stretchf = 0.5;

	if (vid.width < 800 && vid.aspect > 1.6)
	{
			stretchf *= 0.8;	// try to fix it for 320x200....
	}

// force the status bar to redraw
	Sbar_Changed ();

//========================================

// bound viewsize
	if (scr_viewsize->value < 30)
		Cvar_Set (scr_viewsize, "30");
	if (scr_viewsize->value > 120)
		Cvar_Set (scr_viewsize, "120");

// bound field of view
	if (scr_fov->value < 10)
		Cvar_Set (scr_fov,"10");
//	if (scr_fov->value > 90)
//		Cvar_Set (scr_fov,"90"); // cheaters can eat crap
		if (scr_fov->value > 199)
		Cvar_Set (scr_fov,"199"); // cheaters can eat crap
				// leilei - Removed this because it breaks the 'tear gars' in swatteam

// intermission is always full screen
	if (cl.intermission)
		size = 120;
	else
		size = scr_viewsize->value;
#ifdef SCALED2D
	if(sb_scaled){
	if (size >= 120)
		sb_lines = 0;		// no status bar at all
	else if (size >= 110)
		sb_lines = 24;		// no inventory
	else
		sb_lines = 24+16+8;
	
}	else
#endif
	{
	if (size >= 120)
		sb_lines = 0;		// no status bar at all
	else if (size >= 110)
		sb_lines = 24;		// no inventory
	else
		sb_lines = 24+16+8;
}


#ifdef SCALED2D
	if (sb_scaled)
//	sb_what_lines = sb_lines * (vid.vconheight / vid.height); // leilei - refdef hack fix
	sb_what_lines = sb_lines * (scalefactorv); // leilei - refdef hack fix
	else
#endif
	sb_what_lines = sb_lines;
	
// these calculations mirror those in R_Init() for r_refdef, but take no
// account of water warping
	vrect.x = 0;
	vrect.y = 0;
	vrect.width = vid.width;
	vrect.height = vid.height;

	R_SetVrect (&vrect, &scr_vrect, sb_what_lines);
	r_refdef.vrect = scr_vrect;
	//r_refdef.fov_x = AdaptFovx (scr_fov->value, r_refdef.vrect.width, r_refdef.vrect.height);
	//r_refdef.fov_y = CalcFovy (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);
	//r_refdef.fov_y = CalcFovy (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);

		// quakespasm
	if (scr_aspectmode->value == 1){
	r_refdef.fov_x = AdaptFovx (scr_fov->value, r_refdef.vrect.width * stretchf, r_refdef.vrect.height);
	r_refdef.fov_y = CalcFovy (r_refdef.fov_x, r_refdef.vrect.width	 * stretchf, r_refdef.vrect.height);
	}
		// mh's "Quakespasm is wrong" post, a bit re-adapted
	else if (scr_aspectmode->value == 2){
	SCR_SetFOV(scr_fov->value, r_refdef.vrect.width * stretchf, r_refdef.vrect.height);
	}
	else
		// crappy old fov where we lose vert. just like Q3A!
	{
	r_refdef.fov_x = scr_fov->value;
	r_refdef.fov_y = CalcFovOld (r_refdef.fov_x, r_refdef.vrect.width * stretchf, r_refdef.vrect.height);


	}
// guard against going from one mode to another that's less than half the
// vertical resolution
	if (scr_con_current > vid.height)
		scr_con_current = vid.height;

// notify the refresh of the change
	R_ViewChanged (&vrect, sb_what_lines, vid.aspect);
//	R_ViewChanged (vid.aspect);
}


/*
=================
SCR_SizeUp_f

Keybinding command
=================
*/
void SCR_SizeUp_f (void)
{
	Cvar_SetValue (scr_viewsize, scr_viewsize->value+10);
	vid.recalc_refdef = 1;
}


/*
=================
SCR_SizeDown_f

Keybinding command
=================
*/
void SCR_SizeDown_f (void)
{
	Cvar_SetValue (scr_viewsize, scr_viewsize->value-10);
	vid.recalc_refdef = 1;
}

//============================================================================

extern cvar_t *cl_bobmodel;
extern cvar_t *r_shading;
extern cvar_t *s_oldspatial;

// 2001-09-18 New cvar system by Maddes (Init)  start
/*
==================
SCR_Init_Cvars
==================
*/
cvar_t *scr_fov_adapt;
void SCR_Init_Cvars (void)
{
 	scr_fov = Cvar_Get ("fov", "90", CVAR_ORIGINAL);
	scr_viewsize = Cvar_Get ("viewsize", "100", CVAR_ARCHIVE|CVAR_ORIGINAL);
	scr_conspeed = Cvar_Get ("scr_conspeed", "300", CVAR_ORIGINAL);
	scr_showram = Cvar_Get ("showram", "1", CVAR_ORIGINAL);
	scr_showturtle = Cvar_Get ("showturtle", "0", CVAR_ORIGINAL);
	scr_showpause = Cvar_Get ("showpause", "1", CVAR_ORIGINAL);
	scr_centertime = Cvar_Get ("scr_centertime", "2", CVAR_ORIGINAL);
	scr_printspeed = Cvar_Get ("scr_printspeed", "8", CVAR_ORIGINAL);
	scr_fov_adapt = Cvar_Get ("scr_fov_adapt", "1", CVAR_ARCHIVE | CVAR_ORIGINAL);
// 2000-01-12 Variable console height by Fett  start
	scr_conheight = Cvar_Get ("scr_conheight", "0.5", CVAR_ARCHIVE);
	Cvar_SetRangecheck (scr_conheight, Cvar_RangecheckFloat, 0, 1);
	Cvar_Set(scr_conheight, scr_conheight->string);	// do rangecheck
// 2000-01-12 Variable console height by Fett  end

	scr_scale = Cvar_Get ("scr_scale", "2", CVAR_ARCHIVE | CVAR_ORIGINAL);	// leilei - default to 2
	scr_retroscale = Cvar_Get ("scr_retroscale", "1", CVAR_ARCHIVE | CVAR_ORIGINAL);	// leilei - default to 1 since it's fast now.....
	scr_aspectmode = Cvar_Get ("scr_aspectmode", "1", CVAR_ARCHIVE | CVAR_ORIGINAL);
}
// 2001-09-18 New cvar system by Maddes (Init)  end

/*
==================
SCR_Init
==================
*/
void SCR_Init (void)
{

// 2001-09-18 New cvar system by Maddes (Init)  end

//
// register our commands
//
	Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
	Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
	Cmd_AddCommand ("sizedown",SCR_SizeDown_f);


	scr_ram = Draw_PicFromWad ("ram");
	scr_net = Draw_PicFromWad ("net");
	scr_turtle = Draw_PicFromWad ("turtle");

	scr_initialized = true;
}



/*
==============
SCR_DrawRam
==============
*/
void SCR_DrawRam (void)
{
	if (!scr_showram->value)
		return;

	if (!r_cache_thrash)
		return;
if (menu_scaled)
	Draw_Pic_Scaled (scr_vrect.x+32, scr_vrect.y, scr_ram);
else
	Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram);
}

/*
==============
SCR_DrawTurtle
==============
*/
void SCR_DrawTurtle (void)
{
	static int	count;

	if (!scr_showturtle->value)
		return;

// 2001-10-20 TIMESCALE extension by Tomaz/Maddes  start
//	if (host_frametime < 0.1)
	if (host_cpu_frametime < 0.1)
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes  end
	{
		count = 0;
		return;
	}

	count++;
	if (count < 3)
		return;
if (menu_scaled)
	Draw_Pic_Scaled (scr_vrect.x, scr_vrect.y, scr_turtle);
else
	Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle);
}

/*
==============
SCR_DrawNet
==============
*/
void SCR_DrawNet (void)
{
	if (realtime - cl.last_received_message < 0.3)
		return;
	if (cls.demoplayback)
		return;
if (menu_scaled)
	Draw_Pic_Scaled (scr_vrect.x+64, scr_vrect.y, scr_net);
else
	Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net);
}

// 2001-11-31 FPS display by QuakeForge/Muff  start
/*
==============
SCR_DrawFPS
==============
*/
//muff - hacked out of SourceForge implementation + modified
void SCR_DrawFPS (void)
{
	static double lastframetime;
	double t;
	static int lastfps;
	int x, y;
	char st[80];

	if (!cl_showfps->value)
		return;

	t = Sys_FloatTime ();
	if ((t - lastframetime) >= 1.0) {
		lastfps = fps_count;
		fps_count = 0;
		lastframetime = t;
	}

	sprintf(st, "%3d FPS", lastfps);
#ifdef SCALED2D
	if (sb_scaled)
	x = vid.vconwidth - strlen(st) * 16 - 16;
	else
#endif
	x = vid.width - strlen(st) * 16 - 16;
	y = 0 ; 
#ifdef SCALED2D
	if (sb_scaled)
	Draw_String_Scaled(x, y, st);
	else
#endif
	Draw_String(x, y, st);

}
// 2001-11-31 FPS display by QuakeForge/Muff  end

/*
==============
DrawPause
==============
*/
void SCR_DrawPause (void)
{
	qpic_t	*pic;

	if (!scr_showpause->value)		// turn off for screenshots
		return;

	if (!cl.paused)
		return;
	
	pic = Draw_CachePic ("gfx/pause.lmp");
if (menu_scaled)
	Draw_Pic_Scaled ( (vid.vconwidth - pic->width)/2,
		(vid.vconheight - 48 - pic->height)/2, pic);
else
	Draw_Pic ( (vid.width - pic->width)/2,
		(vid.height - 48 - pic->height)/2, pic);
}



/*
==============
SCR_DrawLoading
==============
*/
void SCR_DrawLoading (void)
{
	qpic_t	*pic;

	if (!scr_drawloading)
		return;

	pic = Draw_CachePic ("gfx/loading.lmp");
if (menu_scaled)
	Draw_Pic_Scaled ( (vid.vconwidth - pic->width)/2,
		(vid.vconheight - 48 - pic->height)/2, pic);
else
	Draw_Pic ( (vid.width - pic->width)/2,
		(vid.height - 48 - pic->height)/2, pic);
}



//=============================================================================


/*
==================
SCR_SetUpToDrawConsole
==================
*/
extern cvar_t	*con_alpha;	// 2000-08-04 "Transparent" console background for software renderer by Norberto Alfredo Bensa/Maddes

void SCR_SetUpToDrawConsole (void)
{
	Con_CheckResize ();

	if (scr_drawloading)
		return;		// never a console with loading plaque

// decide on the height of the console
	con_forcedup = !cl.worldmodel || cls.signon != SIGNONS;

	if (con_forcedup)
	{
		scr_conlines = vid.height;		// full screen
		scr_con_current = scr_conlines;
	}
	else if (key_dest == key_console)
// 2000-01-12 Variable console height by Fett/Maddes  start
	{
		scr_conlines = vid.height/2;	// half screen
#ifdef SCALED2D
		if (console_scaled)
			//		scr_conlines = vid.vconheight/2;	// half screen
			scr_conlines = vid.vconheight*scr_conheight->value;	// in-game console
			else
#endif
			scr_conlines = vid.height*scr_conheight->value;	// in-game console

		if (scr_conlines < (3*8+8+8))		// always leave three lines visible (plus command line and border)
		{
			scr_conlines = (3*8+8+8);
		}
		if (scr_conlines >= vid.height)
		{
#ifdef SCALED2D
if (console_scaled)
			scr_conlines = vid.vconheight - 1;	// maximum is full screen
		else
#endif
			scr_conlines = vid.height - 1;	// maximum is full screen
		}
	}
// 2000-01-12 Variable console height by Fett/Maddes  end
	else
		scr_conlines = 0;				// none visible

	if (scr_conlines < scr_con_current)
	{
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes  start
//		scr_con_current -= scr_conspeed->value*host_frametime;
		if (console_scaled)
		scr_con_current -= scr_conspeed->value*host_cpu_frametime * vid.height / vid.vconheight;
		else
		scr_con_current -= scr_conspeed->value*host_cpu_frametime;
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes  end
		if (scr_conlines > scr_con_current)
			scr_con_current = scr_conlines;

	}
	else if (scr_conlines > scr_con_current)
	{
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes  start
//		scr_con_current += scr_conspeed->value*host_frametime;
		if (console_scaled)
		scr_con_current += scr_conspeed->value*host_cpu_frametime  * vid.height / vid.vconheight;
		else
		scr_con_current += scr_conspeed->value*host_cpu_frametime;
// 2001-10-20 TIMESCALE extension by Tomaz/Maddes  end
		if (scr_conlines < scr_con_current)
			scr_con_current = scr_conlines;
	}

	if (clearconsole++ < vid.numpages)
	{
		scr_copytop = 1;
// 2000-08-04 "Transparent" console background for software renderer by Norberto Alfredo Bensa/Maddes  start
		if (con_alpha->value == 1.0)
		{
// 2000-08-04 "Transparent" console background for software renderer by Norberto Alfredo Bensa/Maddes  end
		Draw_TileClear (0,(int)scr_con_current,vid.width, vid.height - (int)scr_con_current);
// 2000-08-04 "Transparent" console background for software renderer by Norberto Alfredo Bensa/Maddes  start
		}
		else
		{
			// Draw full valid screen
			Draw_TileClear (0, 0, vid.width, vid.height );
		}
// 2000-08-04 "Transparent" console background for software renderer by Norberto Alfredo Bensa/Maddes  end
		Sbar_Changed ();
	}
	else if (clearnotify++ < vid.numpages)
	{
		scr_copytop = 1;
		Draw_TileClear (0,0,vid.width, con_notifylines);
	}
	else
		con_notifylines = 0;
}

/*
==================
SCR_DrawConsole
==================
*/
void SCR_DrawConsole (void)
{
	if (scr_con_current)
	{
		scr_copyeverything = 1;
		Con_DrawConsole (scr_con_current, true);
		clearconsole = 0;
	}
	else
	{
		if (key_dest == key_game || key_dest == key_message)
			Con_DrawNotify ();	// only draw notify in game
	}
}


/*
==============================================================================

						SCREEN SHOTS

==============================================================================
*/


typedef struct
{
    char	manufacturer;
    char	version;
    char	encoding;
    char	bits_per_pixel;
    unsigned short	xmin,ymin,xmax,ymax;
    unsigned short	hres,vres;
    unsigned char	palette[48];
    char	reserved;
    char	color_planes;
    unsigned short	bytes_per_line;
    unsigned short	palette_type;
    char	filler[58];
    unsigned char	data;			// unbounded
} pcx_t;

/*
==============
WritePCXfile
==============
*/
void WritePCXfile (char *filename, byte *data, int width, int height,
	int rowbytes, byte *palette)
{
	int		i, j, length;
	pcx_t	*pcx;
	byte		*pack;

	pcx = Hunk_TempAlloc (width*height*2+1000);
	if (pcx == NULL)
	{
		Con_Printf("SCR_ScreenShot_f: not enough memory\n");
		return;
	}

	pcx->manufacturer = 0x0a;	// PCX id
	pcx->version = 5;			// 256 color
 	pcx->encoding = 1;		// uncompressed
	pcx->bits_per_pixel = 8;		// 256 color
	pcx->xmin = 0;
	pcx->ymin = 0;
	pcx->xmax = LittleShort((short)(width-1));
	pcx->ymax = LittleShort((short)(height-1));
	pcx->hres = LittleShort((short)width);
	pcx->vres = LittleShort((short)height);
	Q_memset (pcx->palette,0,sizeof(pcx->palette));
	pcx->color_planes = 1;		// chunky image
	pcx->bytes_per_line = LittleShort((short)width);
	pcx->palette_type = LittleShort(2);		// not a grey scale
	Q_memset (pcx->filler,0,sizeof(pcx->filler));

// pack the image
	pack = &pcx->data;

	for (i=0 ; i<height ; i++)
	{
		for (j=0 ; j<width ; j++)
		{
			if ( (*data & 0xc0) != 0xc0)
				*pack++ = *data++;
			else
			{
				*pack++ = 0xc1;
				*pack++ = *data++;
			}
		}

		data += rowbytes - width;
	}

// write the palette
	*pack++ = 0x0c;	// palette ID byte
	for (i=0 ; i<768 ; i++)
		*pack++ = *palette++;

// write output file
	length = pack - (byte *)pcx;
	COM_WriteFile (filename, pcx, length);
}
#ifdef _WIN32
extern unsigned char	vid_curpal[256*3];
#endif
/*
==================
SCR_ScreenShot_f
==================
*/
void SCR_ScreenShot_f (void)
{
	int		i;
	char	pcxname[80];
	char	pcxnametwo[80];
	char	checkname[MAX_OSPATH];

//
// find a file name to save it to
//
	strcpy(pcxname,"quake00.pcx");

	for (i=0 ; i<=99 ; i++)
	{
		pcxname[5] = i/10 + '0';
		pcxname[6] = i%10 + '0';
		sprintf (checkname, "%s/%s", com_gamedir, pcxname);
		if (Sys_FileTime(checkname) == -1)
			break;	// file doesn't exist
	}
	if (i==100)
	{
		Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX file\n");
		return;
 	}

//
// save the pcx file
//
	D_EnableBackBufferAccess ();	// enable direct drawing of console to back
									//  buffer


#ifdef _WIN32
	WritePCXfile (pcxname, vid.buffer, vid.width, vid.height, vid.rowbytes,
				  vid_curpal);
	
#else
	WritePCXfile (pcxname, vid.buffer, vid.width, vid.height, vid.rowbytes,
				  host_basepal);
#endif

	D_DisableBackBufferAccess ();	// for adapters that can't stay mapped in
									//  for linear writes all the time

	Con_Printf ("Wrote %s\n", pcxname);
}


//=============================================================================


/*
===============
SCR_BeginLoadingPlaque

================
*/
void SCR_BeginLoadingPlaque (void)
{
	S_StopAllSounds (true);

	if (cls.state != ca_connected)
		return;
	if (cls.signon != SIGNONS)
		return;

// redraw with no console and the loading plaque
	Con_ClearNotify ();
	scr_centertime_off = 0;
	scr_con_current = 0;

	scr_drawloading = true;
	scr_fullupdate = 0;
	Sbar_Changed ();
	SCR_UpdateScreen ();
	scr_drawloading = false;

	scr_disabled_for_loading = true;
	scr_disabled_time = realtime;
	scr_fullupdate = 0;
}

/*
===============
SCR_EndLoadingPlaque

================
*/
void SCR_EndLoadingPlaque (void)
{
	scr_disabled_for_loading = false;
	scr_fullupdate = 0;
	Con_ClearNotify ();
}

//=============================================================================

char	*scr_notifystring;
qboolean	scr_drawdialog;

void SCR_DrawNotifyString (void)
{
	char	*start;
	int		l;
	int		j;
	int		x, y;

	start = scr_notifystring;
#ifdef SCALED2D
	if (menu_scaled)
	y = vid.vconheight*0.35;


	else
#endif
	y = vid.height*0.35;

	do
	{
	// scan the width of the line
		for (l=0 ; l<40 ; l++)
			if (start[l] == '\n' || !start[l])
				break;
		
#ifdef SCALED2D
		if (menu_scaled){		// leilei - we consider this part of the menu isntead
			x = (vid.vconwidth - l*8)/2;
		for (j=0 ; j<l ; j++, x+=8)
			Draw_Character_Scaled (x, y, start[j]);
		}
		else
#endif
			
		{
			x = (vid.width - l*8)/2;
		for (j=0 ; j<l ; j++, x+=8)
			Draw_Character (x, y, start[j]);
		}

		y += 8;

		while (*start && *start != '\n')
			start++;

		if (!*start)
			break;
		start++;		// skip the \n
	} while (1);
}

/*
==================
SCR_ModalMessage

Displays a text string in the center of the screen and waits for a Y or N
keypress.
==================
*/
int SCR_ModalMessage (char *text)
{
	if (cls.state == ca_dedicated)
		return true;

	scr_notifystring = text;

// draw a fresh screen
	scr_fullupdate = 0;
	scr_drawdialog = true;
	SCR_UpdateScreen ();
	scr_drawdialog = false;

	S_ClearBuffer ();		// so dma doesn't loop current sound

	do
	{
		key_count = -1;		// wait for a key down and up
		Sys_SendKeyEvents ();
	} while (key_lastpress != 'y' && key_lastpress != 'n' && key_lastpress != K_ESCAPE);

	scr_fullupdate = 0;
	SCR_UpdateScreen ();

	return key_lastpress == 'y';
}


//=============================================================================

/*
===============
SCR_BringDownConsole

Brings the console down and fades the palettes back to normal
================
*/
void SCR_BringDownConsole (void)
{
	int		i;

	scr_centertime_off = 0;

	for (i=0 ; i<20 && scr_conlines != scr_con_current ; i++)
		SCR_UpdateScreen ();

	cl.cshifts[0].percent = 0;		// no area contents palette on next frame
	VID_SetPalette (host_basepal);
}

extern int nolookups;
extern float oldwateralpha;
extern float newwateralpha;
extern cvar_t *r_wateralpha;
extern cvar_t *r_waterblend;
extern cvar_t *r_shadowhack;
extern cvar_t *r_dynamic;
extern cvar_t *r_tranquality;
int oldwaterblend;
int oldtranquality;
int dyncolor;
extern unsigned int shadowhack;
void R_ApplyFog (void);
void R_ApplyDof (void);


/*
==================
SCR_UpdateScreen

This is called every frame, and can also be called explicitly to flush
text to the screen.

WARNING: be very careful calling this from elsewhere, because the refresh
needs almost the entire 256k of stack space!
==================
*/
extern cvar_t *r_fogquality;
extern cvar_t *r_waterquality;
int	waterinsight;
extern client_static_t	clssplit;
extern client_state_t	clsplit;


void SCR_ReallyRender (void)
{
#ifdef WATERREFLECTIONS
	if(waterinsight && reflectavailable && r_waterquality->value > 1 && !r_dowarp && !r_docrap){
	reflectpass = 1;



	V_RenderView ();
	reflectpass = 0;
	
	V_RenderView ();
	}
	else
	{
	
	reflectpass = 0;
	V_RenderView ();
	}
#else
		reflectpass = 0;
	V_RenderView ();
#endif
};
int		dontevendraw;
void SCR_UpdateScreen (void)
{
	static float	oldscr_viewsize;
	static float	oldlcd_x;
	static float	oldv_detail;
	vrect_t		vrect;
	
	if (scr_skipupdate || block_drawing)
		return;
	
	scr_copytop = 0;
	scr_copyeverything = 0;

	if (fogenabled && r_fogquality->value){
		foguse2 = 0;foguse = 2;}
	else if (fogenabled && !r_fogquality->value){
		foguse = 1;foguse2 = 0;}
	else if (!fogenabled){
		foguse = 0; foguse2 = 0;}

	if (scr_disabled_for_loading)
	{
		if (realtime - scr_disabled_time > 60)
		{
			scr_disabled_for_loading = false;
			Con_Printf ("load failed.\n");
		}
		else
			return;
	}

	if (cls.state == ca_dedicated)
		return;				// stdout only

	if (!scr_initialized || !con_initialized)
		return;				// not initialized yet

	if (scr_viewsize->value != oldscr_viewsize)
	{
		oldscr_viewsize = scr_viewsize->value;
		vid.recalc_refdef = 1;
	}

//
// check for vid changes
//
	if (oldfov != scr_fov->value)
	{
		oldfov = scr_fov->value;
		vid.recalc_refdef = true;
	}

	if (oldlcd_x != lcd_x->value)
	{
		oldlcd_x = lcd_x->value;
		vid.recalc_refdef = true;
	}

		if (oldv_detail != v_detail->value)
	{
		oldv_detail = v_detail->value;
		vid.recalc_refdef = true;
	}
	if (oldscreensize != scr_viewsize->value)
	{
		oldscreensize = scr_viewsize->value;
		vid.recalc_refdef = true;
	}

		if (oldsbar != cl_sbar->value)
	{
		oldsbar = cl_sbar->value;
		vid.recalc_refdef = true;
	}
	if (!reflectpass){
	if (vid.recalc_refdef)
	{
	// something changed, so reorder the screen
		SCR_CalcRefdef ();
	}

//
// do 3D refresh drawing, and then update the screen
//
	D_EnableBackBufferAccess ();	// of all overlay stuff if drawing directly
	
	if (scr_fullupdate++ < vid.numpages)
	{	// clear the entire screen
		scr_copyeverything = 1;
		Draw_TileClear (0,0,vid.width,vid.height);
		Sbar_Changed ();
	}

	pconupdate = NULL;
}
	
	SCR_SetUpToDrawConsole ();
	SCR_EraseCenterString ();

	D_DisableBackBufferAccess ();	// for adapters that can't stay mapped in
														//  for linear writes all the time

//	Draw_FadeScreen ();

	VID_LockBuffer ();
//	Draw_GetShroomedPT1();	

	
	// leilei - water reflections need two passes
	// we all start it here!
	// TODO- mirror chain	
	splitpass = 0;
	if (!dontevendraw)
	SCR_ReallyRender();
	if(splitmeup){
		client_t	oldcle;
		client_state_t oldcl;
		//*oldcl = cl;
		oldcl = cl;
		//cl = clsplit;
		splitpass = 1;
		if (!dontevendraw)
		SCR_ReallyRender();
	}
	VID_UnlockBuffer ();

	D_EnableBackBufferAccess ();	// of all overlay stuff if drawing directly


	// stupid place to put this
	//if(oldwateralpha != newwateralpha){
	//	oldwateralpha = newwateralpha = r_wateralpha->value;
	//	if(newwateralpha < 1)	WaterTableGet();
	//}

	
	// leilei - make a new alpha tables.
	if (!nolookups){
	if(oldwateralpha != r_wateralpha->value || (oldtranquality != (int)r_tranquality->value) || (oldwaterblend != (int)r_waterblend->value)){
		oldtranquality = r_tranquality->value;
		oldwateralpha = r_wateralpha->value;
		oldwaterblend = r_waterblend->value;
		WaterTableGet();
	}
	
	}

	if(retroscalefactor != scr_retroscale->value){
		
		SCR_CvarCheck();
		
	}
	

	else if(scalefactor != scr_scale->value && !scr_retroscale->value){
		
		SCR_CvarCheck();
		
	}
	


	if (dyncolor != r_coloreddyns->value){

		dyncolor = r_coloreddyns->value;
	}

	if (shadowhack != r_shadowhack->value){

		shadowhack = r_shadowhack->value;
	}
	if (r_dynamic->value)
		dynlightenabled = 1;
	else
		dynlightenabled = 0;
//	Draw_GetShroomedPT2();			
	if (r_tinge->value == 3)
	Draw_DudeScreen ();
	else if (r_tinge->value == 2)
		Draw_DudeScreen66 ();
	else if (r_tinge->value == 1)
		Draw_DudeScreen33 ();
	else if (r_tinge->value == -1)
		Draw_DudeScreen33A ();
	else if (r_tinge->value == -2)
		Draw_DudeScreen66A ();
	
		//Draw_Bloom ();
	/*if (cl.paused || scr_drawloading || scr_drawdialog){
			dontevendraw = 1;
			Draw_Something ();
			
				Sbar_Draw ();
				
	}
	else
		dontevendraw = 0;
		*/

	if (scr_drawdialog)
	{
		Sbar_Draw ();
		Draw_FadeScreen ();
		SCR_DrawNotifyString ();
		scr_copyeverything = true;
	}

	else if (scr_drawloading)
	{
		//Draw_FadeScreen ();	// this WAS cool, but... i'll have to make a whole transition thing first
		SCR_DrawLoading ();
		Sbar_Draw ();
	}

	else if (cl.intermission == 1 && key_dest == key_game)
	{
		Sbar_IntermissionOverlay ();
	}
	else if (cl.intermission == 2 && key_dest == key_game)
	{
		Sbar_FinaleOverlay ();
		SCR_CheckDrawCenterString ();
	}
	else if (cl.intermission == 3 && key_dest == key_game)
	{
		SCR_CheckDrawCenterString ();
	}
	else
	{
		SCR_DrawRam ();
		SCR_DrawNet ();
		SCR_DrawTurtle ();
		SCR_DrawFPS ();	// 2001-11-31 FPS display by QuakeForge/Muff
		SCR_DrawPause ();
		SCR_CheckDrawCenterString ();
		Sbar_Draw ();
		SCR_DrawConsole ();
    	
		M_Draw ();
	}

	D_DisableBackBufferAccess ();	// for adapters that can't stay mapped in
									//  for linear writes all the time
	if (pconupdate)
	{
		D_UpdateRects (pconupdate);
	}

	V_UpdatePalette ();

//
// update one of three areas
//
#ifdef SCALED2D
	if (sb_scaled)
	sb_what_lines = sb_lines * (vid.height / vid.vconheight); // leilei - refdef hack fix
	else
#endif
//		if (!reflectpass)
		sb_what_lines = sb_lines;
//		else
//		sb_what_lines = 0;
	if (scr_copyeverything)
	{
		vrect.x = 0;
		vrect.y = 0;
		vrect.width = vid.width;
		vrect.height = vid.height;
		vrect.pnext = 0;

		VID_Update (&vrect);
	}
	else if (scr_copytop)
	{
		vrect.x = 0;
		vrect.y = 0;
		vrect.width = vid.width;
		vrect.height = vid.height - sb_lines;
		vrect.pnext = 0;

		VID_Update (&vrect);
	}
	else
	{
		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);
	}
	
}


/*
==================
SCR_UpdateWholeScreen
==================
*/
void SCR_UpdateWholeScreen (void)
{
	scr_fullupdate = 0;
	SCR_UpdateScreen ();
}
