/*
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.

*/

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "quakedef.h"
#include "gl_local.h"
#include "version.h"
#include "sbar.h"

#include "image.h"
#include "utils.h"

static unsigned char drawgl_inited;

extern cvar_t crosshair, cl_crossx, cl_crossy, crosshaircolor, crosshairsize;
extern cvar_t scr_coloredText;

cvar_t	scr_conalpha		= {"scr_conalpha", "0.8"};
cvar_t	scr_menualpha		= {"scr_menualpha", "0.7"};


qboolean OnChange_gl_crosshairimage(cvar_t *, char *);
cvar_t	gl_crosshairimage   = {"crosshairimage", "", 0, OnChange_gl_crosshairimage};

qboolean OnChange_gl_consolefont (cvar_t *, char *);
cvar_t	gl_consolefont		= {"gl_consolefont", "original", 0, OnChange_gl_consolefont};

cvar_t	gl_crosshairalpha	= {"crosshairalpha", "1"};


qboolean OnChange_gl_smoothfont (cvar_t *var, char *string);
cvar_t gl_smoothfont = {"gl_smoothfont", "0", 0, OnChange_gl_smoothfont};

byte			*draw_chars;						// 8*8 graphic characters
mpic_t			*draw_disc;
static mpic_t	*draw_backtile;

static int		translate_texture;
static int		char_texture;

static mpic_t	conback;


#define		NUMCROSSHAIRS 6
int			crosshairtextures[NUMCROSSHAIRS];
int			crosshairtexture_txt;
mpic_t		crosshairpic;

static byte customcrosshairdata[64];

#define CROSSHAIR_NONE	0
#define CROSSHAIR_TXT	1
#define CROSSHAIR_IMAGE	2
static int customcrosshair_loaded = CROSSHAIR_NONE;


static byte crosshairdata[NUMCROSSHAIRS][64] =
{
	{
		0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
		0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff,
		0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	},

	{
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	},

	{
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	},

	{
		0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
		0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff,
		0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff,
		0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff,
		0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
	},

	{
		0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 
		0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 
		0xfe, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xfe, 0xff, 
		0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 
		0xfe, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xfe, 0xff, 
		0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 
		0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	},
	
	{
		0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
		0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 
		0xfe, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfe, 0xff, 
		0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
		0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	},
};

static void SetupFontSmoothing()
{
	if (!char_texture)
		return;

	GL_Bind(char_texture);

	if (gl_smoothfont.value)
	{
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	}
	else
	{
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	}
}

qboolean OnChange_gl_smoothfont (cvar_t *var, char *string)
{
	if (drawgl_inited)
	{
		var->value = Q_atof(string);

		SetupFontSmoothing();
	}

	return false;
}

static void Crosshair_LoadImage(const char *s)
{
	mpic_t *pic;

	if (!s[0])
	{
		customcrosshair_loaded &= ~CROSSHAIR_IMAGE;
		return;
	}

#warning This leaks memory if the cvar is changed, yes?
	if (!(pic = GL_LoadPicImage(va("crosshairs/%s", s), "crosshair", 0, 0, TEX_ALPHA)))
	{
		customcrosshair_loaded &= ~CROSSHAIR_IMAGE;
		Com_Printf("Couldn't load image %s\n", s);
		return;
	}

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	crosshairpic = *pic;
	customcrosshair_loaded |= CROSSHAIR_IMAGE;

}

qboolean OnChange_gl_crosshairimage(cvar_t *v, char *s)
{
	if (drawgl_inited)
		Crosshair_LoadImage(s);

	return false;
}

void customCrosshair_Init(void)
{
	FILE *f;
	int i = 0, c;

	customcrosshair_loaded = CROSSHAIR_NONE;

	if (FS_FOpenFile("crosshairs/crosshair.txt", &f) == -1)
		return;

	while (i < 64)
	{
		c = fgetc(f);
		if (c == EOF)
		{
			Com_Printf("Invalid format in crosshair.txt (Need 64 X's and O's)\n");	
			fclose(f);
			return;
		}
		if (isspace(c))
			continue;
		if (tolower(c) != 'x' && tolower(c) != 'o')
		{
			Com_Printf("Invalid format in crosshair.txt (Only X's and O's and whitespace permitted)\n");
			fclose(f);
			return;
		}		
		customcrosshairdata[i++] = (c == 'x' || c  == 'X') ? 0xfe : 0xff;
	}
	fclose(f);
	crosshairtexture_txt = GL_LoadTexture ("", 8, 8, customcrosshairdata, TEX_ALPHA, 1);
	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	customcrosshair_loaded |= CROSSHAIR_TXT;

	Crosshair_LoadImage(gl_crosshairimage.string);
}


