/*(LGPL)
---------------------------------------------------------------------------
	window.h - Generic Rendering Window
---------------------------------------------------------------------------
 * Copyright (C) 2001-2003, 2007 David Olofson
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * Programmer's Documentation:
 *	The window_t class represents a simple window of the
 *	screen surface. It provides some high level rendering
 *	functions that operate inside the window's bounding
 *	rectangle. All operations are clipped. However,
 *	window occlusion is *not* handled, so you can't use
 *	this class for "real" windows.
 *
 *	virtual void init(gfxengine_t *e);
 *		Call this to connect a window_t instance to
 *		the gfxengine_t instance that manages your
 *		screen. You *must* call this for pretty much
 *		any other calls to work.
 *
 *	void place(int left, int top, int sizex, int sizey);
 *		Position the window on the screen.
 *
 *	int offscreen();
 *		Make this an off-screen window, with a
 *		surface of it's own for rendering. An off-
 *		screen window will never be directly visible,
 *		but can be used as a source window for blit().
 *		May return -1 if there is an error.
 *
 *	void select();
 *		Makes this window active, and sets up SDL's
 *		clipping appropriately. You should not use
 *		this call normally, but it's provided to
 *		allow this engine to interface with other
 *		SDL code.
 *
 *	void invalidate(SDL_Rect *r = NULL);
 *		Invalidate the specified area, or the whole
 *		window if r is NULL. This will ensure that
 *		any changes in the invalidated area are made
 *		visible on the screen. Note that this call
 *		may also indirectly cause one or more calls
 *		to refresh().
 *
 *	virtual void refresh(SDL_Rect *r);
 *		Virtual refresh call to be implemented by
 *		windows that aren't repainted every frame.
 *		After a call to invalidate(), refresh() will
 *		be called by the engine as necessary to
 *		produce a correct display. Note that in some
 *		cases, this means that refresh() will be
 *		called once for each screen flip, until all
 *		buffers (usually two) are up to date. Also
 *		note that you should do all rendering to the
 *		window either directly *or* in the refresh()
 *		function.
 *
 *	Uint32 map_rgb(Uint8 r, Uint8 g, Uint8 b);
 *	Uint32 map_rgb(Uint32 rgb);
 *		Two different ways of translating a color in
 *		standard RGB format (separate components or
 *		HTML style hex code, respectively) into a
 *		pixel value compatible with the screen.
 *
 *	void foreground(Uint32 color);
 *	void background(Uint32 color);
 *		Change the foreground and background colors
 *		used by subsequent rendering operations.
 *		Note that the arguments have to be in screen
 *		pixel format - the easiest way is to use the
 *		map_rgb() calls to generate the values.
 *
 *	void bgimage(int bank, int frame);
 *		Select a background image, to use instead of
 *		a background color. Setting bank and frame to
 *		-1 (default) disables the image, so that the
 *		background color is used instead.
 *
 *	void colorkey(Uint32 color);
 *	void colorkey();
 *		Set the colorkey for this window to 'color'
 *		and enable colorkeying. This affects blit()
 *		when this window is used as the source. The
 *		version without arguments disables
 *		colorkeying for this window.
 *		NOTE:	Colorkeying is only supported by
 *			offscreen windows.
 *
 *	void alpha(float a);
 *		Set the full surface alpha of this window.
 *		0.0 is fully transparent and 1.0 is opaque.
 *		NOTE:	Alpha is only supported by offscreen
 *			windows.
 *
 *	void clear(SDL_Rect *r = NULL);
 *		Fill the specified rectangle with the current
 *		background color or background image. If r is
 *		NULL (default), clear the whole window.
 *
 *	void font(int fnt);
 *		Select the font to be used for subsequent
 *		text rendering operations. The argument is a
 *		gfxengine_t bank index, and must refer to a
 *		bank that has been loaded with
 *		gfxengine_t::loadfont().
 *
 *	void string(int _x, int _y, const char *txt);
 *		Put text string 'txt' starting at (_x, _y).
 *
 *	void center(int _y, const char *txt);
 *		Put text string 'txt' centered horizontally
 *		inside the window, using '_y' as the y coord.
 *
 *	void center_token(int _x, int _y, const char *txt, char token = 0);
 *		Print string 'txt', positioning it so that
 *		the first occurrence of character 'token'
 *		is located at (_x, _y). Omitting the 'token'
 *		argument, or passing 0 aligns the end of the
 *		string with _x. Setting 'token' to -1 results
 *		in the graphical center of the string being
 *		located at (_x, _y) - basically an advanced
 *		version of center().
 *
 *	void string_fxp(int _x, int _y, const char *txt);
 *	void center_fxp(int _y, const char *txt);
 *	void center_token_fxp(int _x, int _y, const char *txt, char token = 0);
 *		gfxengine_t fixed point format versions of
 *		the corresponding non-fxp calls. (For sub-
 *		pixel accurate rendering and/or to make use
 *		of the higher resolutions in scaled modes.)
 *
 *	int textwidth(const char *txt, int min = 0, int max = 255);
 *		Calculates the graphical width of 'txt' if it
 *		was to be printed with the current font.
 *		Calculation starts at position 'min' in the
 *		string, and ends at 'max', which makes it
 *		possible to use this call for various
 *		advanced alignment calculations.
 *
 *	int fontheight();
 *		Returns the height of the currently selected
 *		font.
 *
 *	void point(int _x, int _y);
 *		Plot a pixel with the current foreground
 *		color at (_x, _y). Note that the pixel will
 *		become a quadratic block if the screen is
 *		scaled to a higher resolution.
 *
 *	void rectangle(int _x, int _y, int w, int h);
 *		Draw a hollow rectangle of w x h pixels at
 *		(_x, _y). The line thickness is scaled along
 *		with screen resolution, as with point().
 *
 *	void fillrect(int _x, int _y, int w, int h);
 *		Draw a solid rectangle of w x h pixels at
 *		(_x, _y).
 *
 *	void fillrect_fxp(int _x, int _y, int w, int h);
 *		As fillrect(), but with pixel or sub-pixel
 *		accuracy, depending on scaling and video
 *		driver.
 *
 *	void sprite(int _x, int _y, int bank, int frame, int inval = 1);
 *		Render sprite 'bank':'frame' at (_x, _y). If
 *		inval is passed and set to 0, the affected
 *		area will *not* be invalidated automatically.
 *
 *	void sprite_fxp(int _x, int _y, int bank, int frame, int inval = 1);
 *		As sprite(), but with pixel or sub-pixel
 *		accuracy, depending on scaling and video
 *		driver.
 *
 *	void blit(int dx, int dy, window_t *src);
 *		Blit from window 'src' into this window, placing
 *		the top left corner of 'src' at (dx, dy).
 *
 *	void blit(int dx, int dy, int sx, int sy, int sw, int sh, window_t *src);
 *		Blit rectangle (sx, sy, sw, sh) from window 'src'
 *		into this window, placing the top left corner of
 *		'src' at (dx, dy).
 *
 *	int x()		{ return rect.x / xsc; }
 *	int y()		{ return rect.y / ysc; }
 *	int width()	{ return rect.w / xsc; }
 *	int height()	{ return rect.h / ysc; }
 *		Get the current screen position of the
 *		window's top-left corner, and it's size.
 *
 *	int x2()	{ return (rect.x + rect.w) / xsc; }
 *	int y2()	{ return (rect.y + rect.h) / ysc; }
 *		Get the screen position of the bottom-right
 *		corner of the window.
 */

