/*
Copyright (C) 1997-2001 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.

*/

// draw.c

#include "header/local.h"


static image_t	*draw_chars;	// 8*8 graphic characters

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

/*
================
RE_Draw_FindPic
================
*/
image_t *
RE_Draw_FindPic (const char *name)
{
	return R_FindPic(name, (findimage_t)R_FindImage);
}



/*
===============
Draw_InitLocal
===============
*/
void
Draw_InitLocal (void)
{
	draw_chars = R_FindPic ("conchars", (findimage_t)R_FindImage);
	if (!draw_chars)
	{
		Com_Error(ERR_FATAL, "%s: Couldn't load pics/conchars.pcx", __func__);
	}
}

/*
================
Draw_Char

Draws one 8*8 graphics character
It can be clipped to the top of the screen to allow the console to be
smoothly scrolled off.
================
*/
void
RE_Draw_CharScaled(int x, int y, int c, float scale)
{
	pixel_t	*dest;
	byte	*source, *pic_pixels;
	int	drawline;
	int	row, col, v, iscale, sscale, width, height;

	iscale = (int) scale;

	if (iscale < 1)
		return;

	c &= 255;

	if ((c&127) == 32)
		return;

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

	if ( ( y + 8 ) > vid_buffer_height )	// status text was missing in sw...
		return;

	row = c>>4;
	col = c&15;

	width = draw_chars->asset_width * iscale;
	height = draw_chars->asset_height * iscale;
	pic_pixels = Get_BestImageSize(draw_chars, &width, &height);

	sscale = width / draw_chars->asset_width;
	pic_pixels += ((row<<10) * sscale + (col<<3)) * sscale;

	if (y < 0)
	{	// clipped
		drawline = 8 + y;
		pic_pixels -= width * y * sscale;
		y = 0;
	}
	else
		drawline = 8;

	dest = vid_buffer + y * vid_buffer_width + x;

	// clipped last lines
	if ((y + iscale * (drawline + 1)) > vid_buffer_height)
	{
		drawline = (vid_buffer_height - y) / scale;
	}

	VID_DamageBuffer(x, y);
	VID_DamageBuffer(x + (scale * 8), y + (scale * drawline));

	drawline = drawline * scale;

	for (v=0 ; v < drawline; v++, dest += vid_buffer_width)
	{
		int f, fstep, u;
		int sv = v * height / (iscale * draw_chars->asset_height);
		source = pic_pixels + sv * width;
		f = 0;
		fstep = (width << SHIFT16XYZ) / (scale * draw_chars->asset_width);
		for (u=0 ; u < (8 * iscale) ; u++)
		{
			if (source[f>>16] != TRANSPARENT_COLOR)
			{
				dest[u] = source[f>>16];
			}
			f += fstep;
		}
	}
}

/*
=============
RE_Draw_GetPicSize
=============
*/
void
RE_Draw_GetPicSize (int *w, int *h, const char *name)
{
	image_t *image;

	image = R_FindPic (name, (findimage_t)R_FindImage);
	if (!image)
	{
		*w = *h = -1;
		return;
	}
	*w = image->asset_width;
	*h = image->asset_height;
}

