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

*/
// gl_rmisc.c

#include "quakedef.h"

void R_InitOtherTextures (void)
{
	int	flags = TEX_MIPMAP | TEX_ALPHA;

	// Environment
	underwatertexture = GL_LoadTextureImage ("textures/water_caustic", NULL, 0, 0,  flags | (gl_waterfog.value ? TEX_COMPLAIN : 0));
	detailtexture = GL_LoadTextureImage ("textures/detail", NULL, 256, 256, flags | (gl_detail.value ? TEX_COMPLAIN : 0));
	chrometexture = GL_LoadTextureImage ("textures/shiny", NULL, 256, 256, flags | (gl_shiny.value ? TEX_COMPLAIN : 0));
	glasstexture = GL_LoadTextureImage ("textures/glass", NULL, 256, 256, flags | (gl_shiny.value ? TEX_COMPLAIN : 0));

	// Liquids
	newwatertex = GL_LoadTextureImage ("textures/decal/q3water", NULL, 0, 0, flags | (r_liquidshade.value ? TEX_COMPLAIN : 0));
	newlavatex = GL_LoadTextureImage ("textures/decal/q3lava", NULL, 0, 0, flags | (r_liquidshade.value ? TEX_COMPLAIN : 0));
	newslimetex = GL_LoadTextureImage ("textures/decal/q3slime", NULL, 0, 0, flags | (r_liquidshade.value ? TEX_COMPLAIN : 0));
	newteletex = GL_LoadTextureImage ("textures/decal/newtele", NULL, 0, 0, flags | (r_liquidshade.value ? TEX_COMPLAIN : 0));
}

/*
==================
R_InitTextures
==================
*/
void R_InitTextures (void)
{
	int	x, y, m;
	byte	*dest;

// create a simple checkerboard texture for the default
	r_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture");

	r_notexture_mip->width = r_notexture_mip->height = 16;
	r_notexture_mip->offsets[0] = sizeof(texture_t);
	r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16;
	r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8;
	r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4;

	for (m=0 ; m<4 ; m++)
	{
		dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m];
		for (y=0 ; y<(16>>m) ; y++)
		{
			for (x=0 ; x<(16>>m) ; x++)
			{
				*dest++ = ((y < (8 >> m)) ^ (x < (8 >> m))) ? 0 : 0x0e;
			}
		}
	}
}