/*
=============================================================================
  scrap allocation

  Allocate all the little status bar objects into a single texture
  to crutch up stupid hardware / drivers
=============================================================================
*/

#warning My God, this sucks!

// some cards have low quality of alpha pics, so load the pics
// without transparent pixels into a different scrap block.
// scrap 0 is solid pics, 1 is transparent
#define	MAX_SCRAPS		2
#define	BLOCK_WIDTH		256
#define	BLOCK_HEIGHT	256

int			scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH];
byte		scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT*4];
int			scrap_dirty;	// bit mask
int			scrap_texnum;

void Scrap_Init()
{
	bzero(scrap_allocated, sizeof(scrap_allocated));
	scrap_dirty = 0;

	scrap_texnum = texture_extension_number;
	texture_extension_number += MAX_SCRAPS;
}

// returns false if allocation failed
qboolean Scrap_AllocBlock(int scrapnum, int w, int h, int *x, int *y)
{
	int i, j, best, best2;

	best = BLOCK_HEIGHT;
	
	for (i = 0; i < BLOCK_WIDTH - w; i++)
	{
		best2 = 0;
		
		for (j = 0; j < w; j++)
		{
			if (scrap_allocated[scrapnum][i + j] >= best)
				break;
			if (scrap_allocated[scrapnum][i + j] > best2)
				best2 = scrap_allocated[scrapnum][i + j];
		}
		if (j == w)
		{	// this is a valid spot
			*x = i;
			*y = best = best2;
		}
	}
	
	if (best + h > BLOCK_HEIGHT)
		return false;
	
	for (i = 0; i < w; i++)
		scrap_allocated[scrapnum][*x + i] = best + h;

	scrap_dirty |= (1 << scrapnum);

	return true;
}

int scrap_uploads;

void Scrap_Upload(void)
{
	int i;

	scrap_uploads++;
	for (i = 0; i < 2 ; i++)
	{
		if (!(scrap_dirty & (1 << i)))
			continue;
		scrap_dirty &= ~(1 << i);
		GL_Bind(scrap_texnum + i);
		GL_Upload8(scrap_texels[i], BLOCK_WIDTH, BLOCK_HEIGHT, TEX_ALPHA);
	}
}

//=============================================================================
/* Support Routines */

typedef struct cachepic_s
{
	char		name[MAX_QPATH];
	mpic_t		pic;
} cachepic_t;

#define	MAX_CACHED_PICS		128
cachepic_t	cachepics[MAX_CACHED_PICS];
int			numcachepics;

byte	menuplyr_pixels[4096];

int		pic_texels;

mpic_t *Draw_CacheWadPic (char *name)
{
	qpic_t	*p;
	mpic_t	*pic, *pic_24bit;

	p = W_GetLumpName (name);
	pic = (mpic_t *)p;

	pic = malloc(sizeof(*pic));
	if (pic)
	{
		memcpy(pic, p, sizeof(*pic));

		if ((pic_24bit = GL_LoadPicImage(va("textures/wad/%s", name), name, 0, 0, TEX_ALPHA))
		 || (pic_24bit = GL_LoadPicImage(va("gfx/%s", name), name, 0, 0, TEX_ALPHA)))
		{
			memcpy(&pic->texnum, &pic_24bit->texnum, sizeof(mpic_t) - 8);
			return pic;
		}

		// load little ones into the scrap
		if (p->width < 64 && p->height < 64)
		{
			int x, y, i, j, k, texnum;

			texnum = memchr(p->data, 255, p->width*p->height) != NULL;
			if (!Scrap_AllocBlock (texnum, p->width, p->height, &x, &y))
			{
				GL_LoadPicTexture (name, pic, p->data);
				return pic;
			}
			k = 0;
			for (i = 0; i < p->height; i++)
			{
				for (j  = 0; j < p->width; j++, k++)
				{
					scrap_texels[texnum][(y + i) * BLOCK_WIDTH + x + j] = p->data[k];
				}
			}

			texnum += scrap_texnum;
			pic->texnum = texnum;
			pic->sl = (x + 0.01) / (float) BLOCK_WIDTH;
			pic->sh = (x + p->width - 0.01) / (float) BLOCK_WIDTH;
			pic->tl = (y + 0.01) / (float) BLOCK_WIDTH;
			pic->th = (y + p->height - 0.01) / (float) BLOCK_WIDTH;

			pic_texels += p->width * p->height;
		}
		else
		{
			GL_LoadPicTexture (name, pic, p->data);
		}
	}

	return pic;
}

