/*****************************************************************************

        GRAOUMF TRACKER 2
        Author: Laurent de Soras, 1996-2016

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*****************************************************************************/



#define	Graphic_CURRENT_MODULE
#define	MODULE_OS



/*\\\ FICHIERS INCLUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include	<assert.h>
#include	<stdio.h>

#define	WIN32_LEAN_AND_MEAN
#define	NOMINMAX
#include <windows.h>
#include <windowsx.h>

#include	"archi.h"
#include	"base.h"
#include	"base_ct.h"
#include	"Font.h"
#include	"Graphic.h"
#include	"Image.h"
#include	"log.h"
#include	"os.h"
#include	"resource.h"



/*\\\ CONSTANTES PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ CONSTANTES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

int	Graphic::COL_BLACK = 16;
int	Graphic::COL_WHITE = 31;
int	Graphic::COL_BLUE_STD = 17;
int	Graphic::COL_RED = 39;

int	Graphic::COL_GREY_DARK = 24;
int	Graphic::COL_GREY_NORMAL = 25;
int	Graphic::COL_GREY_BRIGHT = 26;
int	Graphic::COL_GREY_BLUE_DARK = 28;
int	Graphic::COL_GREY_BLUE_NORMAL = 29;
int	Graphic::COL_GREY_BLUE_BRIGHT = 30;

int	Graphic::COL_RED_GRAD = 32;
int	Graphic::COL_GREEN_GRAD = 40;
int	Graphic::COL_BLUE_GRAD = 48;
int	Graphic::COL_YELLOW_GRAD = 56;
int	Graphic::COL_CYAN_GRAD = 64;
int	Graphic::COL_MAGENTA_GRAD = 72;
int	Graphic::COL_GREY_GRAD = 80;



/*\\\ TYPES & STRUCTURES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ DEFINITION DES VARIABLES DE CLASSE PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\*/

/* Couleurs, composantes RVB de 0 a 87 */
LWORD	Graphic::color_palette [256] =
{
	// 00 - 1F
	0x000000L, 0x8080FFL, 0xFF0000L, 0xFF40FFL, 0x800000L, 0xC060FFL, 0xFF4000L, 0xFF80FFL,
	0x808080L, 0xA0A0A0L, 0xC0C0C0L, 0xFFFFFFL, 0x808090L, 0xA0A0B0L, 0xC0C0D0L, 0xFFFFFFL,
	0x000000L, 0x8080FFL, 0xFF0000L, 0xFF40FFL, 0x800000L, 0xC060FFL, 0xFF4000L, 0xFF80FFL,
	0x808080L, 0xA0A0A0L, 0xC0C0C0L, 0xFFFFFFL, 0x808090L, 0xA0A0B0L, 0xC0C0D0L, 0xFFFFFFL,
	// 20 - 3F
	0x200000L, 0x400000L, 0x600000L, 0x800000L, 0xA00000L, 0xC00000L, 0xE00000L, 0xFF0000L,
	0x002000L, 0x004000L, 0x006000L, 0x008000L, 0x00A000L, 0x00C000L, 0x00E000L, 0x00FF00L,
	0x000020L, 0x000040L, 0x000060L, 0x000080L, 0x0000A0L, 0x0000C0L, 0x0000E0L, 0x0000FFL,
	0x202000L, 0x404000L, 0x606000L, 0x808000L, 0xA0A000L, 0xC0C000L, 0xE0E000L, 0xFFFF00L,
	// 40 - 5F
	0x002020L, 0x004040L, 0x006060L, 0x008080L, 0x00A0A0L, 0x00C0C0L, 0x00E0E0L, 0x00FFFFL,
	0x200020L, 0x400040L, 0x600060L, 0x800080L, 0xA000A0L, 0xC000C0L, 0xE000E0L, 0xFF00FFL,
	0x202020L, 0x404040L, 0x606060L, 0x808080L, 0xA0A0A0L, 0xC0C0C0L, 0xE0E0E0L, 0xFFFFFFL,
	0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L,
	// 60 - 7F
	0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L,
	0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L,
	0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L,
	0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L,
	// 80 - 9F
	/*
	0x004080L, 0x005090L, 0x0060A0L, 0x0070B0L, 0x0080C0L, 0x0090D0L, 0x00A0E0L, 0x00B0F0L,
	0x00C0FFL, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L,
	0x804080L, 0x905090L, 0xA060A0L, 0xB070B0L, 0xC080C0L, 0xD090D0L, 0xE0A0E0L, 0xF0B0F0L,
	0xFFC0FFL, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L, 0x000000L,
	*/
	// ...
};



/*\\\ DEFINITION DES VARIABLES DE CLASSE PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ METHODES PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*==========================================================================*/
/*      Nom: (constructeur)                                                 */
/*      Description: Initialise et installe le mode graphique et installe   */
/*                   la souris.                                             */
/*      Parametres en entree:                                               */
/*        - width: largeur de la zone graphique en pixels. Si la valeur est */
/*                 nulle, la taille d'ecran par defaut est utilisee.        */
/*        - height: hauteur de la zone graphique en pixels.                 */
/*==========================================================================*/