/*
=============
RE_Draw_StretchPicImplementation
=============
*/
static void
RE_Draw_StretchPicImplementation (int x, int y, int w, int h, const image_t *pic)
{
	pixel_t	*dest;
	byte	*source;
	int		height;
	int		skip;
	int		pic_height, pic_width;
	byte	*pic_pixels;

	if ((x < 0) ||
		(x + w > vid_buffer_width) ||
		(y + h > vid_buffer_height))
	{
		Com_Printf("%s: bad coordinates %dx%d[%dx%d]",
			__func__, x, y, w, h);
		return;
	}

	VID_DamageBuffer(x, y);
	VID_DamageBuffer(x + w, y + h);

	height = h;
	if (y < 0)
	{
		skip = -y;
		height += y;
		y = 0;
	}
	else
		skip = 0;

	dest = vid_buffer + y * vid_buffer_width + x;

	pic_height = h;
	pic_width = w;
	pic_pixels = Get_BestImageSize(pic, &pic_width, &pic_height);

	if (!pic->transparent)
	{
		if (w == pic_width)
		{
			int v;

			for (v=0 ; v<height ; v++, dest += vid_buffer_width)
			{
				int sv = (skip + v) * pic_height / h;
				memcpy (dest, pic_pixels + sv*pic_width, w);
			}
		}
		else
		{
			int v;

			// size of screen tile to pic pixel
			int picupscale = h / pic_height;

			for (v=0 ; v<height ; v++, dest += vid_buffer_width)
			{
				int f, fstep, u;
				int sv = (skip + v) * pic_height/h;
				source = pic_pixels + sv*pic_width;
				f = 0;
				fstep = (pic_width << SHIFT16XYZ) / w;
				for (u=0 ; u<w ; u++)
				{
					dest[u] = source[f>>16];
					f += fstep;
				}
				if (picupscale > 1)
				{
					int i;
					int pu = Q_min(height-v, picupscale);
					pixel_t	*dest_orig = dest;

					// copy first line to fill whole sector
					for (i=1; i < pu; i++)
					{
						// go to next line
						dest += vid_buffer_width;
						memcpy (dest, dest_orig, w);
					}
					// skip updated lines
					v += (picupscale - 1);
				}
			}
		}
	}
	else
	{
		source = pic_pixels;

		if (h == pic_height && w == pic_width)
		{
			int v;

			for (v = 0; v < pic_height; v++)
			{
				int u;

				for (u = 0; u < pic_width; u++)
				{
					if (source[u] != TRANSPARENT_COLOR)
						dest[u] = source[u];
				}
				dest += vid_buffer_width;
				source += pic_width;
			}
		}
		else
		{
			int v;

			for (v=0 ; v<height ; v++, dest += vid_buffer_width)
			{
				int f, fstep, u;
				int sv = (skip + v) * pic_height/h;
				source = pic_pixels + sv*pic_width;
				f = 0;
				fstep = (pic_width << SHIFT16XYZ) / w;
				for (u=0 ; u<w ; u++)
				{
					if (source[f>>16] != TRANSPARENT_COLOR)
					{
						dest[u] = source[f>>16];
					}
					f += fstep;
				}
			}
		}
	}
}

/*
=============
RE_Draw_StretchPic
=============
*/
void
RE_Draw_StretchPic (int x, int y, int w, int h, const char *name)
{
	image_t	*pic;

	pic = R_FindPic (name, (findimage_t)R_FindImage);
	if (!pic)
	{
		Com_Printf("Can't find pic: %s\n", name);
		return;
	}
	RE_Draw_StretchPicImplementation (x, y, w, h, pic);
}

/*
=============
RE_Draw_StretchRaw
=============
*/
void
RE_Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, const byte *data, int bits)
{
	byte *image_scaled;
	image_t pic = {0};

	// we have only one image size
	pic.mip_levels = 1;

	if (bits == 32)
	{
		image_scaled = malloc(cols * rows);
		R_Convert32To8bit(data, image_scaled, cols * rows, false);
	}
	else if (r_retexturing->value)
	{
		if (cols < (w / 3) || rows < (h / 3))
		{
			image_scaled = malloc(cols * rows * 9);

			scale3x(data, image_scaled, cols, rows);

			cols = cols * 3;
			rows = rows * 3;
		}
		else
		{
			image_scaled = malloc(cols * rows * 4);

			scale2x(data, image_scaled, cols, rows);

			cols = cols * 2;
			rows = rows * 2;
		}
	}
	else
	{
		image_scaled = (byte *)data;
	}

	pic.pixels[0] = image_scaled;
	pic.width = cols;
	pic.height = rows;
	pic.asset_width = cols;
	pic.asset_height = rows;

	RE_Draw_StretchPicImplementation (x, y, w, h, &pic);

	if (r_retexturing->value)
	{
		free(image_scaled);
	}
}

/*
=============
Draw_Pic
=============
*/
void
RE_Draw_PicScaled(int x, int y, const char *name, float scale)
{
	const image_t *pic;

	pic = R_FindPic (name, (findimage_t)R_FindImage);
	if (!pic)
	{
		Com_Printf("Can't find pic: %s\n", name);
		return;
	}

	RE_Draw_StretchPicImplementation (
		x, y,
		scale * pic->asset_width, scale * pic->asset_height,
		pic);
}