void Draw_FreeWadPic(mpic_t *pic)
{
	free(pic);
}

mpic_t *Draw_CachePic(char *path)
{
	cachepic_t *pic;
	int i;
	qpic_t *dat;
	mpic_t *pic_24bit;

	for (pic = cachepics, i = 0; i < numcachepics; pic++, i++)
		if (!strcmp(path, pic->name))
			return &pic->pic;

	if (numcachepics == MAX_CACHED_PICS)
		Sys_Error("numcachepics == MAX_CACHED_PICS");
	numcachepics++;
	Q_strncpyz(pic->name, path, sizeof(pic->name));

	// load the pic from disk
	if (!(dat = (qpic_t *)FS_LoadMallocFile(path)))
		Sys_Error("Draw_CachePic: failed to load %s", path);
	SwapPic(dat);

	// HACK HACK HACK --- we need to keep the bytes for
	// the translatable player picture just for the menu
	// configuration dialog
	if (!strcmp(path, "gfx/menuplyr.lmp"))
		memcpy(menuplyr_pixels, dat->data, dat->width*dat->height);

	pic->pic.width = dat->width;
	pic->pic.height = dat->height;

	
	if ((pic_24bit = GL_LoadPicImage(path, NULL, 0, 0, TEX_ALPHA)))
		memcpy(&pic->pic.texnum, &pic_24bit->texnum, sizeof(mpic_t) - 8);
	else
		GL_LoadPicTexture(path, &pic->pic, dat->data);

	free(dat);

	return &pic->pic;
}

void GL_FlushPics()
{
		  numcachepics = 0;
}

void Draw_InitConback(void)
{
	qpic_t *cb;
	mpic_t *pic_24bit;

	if ((pic_24bit = GL_LoadPicImage("gfx/conback", "conback", 0, 0, 0)))
	{
		memcpy(&conback.texnum, &pic_24bit->texnum, sizeof(mpic_t) - 8);
	}
	else
	{
		if (!(cb = (qpic_t *) FS_LoadZFile("gfx/conback.lmp")))
			Sys_Error ("Couldn't load gfx/conback.lmp");

		SwapPic (cb);

		if (cb->width != 320 || cb->height != 200)
		{
			Z_Free(cb);
			Sys_Error ("Draw_InitConback: conback.lmp size is not 320x200");
		}

		conback.width = cb->width;
		conback.height = cb->height;
		GL_LoadPicTexture ("conback", &conback, cb->data);

		Z_Free(cb);
	}

	conback.width = vid.conwidth;
	conback.height = vid.conheight;
}

void Draw_SizeChanged()
{
	conback.width = vid.conwidth;
	conback.height = vid.conheight;
}

static int Draw_LoadCharset(char *name)
{
	int texnum;

	if (!Q_strcasecmp(name, "original"))
	{
		int i;
		char buf[128 * 256], *src, *dest;

		memset (buf, 255, sizeof(buf));
		src = draw_chars;
		dest = buf;
		for (i = 0; i < 16; i++)
		{
			memcpy (dest, src, 128 * 8);
			src += 128 * 8;
			dest += 128 * 8 * 2;
		}
		char_texture = GL_LoadTexture ("pic:charset", 128, 256, buf, TEX_ALPHA, 1);
	}
	else if ((texnum = GL_LoadCharsetImage (va("textures/charsets/%s", name), "pic:charset")))
	{
		char_texture = texnum;
	}
	else
	{
		Com_Printf ("Couldn't load charset \"%s\"\n", name);
		return 1;
	}

	SetupFontSmoothing();

	return 0;
}

qboolean OnChange_gl_consolefont(cvar_t *var, char *string)
{
	if (drawgl_inited)
		return Draw_LoadCharset(string);

	return 0;
}