Graphic::Graphic (int width, int height)
{
	RECT		win_rect;
	RECT		old_rect;

	_graph_mode_flag = false;
	_bitmap_ptr = NULL;
	_font_ptr = NULL;
	_screen_ptr = NULL;

	_width = MAX (width, Graphic_MIN_WIDTH);
	_height = MAX (height, Graphic_MIN_HEIGHT);
	_line_width = (_width + 7) & ~7;

	SetRect (&win_rect, 0, 0, _width, _height);
	AdjustWindowRectEx (&win_rect,
	                    GetWindowStyle (OS_window_handle),
	                    GetMenu (OS_window_handle) != NULL,
	                    GetWindowExStyle (OS_window_handle));
	GetWindowRect (OS_window_handle, &old_rect);
	MoveWindow (OS_window_handle, old_rect.left, old_rect.top,
	            win_rect.right - win_rect.left,
	            win_rect.bottom - win_rect.top,
	            TRUE);

	_clip_xmin = 0;
	_clip_ymin = 0;
	_clip_xmax = _width;
	_clip_ymax = _height;
	_lock_flag = false;

	if (init_bitmap ())
	{
		LOG_printf ("Graphic::Graphic: Error: couldn't create bitmap object.\n");
		return;
	}

/*______________________________________________
 *
 * Chargement de la fonte
 *______________________________________________
 */

	_font_ptr = new Font ();
	if (_font_ptr == NULL)
	{
		LOG_printf ("Graphic::Graphic: Error: couldn't create font object.\n");
		return;
	}
	if (_font_ptr->check_ok ())
	{
		LOG_printf ("Graphic::Graphic: Error: Font object error.\n");
		return;
	}

	_graph_mode_flag = true;

	UpdateWindow (OS_window_handle);
}



/*==========================================================================*/
/*      Nom: (destructeur)                                                  */
/*      Description: Desinstalle le mode graphique precedemment installe.   */
/*==========================================================================*/

Graphic::~Graphic (void)
{
	if (_font_ptr != NULL)
	{
		delete _font_ptr;
		_font_ptr = NULL;
	}

	restore_bitmap ();
}



/*==========================================================================*/
/*      Nom: check_ok                                                       */
/*      Description: Verifie l'intergrite de l'objet.                       */
/*      Retour: 0 si l'objet parait correct.                                */
/*==========================================================================*/