void
RE_Draw_PicScaledCol(int x, int y, const char *name, float scale, const float color[3])
{
	const image_t *pic;
	int w, h;
	int pic_width, pic_height;
	byte *pic_pixels;
	pixel_t *dest;
	int v, u;

	pic = R_FindPic(name, (findimage_t)R_FindImage);
	if (!pic)
	{
		Com_Printf("Can't find pic: %s\n", name);
		return;
	}

	/* If no tint needed, fall back to normal draw */
	if (color[0] >= 1.0f && color[1] >= 1.0f && color[2] >= 1.0f)
	{
		RE_Draw_StretchPicImplementation(
			x, y,
			scale * pic->asset_width, scale * pic->asset_height,
			pic);
		return;
	}

	w = (int)(scale * pic->asset_width);
	h = (int)(scale * pic->asset_height);

	if ((x < 0) ||
		(x + w > vid_buffer_width) ||
		(y + h > vid_buffer_height))
	{
		Com_Printf("%s: bad coordinates %dx%d[%dx%d]",
			__func__, x, y, w, h);
		return;
	}

	VID_DamageBuffer(x, y);
	VID_DamageBuffer(x + w, y + h);

	dest = vid_buffer + y * vid_buffer_width + x;

	pic_width = w;
	pic_height = h;
	pic_pixels = Get_BestImageSize(pic, &pic_width, &pic_height);

	for (v = 0; v < h; v++, dest += vid_buffer_width)
	{
		int sv = v * pic_height / h;
		byte *source = pic_pixels + sv * pic_width;
		int f = 0;
		int fstep = (pic_width << SHIFT16XYZ) / w;

		for (u = 0; u < w; u++)
		{
			byte idx = source[f >> 16];
			f += fstep;

			if (idx == TRANSPARENT_COLOR)
			{
				continue;
			}

			/* Compute luminance from the original pixel and apply the
			   tint color at that brightness.  This ensures the desired
			   hue is produced regardless of the palette color used in
			   the source image (e.g. crosshair PCX files). */
			float pr = d_8to24table[idx * 4 + 0];
			float pg = d_8to24table[idx * 4 + 1];
			float pb = d_8to24table[idx * 4 + 2];
			float lum = (0.299f * pr + 0.587f * pg + 0.114f * pb) / 255.0f;

			unsigned int r = (unsigned int)(255.0f * lum * color[0]);
			unsigned int g = (unsigned int)(255.0f * lum * color[1]);
			unsigned int b = (unsigned int)(255.0f * lum * color[2]);

			if (r > 255) r = 255;
			if (g > 255) g = 255;
			if (b > 255) b = 255;

			r = (r >> 3) & 31;
			g = (g >> 2) & 63;
			b = (b >> 3) & 31;

			dest[u] = d_16to8table[r | (g << 5) | (b << 11)];
		}
	}
}

/*
=============
RE_Draw_TileClear

This repeats a 64*64 tile graphic to fill the screen around a sized down
refresh window.
=============
*/
void
RE_Draw_TileClear (int x, int y, int w, int h, const char *name)
{
	int			i, j;
	byte		*psrc;
	pixel_t		*pdest;
	image_t		*pic;
	int			x2;

	if (x < 0)
	{
		w += x;
		x = 0;
	}
	if (y < 0)
	{
		h += y;
		y = 0;
	}
	if (x + w > vid_buffer_width)
		w = vid_buffer_width - x;
	if (y + h > vid_buffer_height)
		h = vid_buffer_height - y;
	if (w <= 0 || h <= 0)
		return;

	VID_DamageBuffer(x, y);
	VID_DamageBuffer(x + w, y + h);

	pic = R_FindPic (name, (findimage_t)R_FindImage);
	if (!pic)
	{
		Com_Printf("Can't find pic: %s\n", name);
		return;
	}
	x2 = x + w;
	pdest = vid_buffer + y * vid_buffer_width;
	for (i=0 ; i<h ; i++, pdest += vid_buffer_width)
	{
		psrc = pic->pixels[0] + pic->width * ((i+y) % pic->height);
		for (j=x ; j<x2 ; j++)
			pdest[j] = psrc[j % pic->width];
	}
}


/*
=============
RE_Draw_Fill

Fills a box of pixels with a single color
=============
*/
void
RE_Draw_Fill (int x, int y, int w, int h, int c)
{
	pixel_t	*dest;
	int	v;

	if (x+w > vid_buffer_width)
		w = vid_buffer_width - x;
	if (y+h > vid_buffer_height)
		h = vid_buffer_height - y;

	if (x < 0)
	{
		w += x;
		x = 0;
	}
	if (y < 0)
	{
		h += y;
		y = 0;
	}

	if (w < 0 || h < 0)
		return;

	VID_DamageBuffer(x, y);
	VID_DamageBuffer(x + w, y + h);

	dest = vid_buffer + y * vid_buffer_width + x;
	for (v=0 ; v<h ; v++, dest += vid_buffer_width)
		memset(dest, c, w);
}
//=============================================================================

/*
================
RE_Draw_FadeScreen

================
*/
void
RE_Draw_FadeScreen (void)
{
	int x,y;

	VID_DamageBuffer(0, 0);
	VID_DamageBuffer(vid_buffer_width, vid_buffer_height);

	for (y=0 ; y<vid_buffer_height ; y++)
	{
		int t;
		pixel_t *pbuf;

		pbuf = vid_buffer + vid_buffer_width * y;
		t = (y & 1) << 1;

		for (x=0 ; x<vid_buffer_width ; x++)
		{
			if ((x & 3) != t)
				pbuf[x] = 0;
		}
	}
}