void Draw_LoadCharset_f (void)
{
	switch (Cmd_Argc())
	{
		case 1:
			Com_Printf("Current charset is \"%s\"\n", gl_consolefont.string);
			break;
		case 2:
			Cvar_Set(&gl_consolefont, Cmd_Argv(1));
			break;
		default:
			Com_Printf("Usage: %s <charset>\n", Cmd_Argv(0));
			break;
	}
}

void Draw_InitCharset(void)
{
	int i;

	draw_chars = W_GetLumpName ("conchars");
	for (i = 0; i < 256 * 64; i++)
	{
		if (draw_chars[i] == 0)
			draw_chars[i] = 255;
	}

	Draw_LoadCharset(gl_consolefont.string);

	if (!char_texture)
	{
		Cvar_Set(&gl_consolefont, "original");
		Draw_LoadCharset(gl_consolefont.string);
	}

	if (!char_texture)	
		Sys_Error("Draw_InitCharset: Couldn't load charset");
}

void Draw_CvarInit(void)
{
	Cmd_AddCommand("loadcharset", Draw_LoadCharset_f);	

	Cvar_SetCurrentGroup(CVAR_GROUP_CONSOLE);
	Cvar_Register (&scr_conalpha);
	Cvar_Register (&gl_smoothfont);
	Cvar_Register (&gl_consolefont);	

	Cvar_SetCurrentGroup(CVAR_GROUP_SCREEN);
	Cvar_Register (&scr_menualpha);

	Cvar_SetCurrentGroup(CVAR_GROUP_CROSSHAIR);
	Cvar_Register (&gl_crosshairimage);	
	Cvar_Register (&gl_crosshairalpha);

	Cvar_ResetCurrentGroup();

	GL_Texture_CvarInit();
}

void Draw_InitGL(void)
{
	int i;

	// load the console background and the charset by hand, because we need to write the version
	// string into the background before turning it into a texture
	Draw_InitCharset ();
	Draw_InitConback ();

	// Load the crosshair pics
	for (i = 0; i < NUMCROSSHAIRS; i++)
	{
		crosshairtextures[i] = GL_LoadTexture ("", 8, 8, crosshairdata[i], TEX_ALPHA, 1);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	}
	customCrosshair_Init();		
	// get the other pics we need
	draw_disc = Draw_CacheWadPic ("disc");
	draw_backtile = Draw_CacheWadPic ("backtile");

	drawgl_inited = 1;
}

void Draw_ShutdownGL()
{
	drawgl_inited = 0;
}

void Draw_Init(void)
{
	// save a texture slot for translated picture
	translate_texture = texture_extension_number++;

	// save slots for scraps
	Scrap_Init();
}

__inline static void Draw_CharPoly(int x, int y, int num)
{
	float frow, fcol;

	frow = (num >> 4) * 0.0625;
	fcol = (num & 15) * 0.0625;

	glTexCoord2f (fcol, frow);
	glVertex2f (x, y);
	glTexCoord2f (fcol + 0.0625, frow);
	glVertex2f (x + 8, y);
	glTexCoord2f (fcol + 0.0625, frow + 0.03125);
	glVertex2f (x + 8, y + 8);
	glTexCoord2f (fcol, frow + 0.03125);
	glVertex2f (x, y + 8);
}

static int textrenderingenabled;

#define NUMBUFFEREDTEXTVERTICES 64
#if (NUMBUFFEREDTEXTVERTICES%4) != 0
#error Fail.
#endif

static float fontvertices[2*NUMBUFFEREDTEXTVERTICES] __attribute__((aligned(64)));
static float fonttexcoords[2*NUMBUFFEREDTEXTVERTICES] __attribute__((aligned(64)));
static int fontindex;

static void Draw_CharPolyArray(int x, int y, int num)
{
	int index;

	float frow, fcol;

	frow = (num >> 4) * 0.0625;
	fcol = (num & 15) * 0.0625;

	index = fontindex;

	fontvertices[index++] = x;
	fontvertices[index++] = y;

	fontvertices[index++] = x + 8;
	fontvertices[index++] = y;

	fontvertices[index++] = x + 8;
	fontvertices[index++] = y + 8;

	fontvertices[index++] = x;
	fontvertices[index++] = y + 8;

	index = fontindex;

	fonttexcoords[index++] = fcol;
	fonttexcoords[index++] = frow;

	fonttexcoords[index++] = fcol + 0.0625;
	fonttexcoords[index++] = frow;

	fonttexcoords[index++] = fcol + 0.0625;
	fonttexcoords[index++] = frow + 0.03125;

	fonttexcoords[index++] = fcol;
	fonttexcoords[index++] = frow + 0.03125;

	fontindex += 8;

	if (fontindex >= NUMBUFFEREDTEXTVERTICES*2)
	{
		glDrawArrays(GL_QUADS, 0, fontindex/2);
		fontindex = 0;
	}
}