signed int	Graphic::check_ok (void) const
{
	if (_bitmap_ptr == NULL)
	{
		LOG_printf ("Graphic::check_ok: Error: bitmap object pointer is null.\n");
		return (-1);
	}

	if (_screen_ptr == NULL)
	{
		LOG_printf ("Graphic::check_ok: Error: Screen surface pointer is null.\n");
		return (-1);
	}

	if (_font_ptr == NULL)
	{
		LOG_printf ("Graphic::check_ok: Error: Font object pointer is null.\n");
		return (-1);
	}

	if (_font_ptr->check_ok ())
	{
		LOG_printf ("Graphic::check_ok: Error: Font object is corrupted.\n");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: self_display                                                   */
/*      Description: Affiche "textuellement" le contenu courant de l'objet. */
/*==========================================================================*/

void	Graphic::self_display (void) const
{

	/*** A faire ***/

}



bool	Graphic::get_graph_mode_flag (void) const
{
	return (_graph_mode_flag);
}



int	Graphic::get_width (void) const
{
	return (_width);
}



int	Graphic::get_height (void) const
{
	return (_height);
}



/****************************************************************************/
/*                                                                          */
/*      PRIMITIVES GRAPHIQUES                                               */
/*                                                                          */
/****************************************************************************/



signed int	Graphic::set_font (int size_x, int size_y, const UBYTE *bitmap_ptr)
{
	return (_font_ptr->set_font (size_x, size_y, bitmap_ptr));
}



void	Graphic::set_palette (const LWORD palette [256])
{
	int		i;
	HDC		hdc;
	RGBQUAD	palette_info [256];

	for (i = 0; i < 256; i ++)
	{
		color_palette [i] = palette [i];
		palette_info [i].rgbRed = (color_palette [i] >> 16) & 0xFF;
		palette_info [i].rgbGreen = (color_palette [i] >> 8) & 0xFF;
		palette_info [i].rgbBlue = color_palette [i] & 0xFF;
		palette_info [i].rgbReserved = PC_NOCOLLAPSE;
	}

	if (_bitmap_ptr != NULL)
	{
		hdc = CreateCompatibleDC (NULL);
		SelectObject (hdc, _bitmap_ptr);

		SetDIBColorTable (hdc, 0, 256, palette_info);

		DeleteDC (hdc);
	}
}



void	Graphic::get_palette (LWORD palette [256])
{
	int		i;

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



/*==========================================================================*/
/*      Nom: set_color                                                      */
/*      Description: Fixe la valeur RVB d'une certaine couleur (index dans  */
/*                   la palette).                                           */
/*      Parametres en entree:                                               */
/*        - pen: numero de la couleur recherchee.                           */
/*        - rvb: Composantes au format 0x00rrvvbb.                          */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	Graphic::set_color (int pen, LWORD rvb)
{
	HDC		hdc;
	RGBQUAD	palette_info;

	color_palette [pen] = rvb;
	palette_info.rgbRed = (rvb >> 16) & 0xFF;
	palette_info.rgbGreen = (rvb >> 8) & 0xFF;
	palette_info.rgbBlue = rvb & 0xFF;
	palette_info.rgbReserved = PC_NOCOLLAPSE;

	if (_bitmap_ptr != NULL)
	{
		hdc = CreateCompatibleDC (NULL);
		SelectObject (hdc, _bitmap_ptr);

		SetDIBColorTable (hdc, pen, 1, &palette_info);

		DeleteDC (hdc);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: display_point                                                  */
/*      Description: Affiche un point.                                      */
/*      Parametres en entree:                                               */
/*        - x: abscisse d'affichage, en pixels.                             */
/*        - y: ligne d'affichage, en pixels.                                */
/*        - color: couleur du point.                                        */
/*==========================================================================*/

void	Graphic::display_point (int x, int y, int color)
{
	lock_screen (x, y, 1, 1);

	*(_screen_ptr + _line_width * y + x) = color;
}



/*==========================================================================*/
/*      Nom: display_hline                                                  */
/*      Description: Affiche une ligne horizontale.                         */
/*      Parametres en entree:                                               */
/*        - x: abscisse d'affichage, en pixels.                             */
/*        - y: ligne d'affichage, en pixels.                                */
/*        - l: longueur de la ligne, en pixels.                             */
/*        - color: couleur de la ligne.                                     */
/*==========================================================================*/

void	Graphic::display_hline (int x, int y, int l, int color)
{
	UBYTE		*draw_ptr;

	lock_screen (x, y, l, 1);

	draw_ptr = _screen_ptr + _line_width * y + x;
	if (l > 0)
	{
		do
		{
			*draw_ptr++ = color;
			l --;
		}
		while (l > 0);
	}
}



/*==========================================================================*/
/*      Nom: display_vline                                                  */
/*      Description: Affiche une ligne verticale.                           */
/*      Parametres en entree:                                               */
/*        - x: abscisse d'affichage, en pixels.                             */
/*        - y: ligne d'affichage, en pixels.                                */
/*        - h: hauteur de la ligne, en pixels.                              */
/*        - color: couleur de la ligne.                                     */
/*==========================================================================*/

void	Graphic::display_vline (int x, int y, int h, int color)
{
	UBYTE		*draw_ptr;

	lock_screen (x, y, 1, h);

	draw_ptr = _screen_ptr + _line_width * y + x;
	if (h > 0)
	{
		do
		{
			*draw_ptr = color;
			draw_ptr += _line_width;
			h --;
		}
		while (h > 0);
	}
}



void	Graphic::display_line (int x1, int y1, int x2, int y2, int color, ULWORD pattern)
{
	UBYTE		*draw_ptr;
	int		l;
	int		h;
	int		al;
	int		ah;
	int		inc_2;
	ULWORD	inc_frac;
	UWORD		pos_frac;

	l = x2 - x1;
	h = y2 - y1;
	al = ABS (l);
	ah = ABS (h);

	lock_screen (MIN (x1, x2), MIN (y1, y2), al + 1, ah + 1);

	/* Trace de lignes verticales */
	if (al < ah)
	{
		/* Rearrange les points de facon a ce que le trace
			soit toujours oriente vers la droite. */
		if (l < 0)
		{
			x1 += l;
			y1 += h;
			l = -l;
			h = -h;
		}

		draw_ptr = _screen_ptr + _line_width * y1 + x1;
		inc_2 = SGN (h) * _line_width;
		inc_frac = ((ULWORD)al << 16) / ah;
		pos_frac = 0x8000U;

		do
		{
			if (pattern & (1UL << (ah & 0x1F)))
			{
				*draw_ptr = color;
			}
			draw_ptr += inc_2;
			pos_frac += (UWORD) inc_frac;
			if (pos_frac < inc_frac)
			{
				draw_ptr ++;
			}
			ah --;
		}
		while (ah >= 0);
	}

	/* Trace de lignes horizontales */
	else
	{
		/* Rearrange les points de facon a ce que le trace
			soit toujours oriente vers le bas. */
		if (h < 0)
		{
			x1 += l;
			y1 += h;
			l = -l;
			h = -h;
		}

		/* Cas particulier (celui-la il arrive quand point) */
		if (al == 0)
		{
			display_point (x1, y1, color);
		}

		else
		{
			draw_ptr = _screen_ptr + _line_width * y1 + x1;
			inc_2 = SGN (l);
			inc_frac = ((ULWORD)ah << 16) / al;
			pos_frac = 0x8000U;

			do
			{
				if (pattern & (1UL << (al & 0x1F)))
				{
					*draw_ptr = color;
				}
				draw_ptr += inc_2;
				pos_frac += (UWORD) inc_frac;
				if (pos_frac < inc_frac)
				{
					draw_ptr += _line_width;
				}
				al --;
			}
			while (al >= 0);
		}
	}
}



/*==========================================================================*/
/*      Nom: display_box                                                    */
/*      Description: Affiche un simple rectangle.                           */
/*      Parametres en entree:                                               */
/*        - cadre: pointeur sur une structure d'infos sur le cadre.         */
/*                 La couleur du rectangle est donnee par la couleur du     */
/*                 fond.                                                    */
/*==========================================================================*/

void	Graphic::display_box (Graphic_FRAME_INFO *cadre)
{
	int		x;
	int		y;
	int		l;
	int		h;
	int		color;
	int		line_cnt;
	int		point_cnt;
	UBYTE		*h_draw_ptr;
	UBYTE		*v_draw_ptr;

	x = cadre->x;
	y = cadre->y;
	l = cadre->l;
	h = cadre->h;
	color = cadre->backgc;

	lock_screen (x, y, l, h);

	v_draw_ptr = _screen_ptr + _line_width * y + x;
	for (line_cnt = 0; line_cnt < h; line_cnt ++)
	{
		h_draw_ptr = v_draw_ptr;
		for (point_cnt = 0; point_cnt < l; point_cnt ++)
		{
			*h_draw_ptr++ = color;
		}
		v_draw_ptr += _line_width;
	}
}



/*==========================================================================*/
/*      Nom: display_xorbox                                                 */
/*      Description: Affiche un simple rectangle en mode XOR.               */
/*      Parametres en entree:                                               */
/*        - cadre: pointeur sur une structure d'infos sur le cadre.         */
/*                 La couleur du rectangle est donnee par la couleur du     */
/*                 fond.                                                    */
/*==========================================================================*/

void	Graphic::display_xorbox (Graphic_FRAME_INFO *cadre)
{
	int		x;
	int		y;
	int		l;
	int		h;
	int		color;
	int		line_cnt;
	int		point_cnt;
	UBYTE		*h_draw_ptr;
	UBYTE		*v_draw_ptr;

	x = cadre->x;
	y = cadre->y;
	l = cadre->l;
	h = cadre->h;
	color = cadre->backgc;

	lock_screen (x, y, l, h);

	v_draw_ptr = _screen_ptr + _line_width * y + x;
	for (line_cnt = 0; line_cnt < h; line_cnt ++)
	{
		h_draw_ptr = v_draw_ptr;
		for (point_cnt = 0; point_cnt < l; point_cnt ++)
		{
			*h_draw_ptr ^= color;
			h_draw_ptr ++;
		}
		v_draw_ptr += _line_width;
	}
}



/*==========================================================================*/
/*      Nom: display_string                                                 */
/*      Description: Affiche une chaine de caracteresen mode "replace".     */
/*      Parametres en entree:                                               */
/*        - text: adresse du texte, termine par un '0'.                     */
/*        - x: abscisse d'affichage, en pixels.                             */
/*        - y: ligne d'affichage, en pixels.                                */
/*        - color: couleur du texte.                                        */
/*==========================================================================*/

void	Graphic::display_string (const char *text_0, int x, int y, int text_color, int bkgnd_color, int max_len)
{
	int		c;
	int		inc_x;

	while ((c = (unsigned char)(*text_0++)) != 0 && max_len > 0)
	{
		display_char (x, y, c, text_color, bkgnd_color);
		inc_x = _font_ptr->_size_x;
		if (inc_x == 0)
		{
			inc_x = _font_ptr->_prop_c [c].size_x;
		}
		x += inc_x;
		max_len --;
	}
}



/*==========================================================================*/
/*      Nom: display_string_transp                                          */
/*      Description: Affiche une chaine de caracteres en transparence.      */
/*      Parametres en entree:                                               */
/*        - text0: pointeur sur une chaine se terminant par 0.              */
/*        - x: abscisse du texte, en pixels.                                */
/*        - y: ordonnee du texte, en pixels.                                */
/*        - text_color: couleur du texte.                                   */
/*==========================================================================*/

void	Graphic::display_string_transp (const char *text_0, int x, int y, int text_color, int max_len)
{
	int		c;
	int		inc_x;

	while ((c = (unsigned char)(*text_0++)) != 0 && max_len > 0)
	{
		display_char_transp (x, y, c, text_color);
		inc_x = _font_ptr->_size_x;
		if (inc_x == 0)
		{
			inc_x = _font_ptr->_prop_c [c].size_x;
		}
		x += inc_x;
		max_len --;
	}
}



/*==========================================================================*/
/*      Nom: display_string_transp_shadow                                   */
/*      Description: Affiche une chaine de caracteres en transparence,      */
/*                   avec ombrage.                                          */
/*      Parametres en entree:                                               */
/*        - text0: pointeur sur une chaine se terminant par 0.              */
/*        - x: abscisse du texte, en pixels.                                */
/*        - y: ordonnee du texte, en pixels.                                */
/*        - text_color: couleur du texte.                                   */
/*        - shadow_color: couleur de l'ombre.                               */
/*==========================================================================*/

void	Graphic::display_string_transp_shadow (const char *text0, int x, int y, int text_color, int shadow_color, int max_len)
{
	display_string_transp (text0, x + 1, y + 1, shadow_color, max_len);
	display_string_transp (text0, x, y, text_color, max_len);
}



/*==========================================================================*/
/*      Nom: display_cadre_int                                              */
/*      Description: Affiche un cadre avec bordure interieure.              */
/*      Parametres en entree:                                               */
/*        - cadre_ptr: pointeur sur une structure d'infos sur le cadre.     */
/*==========================================================================*/

void	Graphic::display_cadre_int (Graphic_FRAME_INFO *cadre_ptr)
{
	int		x;
	int		y;
	int		l;
	int		h;
	Graphic_FRAME_INFO	cadre_2;

	x = cadre_ptr->x;
	y = cadre_ptr->y;
	l = cadre_ptr->l;
	h = cadre_ptr->h;

	/* Le fond */
	cadre_2.x = x + 1;
	cadre_2.y = y + 1;
	cadre_2.l = l - 2;
	cadre_2.h = h - 2;
	cadre_2.backgc = cadre_ptr->backgc;
	display_box (&cadre_2);

	/* Bords eclaires */
	display_hline (x, y, l, cadre_ptr->lborderc);
	display_vline (x, y, h, cadre_ptr->lborderc);

	/* Bords ombres */
	display_hline (x, y+h-1, l, cadre_ptr->sborderc);
	display_vline (x+l-1, y, h, cadre_ptr->sborderc);

	/* Les 2 coins intermediaires */
	display_point (x+l-1, y, cadre_ptr->nborderc);
	display_point (x, y+h-1, cadre_ptr->nborderc);
}



/*==========================================================================*/
/*      Nom: display_cadre_ext                                              */
/*      Description: Affiche un cadre avec bordure exterieure.              */
/*      Parametres en entree:                                               */
/*        - cadre_ptr: pointeur sur une structure d'infos sur le cadre.     */
/*==========================================================================*/

void	Graphic::display_cadre_ext (Graphic_FRAME_INFO *cadre_ptr)
{
	int		x;
	int		y;
	int		l;
	int		h;

	x = cadre_ptr->x;
	y = cadre_ptr->y;
	l = cadre_ptr->l;
	h = cadre_ptr->h;

	/* Le fond */
	display_box (cadre_ptr);

	/* Bords eclaires */
	display_hline (x-1, y-1, l+2, cadre_ptr->lborderc);
	display_vline (x-1, y-1, h+2, cadre_ptr->lborderc);

	/* Bords ombres */
	display_hline (x-1, y+h, l+2, cadre_ptr->sborderc);
	display_vline (x+l, y-1, h+2, cadre_ptr->sborderc);

	/* Les 2 coins intermediaires */
	display_point (x+l, y-1, cadre_ptr->nborderc);
	display_point (x-1, y+h, cadre_ptr->nborderc);
}



/*==========================================================================*/
/*      Nom: disable_zone                                                   */
/*      Description: Grise un zone rectangulaire de l'ecran.                */
/*      Parametres en entree:                                               */
/*        - x, y: coordonnees du coin haut-gauche                           */
/*        - l, h: dimensions du rectangle, en pixels.                       */
/*        - color: couleur de remplissage.                                  */
/*==========================================================================*/

void	Graphic::disable_zone (int x, int y, int l, int h, int color)
{
	int		point_cnt;
	UBYTE		*v_draw_ptr;
	UBYTE		*h_draw_ptr;

	lock_screen (x, y, l, h);

	v_draw_ptr = _screen_ptr + _line_width * y + x;
	for (; h > 0; h --)
	{
		h_draw_ptr = v_draw_ptr;
		point_cnt = 0;
		if (((x + y) & 1) != 0)
		{
			h_draw_ptr ++;
			point_cnt ++;
		}

		for ( ; point_cnt < l; point_cnt += 2)
		{
			*h_draw_ptr = color;
			h_draw_ptr += 2;
		}

		v_draw_ptr += _line_width;
		y ++;
	}
}



/*==========================================================================*/
/*      Nom: invert_2_colors                                                */
/*      Description: Inverse les 2 premieres couleurs rencontrees dans une  */
/*                   zone rectangulaire.                                    */
/*      Parametres en entree:                                               */
/*        - x, y: coordonnees du coin haut-gauche                           */
/*        - l, h: dimensions du rectangle, en pixels.                       */
/*        - color: couleur par defaut si on ne trouve qu'une seule couleur  */
/*      Retour: la premiere couleur trouvee.                                */
/*==========================================================================*/

int	Graphic::invert_2_colors (int x, int y, int l, int h, int color)
{
	int		point_cnt_h;
	int		point_cnt_v;
	UBYTE		*v_draw_ptr;
	UBYTE		*h_draw_ptr;
	int		color_1;
	int		color_2;

	lock_screen (x, y, l, h);

	v_draw_ptr = _screen_ptr + _line_width * y + x;
	color_1 = *v_draw_ptr;
	for (point_cnt_v = h; point_cnt_v > 0; point_cnt_v --)
	{
		h_draw_ptr = v_draw_ptr;
		for (point_cnt_h = l; point_cnt_h > 0; point_cnt_h --)
		{
			color_2 = *h_draw_ptr++;
			if (color_2 != color_1)
			{
				break;
			}
		}

		if (color_2 != color_1)
		{
			break;
		}
		v_draw_ptr += _line_width;
	}

	if (point_cnt_v <= 0)
	{
		color_2 = color;
	}

	v_draw_ptr = _screen_ptr + _line_width * y + x;
	for (point_cnt_v = h; point_cnt_v > 0; point_cnt_v --)
	{
		h_draw_ptr = v_draw_ptr;
		for (point_cnt_h = l; point_cnt_h > 0; point_cnt_h --)
		{
			color = *h_draw_ptr;
			if (color == color_1)
			{
				color = color_2;
			}
			else if (color == color_2)
			{
				color = color_1;
			}
			*h_draw_ptr++ = color;
		}

		v_draw_ptr += _line_width;
	}

	return (color_1);
}



void	Graphic::get_image (UBYTE *ptr_data, int x, int y, int l, int h)
{
	UBYTE		*draw_ptr;

	lock_screen (x, y, l, h);

	draw_ptr = _screen_ptr + _line_width * y + x;
	for (; h > 0; h --)
	{
		memcpy (ptr_data, draw_ptr, l);
		draw_ptr += _line_width;
		ptr_data += l;
	}
}



void	Graphic::put_image (const UBYTE *ptr_data, int x, int y, int l, int h)
{
	UBYTE		*draw_ptr;

	lock_screen (x, y, l, h);

	draw_ptr = _screen_ptr + _line_width * y + x;
	for (; h > 0; h --)
	{
		memcpy (draw_ptr, ptr_data, l);
		draw_ptr += _line_width;
		ptr_data += l;
	}
}



/*==========================================================================*/
/*      Nom: put_image                                                      */
/*      Description: Affiche une image dans un mode graphique 256 couleurs, */
/*                   en mode transparent.                                   */
/*      Parametres en entree:                                               */
/*        - ptr_image: pointeur sur les donnees de l'image a afficher.      */
/*        - x, y: Coordonnees du point haut-gauche                          */
/*        - l, h: Dimensions de l'image.                                    */
/*        - t_color: Couleur de transparence (non affichee).                */
/*==========================================================================*/

void	Graphic::put_image (const UBYTE *ptr_data, int x, int y, int l, int h, int t_color)
{
	UBYTE		pixel;
	int		point_cnt;
	UBYTE		*v_draw_ptr;
	UBYTE		*h_draw_ptr;

	lock_screen (x, y, l, h);

	v_draw_ptr = _screen_ptr + _line_width * y + x;
	for (; h > 0; h --)
	{
		h_draw_ptr = v_draw_ptr;
		for (point_cnt = 0; point_cnt < l; point_cnt ++)
		{
			pixel = *ptr_data++;
			if (pixel != t_color)
			{
				*h_draw_ptr = pixel;
			}
			h_draw_ptr ++;
		}
		v_draw_ptr += _line_width;
	}
}



void	Graphic::put_rectangle (const UBYTE *ptr_data, int img_w, int src_x, int src_y, int src_w, int src_h, int dest_x, int dest_y)
{
	const UBYTE	*v_data_ptr;
	UBYTE		*v_draw_ptr;

	lock_screen (dest_x, dest_y, src_w, src_h);

	v_data_ptr = ptr_data + (long)img_w * src_y + src_x;
	v_draw_ptr = _screen_ptr + _line_width * dest_y + dest_x;
	for (; src_h > 0; src_h --)
	{
		memcpy (v_draw_ptr, v_data_ptr, src_w);
		v_draw_ptr += _line_width;
		v_data_ptr += img_w;
	}
}



void	Graphic::put_rectangle (const UBYTE *ptr_data, int img_w, int src_x, int src_y, int src_w, int src_h, int dest_x, int dest_y, int t_color)
{
	UBYTE		pixel;
	int		point_cnt;
	const UBYTE	*v_data_ptr;
	const UBYTE	*h_data_ptr;
	UBYTE		*v_draw_ptr;
	UBYTE		*h_draw_ptr;

	lock_screen (dest_x, dest_y, src_w, src_h);

	v_data_ptr = ptr_data + (long)img_w * src_y + src_x;
	v_draw_ptr = _screen_ptr + _line_width * dest_y + dest_x;
	for (; src_h > 0; src_h --)
	{
		h_data_ptr = v_data_ptr;
		h_draw_ptr = v_draw_ptr;
		for (point_cnt = 0; point_cnt < src_w; point_cnt ++)
		{
			pixel = *h_data_ptr++;
			if (pixel != t_color)
			{
				*h_draw_ptr = pixel;
			}
			h_draw_ptr ++;
		}
		v_draw_ptr += _line_width;
		v_data_ptr += img_w;
	}
}



/*==========================================================================*/
/*      Nom: save_background                                                */
/*      Description: Sauve une zone de l'ecran dans un buffer. L'ecran est  */
/*                   ensuite debloque.                                      */
/*      Parametres en entree:                                               */
/*        - pixxpos, pixypos: coordonnees du bloc a sauver                  */
/*        - pixwidth, pixheight: taille du bloc                             */
/*      Retour: Image representant le decor sauve. Renvoie NULL en cas      */
/*              d'erreur.                                                   */
/*==========================================================================*/

Image	*Graphic::save_background (int pixxpos, int pixypos, int pixwidth, int pixheight)
{
	UBYTE			*data_ptr;
	Image			*bkgd_ptr;

	bkgd_ptr = new Image (pixwidth, pixheight);
	if (bkgd_ptr == NULL)
	{
		LOG_printf ("Graphic::save_background: Error: couldn't create object for background data.\n");
		return (NULL);
	}
	if (bkgd_ptr->check_ok ())
	{
		LOG_printf ("Graphic::save_background: Error: couldn't create object for background data.\n");
		return (NULL);
	}

	bkgd_ptr->_pos_x = pixxpos;
	bkgd_ptr->_pos_y = pixypos;
	data_ptr = bkgd_ptr->get_data_ptr ();

	/* Sauvegarde du fond */
	get_image (data_ptr, pixxpos, pixypos, pixwidth, pixheight);
	unlock_screen ();

	return (bkgd_ptr);
}



/*==========================================================================*/
/*      Nom: restore_background                                             */
/*      Description: Restitue le fond sauve avec OS_save_background.        */
/*      Parametres en entree:                                               */
/*        - bkgd_ptr: Pointeur sur l'image du decor, qui est ensuite        */
/*                    detruite.                                             */
/*==========================================================================*/

void	Graphic::restore_background (Image *&bkgd_ptr)
{
	/* Restitution du fond */
	put_image (bkgd_ptr->get_data_ptr (), bkgd_ptr->_pos_x, bkgd_ptr->_pos_y,
	           bkgd_ptr->get_width (), bkgd_ptr->get_height());
	unlock_screen ();

	/* Liberation de l'image */
	delete bkgd_ptr;
	bkgd_ptr = NULL;
}



bool	Graphic::get_screen_lock_flag (void)
{
	return (_lock_flag);
}



signed int	Graphic::lock_screen (int x, int y, int w, int h)
{
	assert (x >= 0);
	assert (y >= 0);
	assert (w >= 0);
	assert (h >= 0);
	assert (x + w <= _width);
	assert (y + h <= _height);

	if (! get_screen_lock_flag ())
	{
		_clip_xmin = x;
		_clip_ymin = y;
		_clip_xmax = x + w;
		_clip_ymax = y + h;
		_lock_flag = true;
	}
	else
	{
		_clip_xmin = MIN (_clip_xmin, x);
		_clip_ymin = MIN (_clip_ymin, y);
		_clip_xmax = MAX (_clip_xmax, x + w);
		_clip_ymax = MAX (_clip_ymax, y + h);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: unlock_screen                                                  */
/*      Description: Informe le systeme qu'on n'a plus besoin de l'usage    */
/*                   exclusif de l'ecran. Cette commande doit etre toujours */
/*                   passee a la fin d'un redraw utilisant les commandes    */
/*                   precedentes (OS_...). Les routines resource actionnent */
/*                   cette commande automatiquement.                        */
/*      Retour: 0 si tout s'est bien passe                                  */
/*==========================================================================*/

signed int	Graphic::unlock_screen (void)
{
	HDC		hdc;
	HRGN		clipping_region;
	int		ret_val;

	if (get_screen_lock_flag ())
	{
		hdc = GetDC (OS_window_handle);
		if (hdc != NULL)
		{
			clipping_region = CreateRectRgn (_clip_xmin, _clip_ymin,
			                                 _clip_xmax, _clip_ymax);
			ret_val = ExtSelectClipRgn (hdc, clipping_region, RGN_AND);
			if (   ret_val != ERROR
			    && ret_val != NULLREGION)
			{
				paint (hdc);
			}
			DeleteObject (clipping_region);
		}
		ReleaseDC (OS_window_handle, hdc);

		_lock_flag = false;
	}

	return (0);
}



void		Graphic::paint (HDC hdc)
{
	HDC		context;

	context = CreateCompatibleDC (NULL);
	SelectObject (context, _bitmap_ptr);
	BitBlt (hdc,
		     0, 0,					// Coordinates of destination rectangle's upper-left corner
		     _width, _height,	// Dimension of destination rectangle
		     context,				// handle to source device context
		     0, 0,					// Coordinates of source rectangle's upper-left corner
		     SRCCOPY);				// Raster operation code
	DeleteDC (context);
}



signed int	Graphic::change_resolution (int width, int height)
{
	restore_bitmap ();

	_width = MAX (width, Graphic_MIN_WIDTH);
	_height = MAX (height, Graphic_MIN_HEIGHT);
	_line_width = (_width + 7) & ~7;

	if (init_bitmap ())
	{
		return (-1);
	}

	_graph_mode_flag = true;

	return (0);
}



/*\\\ METHODES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



signed int Graphic::init_bitmap (void)
{
	HDC		hdc;
	int		i;
	struct
	{
		BITMAPINFOHEADER	header;
		RGBQUAD	colors [256];
	}			info =
	{
		{
			sizeof (info.header),
			0, 0, 1, 8, BI_RGB, 0,
			0, 0, 256, 256
		}
	};

/*______________________________________________
 *
 * Creation de la palette
 *______________________________________________
 */

	/* Initialise la palette */
	for (i = 0; i < 256; i ++)
	{
		info.colors [i].rgbBlue = color_palette [i] & 0xFF;
		info.colors [i].rgbGreen = (color_palette [i] >> 8) & 0xFF;
		info.colors [i].rgbRed = (color_palette [i] >> 16) & 0xFF;
		info.colors [i].rgbReserved = 0;
	}

/*______________________________________________
 *
 * Bitmap de l'ecran
 *______________________________________________
 */
	
	info.header.biWidth = _line_width;
	info.header.biHeight = -_height;
	hdc = GetDC (OS_window_handle);
	_bitmap_ptr = CreateDIBSection (hdc, (BITMAPINFO *)(&info), DIB_RGB_COLORS, (void **) (&_screen_ptr), NULL, 0);
	ReleaseDC (OS_window_handle, hdc);
	GdiFlush ();
	
	return (0);
}



void	Graphic::restore_bitmap (void)
{
	unlock_screen ();
	_graph_mode_flag = false;

	if (_bitmap_ptr != NULL)
	{
		DeleteObject (_bitmap_ptr);
		_bitmap_ptr = NULL;
	}
}



/****************************************************************************/
/*                                                                          */
/*      PRIMITIVES GRAPHIQUES                                               */
/*                                                                          */
/****************************************************************************/



void	Graphic::display_char (int x, int y, int c, int color, int bkgnd_color)
{
	int		size_x;
	int		size_y;
	int		line_cpt;
	int		point_cpt;
	UBYTE		*data_ptr;
	UBYTE		*v_draw_ptr;
	UBYTE		*h_draw_ptr;

	/* Trouve l'adresse du caractere ainsi que ses dimensions */
	size_x   = _font_ptr->_size_x;
	size_y   = _font_ptr->_size_y;
	data_ptr = _font_ptr->_data_ptr;
	if (size_x == 0)		/* Fonte proportionnelle */
	{
		data_ptr += _font_ptr->_prop_c [c].offset;
		size_x    = _font_ptr->_prop_c [c].size_x;
	}
	else						/* Fonte non-proportionnelle */
	{
		data_ptr += size_x * size_y * c;
	}

	lock_screen (x, y, size_x, size_y);

	v_draw_ptr = _screen_ptr + _line_width * y + x;
	for (line_cpt = 0; line_cpt < size_y; line_cpt ++)
	{
		/* Trace la ligne */
		h_draw_ptr = v_draw_ptr;
		for (point_cpt = 0; point_cpt < size_x; point_cpt ++)
		{
			if (*data_ptr++ != 0)
			{
				*h_draw_ptr++ = color;
			}
			else
			{
				*h_draw_ptr++ = bkgnd_color;
			}
		}
		v_draw_ptr += _line_width;
	}
}



void	Graphic::display_char_transp (int x, int y, int c, int color)
{
	int		size_x;
	int		size_y;
	int		line_cpt;
	int		point_cpt;
	UBYTE		*data_ptr;
	UBYTE		*v_draw_ptr;
	UBYTE		*h_draw_ptr;

	/* Trouve l'adresse du caractere ainsi que ses dimensions */
	size_x   = _font_ptr->_size_x;
	size_y   = _font_ptr->_size_y;
	data_ptr = _font_ptr->_data_ptr;
	if (size_x == 0)		/* Fonte proportionnelle */
	{
		data_ptr += _font_ptr->_prop_c [c].offset;
		size_x    = _font_ptr->_prop_c [c].size_x;
	}
	else						/* Fonte non-proportionnelle */
	{
		data_ptr += size_x * size_y * c;
	}

	lock_screen (x, y, size_x, size_y);

	v_draw_ptr = _screen_ptr + _line_width * y + x;
	for (line_cpt = 0; line_cpt < size_y; line_cpt ++)
	{
		/* Trace la ligne */
		h_draw_ptr = v_draw_ptr;
		for (point_cpt = 0; point_cpt < size_x; point_cpt ++)
		{
			if (*data_ptr++ != 0)
			{
				*h_draw_ptr++ = color;
			}
			else
			{
				h_draw_ptr ++;
			}
		}
		v_draw_ptr += _line_width;
	}
}



long	Graphic::get_imagesize (int size_x, int size_y)
{
	return ((long)size_x * size_y);
}



/****************************************************************************/
/*                                                                          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/