#ifndef _WINDOW_H_
#define _WINDOW_H_

#include "sdl_compat.h"

class gfxengine_t;

class window_t
{
	friend class gfxengine_t;
  public:
	window_t();
	window_t(gfxengine_t *e);
	virtual ~window_t();

	virtual void init(gfxengine_t *e);
	void place(int left, int top, int sizex, int sizey);
	int offscreen();
	void visible(int vis);
	int visible()	{ return _visible; }
	void select();
	void invalidate(SDL_Rect *r = NULL);
	virtual void refresh(SDL_Rect *r)	{ ; }

	/* Color tools */
	Uint32 mulrgb(Uint32 c1, Uint32 c2)
	{
		int r = ((c1 >> 16) & 0xff) * ((c2 >> 16) & 0xff) * 258 >> 16;
		int g = ((c1 >> 8) & 0xff) * ((c2 >> 8) & 0xff) * 258 >> 16;
		int b = (c1 & 0xff) * (c2 & 0xff) * 258 >> 16;
		return r << 16 | g << 8 | b;
	}
	Uint32 fadergb(Uint32 rgb, Uint8 fade)
	{
		int r = ((rgb >> 16) & 0xff) * fade * 258 >> 16;
		int g = ((rgb >> 8) & 0xff) * fade * 258 >> 16;
		int b = (rgb & 0xff) * fade * 258 >> 16;
		return r << 16 | g << 8 | b;
	}