//Draws one 8*8 graphics character with 0 being transparent.
//It can be clipped to the top of the screen to allow the console to be smoothly scrolled off.
void Draw_Character(int x, int y, int num)
{

	if (y <= -8)
		return;			// totally off screen

	if (num == 32)
		return;		// space

	num &= 255;


	GL_Bind(char_texture);

	glBegin(GL_QUADS);
	Draw_CharPoly(x, y, num);
	glEnd();
}

void Draw_BeginTextRendering()
{
	if (!textrenderingenabled)
	{
		GL_Bind(char_texture);

		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glVertexPointer(2, GL_FLOAT, 0, fontvertices);
		glTexCoordPointer(2, GL_FLOAT, 0, fonttexcoords);

		fontindex = 0;
	}

	textrenderingenabled++;
}

void Draw_EndTextRendering()
{
	textrenderingenabled--;

	if (!textrenderingenabled)
	{
		if (fontindex)
			glDrawArrays(GL_QUADS, 0, fontindex/2);

		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glDisableClientState(GL_VERTEX_ARRAY);
	}
}

void Draw_String(int x, int y, const char *str)
{
	int num;

	if (y <= -8)
		return;			// totally off screen

	if (!*str)
		return;

	Draw_BeginTextRendering();

	while (*str) {	// stop rendering when out of characters		
		if ((num = (unsigned char)(*str++)) != 32)	// skip spaces
			Draw_CharPolyArray(x, y, num);

		x += 8;
	}

	Draw_EndTextRendering();
}

void Draw_String_Length(int x, int y, const char *str, int len)
{
	int num;
	int disabletextrendering;

	if (y <= -8)
		return;			// totally off screen

	if (!*str)
		return;

	Draw_BeginTextRendering();

	while(len--)
	{
		if ((num = (unsigned char)(*str++)) != 32)	// skip spaces
			Draw_CharPolyArray(x, y, num);

		x += 8;
	}

	Draw_EndTextRendering();
}

void Draw_Alt_String(int x, int y, const char *str)
{
	int num;

	if (y <= -8)
		return;			// totally off screen

	if (!*str)
		return;

	GL_Bind(char_texture);

	glBegin(GL_QUADS);

	while (*str) {// stop rendering when out of characters
		if ((num = *str++ | 128) != (32 | 128))	// skip spaces
			Draw_CharPoly(x, y, num);

		x += 8;
	}

	glEnd();
}


static int HexToInt(char c)
{
	if (isdigit(c))
		return c - '0';
	else if (c >= 'a' && c <= 'f')
		return 10 + c - 'a';
	else if (c >= 'A' && c <= 'F')
		return 10 + c - 'A';
	else
		return -1;
}

void Draw_ColoredString (int x, int y, char *text, int red)
{
	int r, g, b, num;
	qboolean white = true;

	if (y <= -8)
		return;			// totally off screen

	if (!*text)
		return;

	GL_Bind(char_texture);

	if (scr_coloredText.value)
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glBegin(GL_QUADS);

	for ( ; *text; text++)
	{
		if (*text == '&')
		{
			if (text[1] == 'c' && text[2] && text[3] && text[4])
			{
				r = HexToInt(text[2]);
				g = HexToInt(text[3]);
				b = HexToInt(text[4]);
				if (r >= 0 && g >= 0 && b >= 0)
				{
					if (scr_coloredText.value)
					{
						glColor3f(r / 16.0, g / 16.0, b / 16.0);
						white = false;
					}
					text += 4;
				}
				continue;
			}
			else if (text[1] == 'r')
			{
				if (!white)
				{
					glColor3ubv(color_white);
					white = true;
				}
				text += 1;
				continue;
			}
		}

		num = *text & 255;
		if (!scr_coloredText.value && red)
			num |= 128;

		if (num != 32 && num != (32 | 128))
			Draw_CharPoly(x, y, num);

		x += 8;
	}

	glEnd();

	if (!white)
		glColor3ubv(color_white);

	if (scr_coloredText.value)
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}