/*
===============	
R_TranslatePlayerSkin

Translates a skin texture by the per-player color lookup
===============
*/
void R_TranslatePlayerSkin (int playernum)
{
	int			top, bottom, i, j, size, scaled_width, scaled_height, inwidth, inheight;
	byte		translate[256], *original, *inrow;
	unsigned	translate32[256], pixels[512*256], *out, frac, fracstep;
	model_t		*model;
	aliashdr_t	*paliashdr;

	GL_DisableMultitexture ();

	top = cl.scores[playernum].colors & 0xf0;
	bottom = (cl.scores[playernum].colors & 15) << 4;

	for (i=0 ; i<256 ; i++) {
		translate[i] = i;
	}

	for (i=0 ; i<16 ; i++) {
		// the artists made some backward ranges. sigh.
		translate[TOP_RANGE+i] = (top < 128) ? top + i : top + 15 - i;
		translate[BOTTOM_RANGE+i] = (bottom < 128) ? bottom + i : bottom + 15 - i;
	}

	// locate the original skin pixels
	currententity = &cl_entities[1+playernum];
	if (!(model = currententity->model)) return;		// player doesn't have a model yet
	if (model->type != mod_alias) return;				// only translate skins on alias models

	paliashdr = (aliashdr_t *)Mod_Extradata (model);
	size = paliashdr->skinwidth * paliashdr->skinheight;

	if (currententity->skinnum < 0 || currententity->skinnum >= paliashdr->numskins) {
		Con_DPrintf ("(%d): Invalid player skin #%d\n", playernum, currententity->skinnum);
		original = (byte *)paliashdr + paliashdr->texels[0];
	} else {
		original = (byte *)paliashdr + paliashdr->texels[currententity->skinnum];
	}

	if (size & 3) {
		Con_Printf ("R_TranslatePlayerSkin: size & 3");
	}
	inwidth = paliashdr->skinwidth;
	inheight = paliashdr->skinheight;

	// because this happens during gameplay, do it fast
	// instead of sending it through gl_upload 8
	GL_Bind (playertextures + playernum + 1);

	scaled_width = gl_picmip_all.value ? min(gl_max_size.value, 512) : min(gl_max_size_default, 512);
	scaled_height = gl_picmip_all.value ? min(gl_max_size.value, 256) : min(gl_max_size_default, 256);

	// allow users to crunch sizes down even more if they want
	scaled_width >>= max(1, (int)gl_playermip.value);
	scaled_height >>= max(1, (int)gl_playermip.value);

	for (i=0 ; i<256 ; i++) {
		translate32[i] = d_8to24table[translate[i]];
	}
	out = pixels;
	memset (pixels, 0, sizeof(pixels));
	fracstep = (inwidth << 16) / scaled_width;
	for (i = 0 ; i < scaled_height ; i++, out += scaled_width) {
		inrow = original + inwidth * (i * inheight / scaled_height);
		frac = fracstep >> 1;
		for (j = 0 ; j < scaled_width ; j += 4) {
			out[j] = translate32[inrow[frac>>16]];
			frac += fracstep;
			out[j+1] = translate32[inrow[frac>>16]];
			frac += fracstep;
			out[j+2] = translate32[inrow[frac>>16]];
			frac += fracstep;
			out[j+3] = translate32[inrow[frac>>16]];
			frac += fracstep;
		}
	}
	glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

	glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	// the former struct here was a total timewaster
	// still not working arrghhh psycadelic player model :S
	// dunno joe's compile works but even with the original
	// it still fucks up here buaahhh'''
	if (Mod_HasFullbrights(original, inwidth*inheight)) {
		GL_Bind (playerfbtextures + playernum + 1);
		out = pixels;
		memset (pixels, 0, sizeof(pixels));
		fracstep = (inwidth << 16) / scaled_width;
		// make all non-fullbright colors transparent
		for (i = 0 ; i < scaled_height ; i++, out += scaled_width) {
			inrow = original + inwidth * (i * inheight / scaled_height);
			frac = fracstep >> 1;
			for (j = 0 ; j < scaled_width ; j += 4) {
				if (inrow[frac>>16] < 224)
					out[j] = translate32[inrow[frac>>16]] & 0x00FFFFFF; // transparent
				else
					out[j] = translate32[inrow[frac>>16]];				// fullbright
				frac += fracstep;
				if (inrow[frac>>16] < 224)
					out[j+1] = translate32[inrow[frac>>16]] & 0x00FFFFFF;
				else
					out[j+1] = translate32[inrow[frac>>16]];
				frac += fracstep;
				if (inrow[frac>>16] < 224)
					out[j+2] = translate32[inrow[frac>>16]] & 0x00FFFFFF;
				else
					out[j+2] = translate32[inrow[frac>>16]];
				frac += fracstep;
				if (inrow[frac>>16] < 224)
					out[j+3] = translate32[inrow[frac>>16]] & 0x00FFFFFF;
				else
					out[j+3] = translate32[inrow[frac>>16]];
				frac += fracstep;
			}
		}
		glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

		glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	}
}

// joe: added from FuhQuake
void R_PreMapLoad (char *mapname)
{
	Cvar_Set ("mapname", mapname);
	lightmode = 2;
}

/*
===============
R_NewMap
===============
*/
void R_NewMap (void)
{
	int	i, waterline;
	
	for (i=0 ; i<256 ; i++)
	{
		d_lightstylevalue[i] = 264;		// normal light value
	}
	memset (&r_worldentity, 0, sizeof(r_worldentity));
	r_worldentity.model = cl.worldmodel;

	// clear out efrags in case the level hasn't been reloaded
	// FIXME: is this one short?
	// yup!!
	for (i=0 ; i<cl.worldmodel->numleafs + 1 ; i++) {
		cl.worldmodel->leafs[i].efrags = NULL;
	}
	r_viewleaf = NULL;
	R_ClearParticles ();

	GL_BuildLightmaps ();

	// identify sky texture
	for (i=0 ; i<cl.worldmodel->numtextures ; i++)
	{
		if (!cl.worldmodel->textures[i]) continue;

		for (waterline=0 ; waterline<2 ; waterline++)
		{
 			cl.worldmodel->textures[i]->texturechain[waterline] = NULL;
			cl.worldmodel->textures[i]->texturechain_tail[waterline] = &cl.worldmodel->textures[i]->texturechain[waterline];
		}
	}
}


/*
====================
R_TimeRefresh_f

For program optimization
====================
*/
void R_TimeRefresh_f (void)
{
	int			i;
	float		start, stop, time;

	glDrawBuffer (GL_FRONT);
	glFinish ();

	start = Sys_DoubleTime ();
	for (i=0 ; i<128 ; i++)
	{
		r_refdef.viewangles[1] = i * (360.0 / 128.0);
		R_RenderView ();
	}

	glFinish ();
	stop = Sys_DoubleTime ();
	time = stop - start;
	Con_Printf ("%f seconds (%f fps)\n", time, 128/time);

	glDrawBuffer (GL_BACK);
	GL_EndRendering ();
}

void D_FlushCaches (void)
{
}