	/* Rendering */
	Uint32 map_rgb(Uint8 r, Uint8 g, Uint8 b);
	Uint32 map_rgb(Uint32 rgb);
	void foreground(Uint32 color)	{ fgcolor = color; }
	void background(Uint32 color)	{ bgcolor = color; };

	void bgimage(int bank = -1, int frame = -1);
	void colorkey(Uint32 color);
	void colorkey();
	void alpha(float a);

	void clear(SDL_Rect *r = NULL);
	void font(int fnt);
	void string(int _x, int _y, const char *txt);
	void center(int _y, const char *txt);
	void center_token(int _x, int _y, const char *txt, char token = 0);
	void string_fxp(int _x, int _y, const char *txt);
	void center_fxp(int _y, const char *txt);
	void center_token_fxp(int _x, int _y, const char *txt, char token = 0);
	int textwidth(const char *txt, int min = 0, int max = 255);
	int textwidth_fxp(const char *txt, int min = 0, int max = 255);
	int fontheight();

	void point(int _x, int _y);
	void rectangle(int _x, int _y, int w, int h);
	void fillrect(int _x, int _y, int w, int h);
	void fillrect_fxp(int _x, int _y, int w, int h);

	void sprite(int _x, int _y, int bank, int frame, int inval = 1);
	void sprite_fxp(int _x, int _y, int bank, int frame, int inval = 1);

	void blit(int dx, int dy, int sx, int sy, int sw, int sh, window_t *src);
	void blit(int dx, int dy, window_t *src);

	int x()		{ return (phys_rect.x * 256 + 128) / xs; }
	int y()		{ return (phys_rect.y * 256 + 128) / ys; }
	int x2()	{ return ((phys_rect.x + phys_rect.w) * 256 + 128) / xs; }
	int y2()	{ return ((phys_rect.y + phys_rect.h) * 256 + 128) / ys; }
	int width()	{ return x2() - x(); }
	int height()	{ return y2() - y(); }

	SDL_Rect	phys_rect;

  protected:
	window_t	*next, *prev;
	/*
	 * Kludge: This is for window selection to work.
	 * the problem is that this will break (along
	 * with some other things) if more than one
	 * gfxengine_t instance is used.
	 */
	static window_t	*selected;	//Currently selected window

	gfxengine_t	*engine;
	SDL_Surface	*surface;
	int		xs, ys;		//fixp 24:8
	Uint32		fgcolor, bgcolor;
	int		bg_bank, bg_frame;
	int		_font;
	int		_visible;
	int		_offscreen;

	void _select();		//Internal version
	void link(gfxengine_t *e);
	void unlink(void);

	void phys_refresh(SDL_Rect *r)
	{
		if(!r)
			refresh(NULL);
		else
		{
			SDL_Rect dr;
			dr.x = r->x - phys_rect.x;
			dr.y = r->y - phys_rect.y;
			int x2 = (int)(dr.x + r->w) * 256 / xs;
			int y2 = (int)(dr.y + r->h) * 256 / ys;
			dr.x = (int)dr.x * 256 / xs;
			dr.y = (int)dr.y * 256 / ys;
			dr.w = x2 - dr.x;
			dr.h = y2 - dr.y;
			refresh(&dr);
		}
	}
};

#endif