void Draw_Crosshair(void)
{
	float x, y, ofs1, ofs2, sh, th, sl, tl;
	byte *col;
	extern vrect_t scr_vrect;

	if ((crosshair.value >= 2 && crosshair.value <= NUMCROSSHAIRS + 1)
	 || ((customcrosshair_loaded & CROSSHAIR_TXT) && crosshair.value == 1)
	 || (customcrosshair_loaded & CROSSHAIR_IMAGE))
	{
		x = scr_vrect.x + scr_vrect.width / 2 + cl_crossx.value; 
		y = scr_vrect.y + scr_vrect.height / 2 + cl_crossy.value;

		if (!gl_crosshairalpha.value)
			return;

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		col = StringToRGB(crosshaircolor.string);

		if (gl_crosshairalpha.value)
		{
			glDisable(GL_ALPHA_TEST);
			glEnable (GL_BLEND);
			col[3] = bound(0, gl_crosshairalpha.value, 1) * 255;
			glColor4ubv (col);
		}
		else
		{
			glColor3ubv (col);
		}


		if (customcrosshair_loaded & CROSSHAIR_IMAGE)
		{
			GL_Bind (crosshairpic.texnum);
			ofs1 = 4 - 4.0 / crosshairpic.width;
			ofs2 = 4 + 4.0 / crosshairpic.width;
			sh = crosshairpic.sh;
			sl = crosshairpic.sl;
			th = crosshairpic.th;
			tl = crosshairpic.tl;
		}
		else
		{
			GL_Bind ((crosshair.value >= 2) ? crosshairtextures[(int) crosshair.value - 2] : crosshairtexture_txt);	
			ofs1 = 3.5;
			ofs2 = 4.5;
			tl = sl = 0;
			sh = th = 1;
		}
		ofs1 *= (vid.width / 320) * bound(0, crosshairsize.value, 20);
		ofs2 *= (vid.width / 320) * bound(0, crosshairsize.value, 20);

		glBegin(GL_QUADS);
		glTexCoord2f(sl, tl);
		glVertex2f(x - ofs1, y - ofs1);
		glTexCoord2f(sh, tl);
		glVertex2f(x + ofs2, y - ofs1);
		glTexCoord2f(sh, th);
		glVertex2f(x + ofs2, y + ofs2);
		glTexCoord2f(sl, th);
		glVertex2f(x - ofs1, y + ofs2);
		glEnd();

		if (gl_crosshairalpha.value)
		{
			glDisable(GL_BLEND);
			glEnable(GL_ALPHA_TEST);
		}

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glColor3ubv(color_white);
	}
	else if (crosshair.value)
	{
		Draw_Character(scr_vrect.x + scr_vrect.width / 2 - 4 + cl_crossx.value, scr_vrect.y + scr_vrect.height / 2 - 4 + cl_crossy.value, '+');
	}
}

void Draw_TextBox(int x, int y, int width, int lines)
{
	mpic_t *p;
	int cx, cy, n;

	// draw left side
	cx = x;
	cy = y;
	p = Draw_CachePic("gfx/box_tl.lmp");
	Draw_TransPic(cx, cy, p);
	p = Draw_CachePic("gfx/box_ml.lmp");
	for (n = 0; n < lines; n++)
	{
		cy += 8;
		Draw_TransPic(cx, cy, p);
	}
	p = Draw_CachePic("gfx/box_bl.lmp");
	Draw_TransPic(cx, cy+8, p);

	// draw middle
	cx += 8;
	while (width > 0)
	{
		cy = y;
		p = Draw_CachePic("gfx/box_tm.lmp");
		Draw_TransPic(cx, cy, p);
		p = Draw_CachePic("gfx/box_mm.lmp");
		for (n = 0; n < lines; n++)
		{
			cy += 8;
			if (n == 1)
				p = Draw_CachePic("gfx/box_mm2.lmp");
			Draw_TransPic(cx, cy, p);
		}
		p = Draw_CachePic("gfx/box_bm.lmp");
		Draw_TransPic(cx, cy+8, p);
		width -= 2;
		cx += 16;
	}

	// draw right side
	cy = y;
	p = Draw_CachePic("gfx/box_tr.lmp");
	Draw_TransPic (cx, cy, p);
	p = Draw_CachePic("gfx/box_mr.lmp");
	for (n = 0; n < lines; n++)
	{
		cy += 8;
		Draw_TransPic(cx, cy, p);
	}
	p = Draw_CachePic("gfx/box_br.lmp");
	Draw_TransPic(cx, cy+8, p);
}

void Draw_Pic(int x, int y, mpic_t *pic)
{
	if (scrap_dirty)
		Scrap_Upload();
	GL_Bind(pic->texnum);
	glBegin(GL_QUADS);
	glTexCoord2f(pic->sl, pic->tl);
	glVertex2f(x, y);
	glTexCoord2f(pic->sh, pic->tl);
	glVertex2f(x + pic->width, y);
	glTexCoord2f(pic->sh, pic->th);
	glVertex2f(x + pic->width, y + pic->height);
	glTexCoord2f(pic->sl, pic->th);
	glVertex2f(x, y + pic->height);
	glEnd();
}

void Draw_AlphaPic(int x, int y, mpic_t *pic, float alpha)
{
	if (scrap_dirty)
		Scrap_Upload ();
	glDisable(GL_ALPHA_TEST);
	glEnable(GL_BLEND);
//	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glCullFace(GL_FRONT);
	glColor4f(1, 1, 1, alpha);
	GL_Bind(pic->texnum);
	glBegin(GL_QUADS);
	glTexCoord2f(pic->sl, pic->tl);
	glVertex2f(x, y);
	glTexCoord2f(pic->sh, pic->tl);
	glVertex2f(x + pic->width, y);
	glTexCoord2f(pic->sh, pic->th);
	glVertex2f(x + pic->width, y + pic->height);
	glTexCoord2f(pic->sl, pic->th);
	glVertex2f(x, y + pic->height);
	glEnd();
	glColor3ubv(color_white);
	glEnable(GL_ALPHA_TEST);
	glDisable(GL_BLEND);
}

void Draw_SubPic(int x, int y, mpic_t *pic, int srcx, int srcy, int width, int height)
{
	float newsl, newtl, newsh, newth, oldglwidth, oldglheight;

	if (scrap_dirty)
		Scrap_Upload ();

	oldglwidth = pic->sh - pic->sl;
	oldglheight = pic->th - pic->tl;

	newsl = pic->sl + (srcx * oldglwidth) / pic->width;
	newsh = newsl + (width * oldglwidth) / pic->width;

	newtl = pic->tl + (srcy * oldglheight) / pic->height;
	newth = newtl + (height * oldglheight) / pic->height;

	GL_Bind(pic->texnum);
	glBegin(GL_QUADS);
	glTexCoord2f(newsl, newtl);
	glVertex2f(x, y);
	glTexCoord2f(newsh, newtl);
	glVertex2f(x + width, y);
	glTexCoord2f(newsh, newth);
	glVertex2f(x + width, y + height);
	glTexCoord2f(newsl, newth);
	glVertex2f(x, y + height);
	glEnd();
}

void Draw_TransPic(int x, int y, mpic_t *pic)
{
	if (x < 0 || (unsigned) (x + pic->width) > vid.width || y < 0 || (unsigned) (y + pic->height) > vid.height)
		Sys_Error("Draw_TransPic: bad coordinates");
		
	Draw_Pic(x, y, pic);
}

//Only used for the player color selection menu
void Draw_TransPicTranslate(int x, int y, mpic_t *pic, byte *translation)
{
	int v, u, c, p;
	unsigned trans[64 * 64], *dest;
	byte *src;

	GL_Bind (translate_texture);

	c = pic->width * pic->height;

	dest = trans;
	for (v = 0; v < 64; v++, dest += 64)
	{
		src = &menuplyr_pixels[ ((v * pic->height) >> 6) *pic->width];
		for (u = 0; u < 64; u++)
		{
			p = src[(u * pic->width) >> 6];
			dest[u] = (p == 255) ? p : d_8to24table[translation[p]];
		}
	}

	glTexImage2D(GL_TEXTURE_2D, 0, gl_alpha_format, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans);

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

	glBegin(GL_QUADS);
	glTexCoord2f(0, 0);
	glVertex2f(x, y);
	glTexCoord2f(1, 0);
	glVertex2f(x + pic->width, y);
	glTexCoord2f(1, 1);
	glVertex2f(x + pic->width, y + pic->height);
	glTexCoord2f(0, 1);
	glVertex2f(x, y + pic->height);
	glEnd();
}

void Draw_ConsoleBackground(int lines)
{
	char ver[80];

	if (SCR_NEED_CONSOLE_BACKGROUND)
	{
		Draw_Pic(0, lines - vid.height, &conback);
	}
	else
	{
		if (scr_conalpha.value)
			Draw_AlphaPic (0, lines - vid.height, &conback, bound (0, scr_conalpha.value, 1));
	}

	sprintf(ver, "FodQuake %s", FODQUAKE_VERSION);
	Draw_Alt_String(vid.conwidth - strlen(ver) * 8 - 8, lines - 10, ver);
}

//This repeats a 64 * 64 tile graphic to fill the screen around a sized down refresh window.
void Draw_TileClear(int x, int y, int w, int h)
{
	GL_Bind(draw_backtile->texnum);
	glBegin(GL_QUADS);
	glTexCoord2f(x / 64.0, y / 64.0);
	glVertex2f(x, y);
	glTexCoord2f((x + w) / 64.0, y / 64.0);
	glVertex2f(x + w, y);
	glTexCoord2f((x + w) / 64.0, (y + h) / 64.0);
	glVertex2f(x + w, y + h);
	glTexCoord2f(x / 64.0, (y + h) / 64.0 );
	glVertex2f(x, y + h);
	glEnd();
}

void Draw_AlphaFill(int x, int y, int w, int h, int c, float alpha)
{
	alpha = bound(0, alpha, 1);

	if (!alpha)
		return;

	glDisable(GL_TEXTURE_2D);
	if (alpha < 1)
	{
		glEnable(GL_BLEND);
		glDisable(GL_ALPHA_TEST);
		glColor4f(host_basepal[c * 3] / 255.0,  host_basepal[c * 3 + 1] / 255.0, host_basepal[c * 3 + 2] / 255.0, alpha);
	}
	else
	{
		glColor3f(host_basepal[c * 3] / 255.0, host_basepal[c * 3 + 1] / 255.0, host_basepal[c * 3 + 2]  /255.0);
	}

	glBegin(GL_QUADS);
	glVertex2f(x, y);
	glVertex2f(x + w, y);
	glVertex2f(x + w, y + h);
	glVertex2f(x, y + h);
	glEnd();

	glEnable(GL_TEXTURE_2D);
	if (alpha < 1)
	{
		glEnable(GL_ALPHA_TEST);
		glDisable(GL_BLEND);
	}
	glColor3ubv(color_white);
}

void Draw_Fill(int x, int y, int w, int h, int c)
{
	Draw_AlphaFill(x, y, w, h, c, 1);
}

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

void Draw_FadeScreen(void)
{
	float alpha;

	alpha = bound(0, scr_menualpha.value, 1);
	if (!alpha)
		return;

	if (alpha < 1)
	{
		glDisable(GL_ALPHA_TEST);
		glEnable(GL_BLEND);
		glColor4f(0, 0, 0, alpha);
	}
	else
	{
		glColor3f(0, 0, 0);
	}
	glDisable(GL_TEXTURE_2D);

	glBegin(GL_QUADS);
	glVertex2f(0, 0);
	glVertex2f(vid.width, 0);
	glVertex2f(vid.width, vid.height);
	glVertex2f(0, vid.height);
	glEnd();

	if (alpha < 1)
	{
		glDisable(GL_BLEND);
		glEnable(GL_ALPHA_TEST);
	}
	glColor3ubv(color_white);
	glEnable(GL_TEXTURE_2D);

	Sbar_Changed();
}

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

//Draws the little blue disc in the corner of the screen. 
//Call before beginning any disc IO.
void Draw_BeginDisc(void)
{
	if (!draw_disc)
		return;
	glDrawBuffer(GL_FRONT);
	Draw_Pic(vid.width - 24, 0, draw_disc);
	glDrawBuffer(GL_BACK);
}

//Erases the disc icon.
//Call after completing any disc IO
void Draw_EndDisc(void)
{
}

void GL_Set2D(void)
{
	glViewport(glx, gly, glwidth, glheight);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, vid.width, vid.height, 0, -99999, 99999);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glDisable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);
	glDisable(GL_BLEND);
	glEnable(GL_ALPHA_TEST);

	glColor3ubv(color_white);
}

