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

        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.

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



#if defined (_MSC_VER)
	#pragma warning (1 : 4130 4223 4705 4706)
	#pragma warning (4 : 4355 4786 4800)
#endif



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

#include	<stdio.h>
#include	<stdlib.h>

#include	"archi.h"
#include	"base.h"
#include	"base_ct.h"
#include	"DialBox.h"
#include	"Image.h"
#include	"intrface.h"
#include	"keyboard.h"
#include	"log.h"
#include	"memory.h"
#include	"os.h"
#include	"resource.h"
#include	"rsc01.h"



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



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



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



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

/* Pointeur sur l'objet affiche s'il existe, NULL sinon. */
DialBox	*DialBox::displayed_dialog_box_ptr = NULL;



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

const int	DialBox::line_object [DialBox_NBR_LIN_MAX] =
{
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L00,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L01,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L02,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L03,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L04,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L05,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L06,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L07,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L08,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L09,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L10,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L11,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L12,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L13,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L14,
	RSC_OBJ_AB_EMPTY_BACK_TBOX_L15
};

const int	DialBox::button_object [DialBox_NBR_BUT_MAX] =
{
	RSC_OBJ_AB_EMPTY_BACK_BUT_B00,
	RSC_OBJ_AB_EMPTY_BACK_BUT_B01,
	RSC_OBJ_AB_EMPTY_BACK_BUT_B02,
	RSC_OBJ_AB_EMPTY_BACK_BUT_B03,
	RSC_OBJ_AB_EMPTY_BACK_BUT_B04,
	RSC_OBJ_AB_EMPTY_BACK_BUT_B05,
	RSC_OBJ_AB_EMPTY_BACK_BUT_B06,
	RSC_OBJ_AB_EMPTY_BACK_BUT_B07
};



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



/*==========================================================================*/
/*      Nom: (constructeur)                                                 */
/*      Description: Initialise la boite de dialogue                        */
/*      Parametres en entree:                                               */
/*        - title_0: pointeur sur le titre de la boite, termine par 0.      */
/*        - text_0: pointeur sur le texte de la boite, termine par 0. Il    */
/*                  peut y avoir 8 lignes de texte de 80 caracteres maxi,   */
/*                  separees par un \n.                                     */
/*        - buttons_0: pointeur sur le texte des boutons, termine par un 0. */
/*                     Il peut y avoir 8 boutons de 20 caracteres maxi,     */
/*                     separes par des \n.                                  */
/*==========================================================================*/

DialBox::DialBox (const char *title_0, const char *text_0, const char *buttons_0)
:	_title (title_0)
,	_line_arr ()
,	_button_arr ()
,	_nbr_disp_lines (-1)
,	_nbr_buttons (-1)
,	_biggest_line_len (-1)
,	_biggest_button_len (-1)
,	_displayed_flag (false)
,	_slider_flag (false)
,	_line_offset (0)
,	_pix_width (-1)
,	_pix_height (-1)
,	_pix_x_pos (0)
,	_pix_y_pos (0)
,	_slider ()
{
	int				string_len;
	const char *	string_end_0;

/*______________________________________________
 *
 * Recherche, verifie et separe les differentes
 * chaines qui constituent les lignes et boutons
 *______________________________________________
 */

	/* Lignes de texte */
	_biggest_line_len = int (strlen (title_0));
	_nbr_disp_lines = 0;
	do
	{
		string_end_0 = strchr (text_0, '\n');
		if (string_end_0 == NULL)
		{
			string_len = int (strlen (text_0));
		}
		else
		{
			string_len = int (string_end_0 - text_0);
		}

		_line_arr.push_back (std::string (text_0, string_len));
		text_0 += string_len + 1;
		++ _nbr_disp_lines;

		_biggest_line_len = MAX (_biggest_line_len, string_len);
	}
	while (string_end_0 != NULL);

	_slider_flag = (_nbr_disp_lines >= DialBox_NBR_LIN_MAX);
	_nbr_disp_lines = MIN (_nbr_disp_lines, DialBox_NBR_LIN_MAX);

	/* Boutons */
	_biggest_button_len = 8;
	_nbr_buttons = 0;
	do
	{
		string_end_0 = strchr (buttons_0, '\n');
		if (string_end_0 == NULL)
		{
			string_len = int (strlen (buttons_0));
		}
		else
		{
			string_len = int (string_end_0 - buttons_0);
		}

		_button_arr.push_back (std::string (buttons_0, string_len));
		buttons_0 += string_len + 1;
		_nbr_buttons ++;

		_biggest_button_len = MAX (_biggest_button_len, string_len);
	}
	while (string_end_0 != NULL);

	_biggest_button_len += 2;		/* Rajoute 2 espaces de chaque cote de la chaine */

	if (_nbr_buttons >= DialBox_NBR_BUT_MAX)
	{
		LOG_printf ("DialBox::DialBox: Warning: Too much buttons in dialog box.\n");
	}

	_slider.changed_pos_flag = false;
	_slider.direction = SLID_DIRECTION_VERTICAL;
	_slider.glisse_pos = -1;
	_slider.pix_pos = 0;
	_slider.pix_win = 0;
	_slider.sbar_object = RSC_OBJ_AB_EMPTY_BACK_SB_SBAR;
	_slider.slider_object = RSC_OBJ_AB_EMPTY_BACK_SB_SBAR_SLIDER;
	_slider.no_graphic_limit_flag = false;
	_slider.inf = 0;
	_slider.sup = 0;
	_slider.virtual_len = long (_line_arr.size ());
	_slider.virtual_pos = _line_offset;
	_slider.virtual_win = _nbr_disp_lines;
}



/*==========================================================================*/
/*      Nom: (destructeur)                                                  */
/*      Description: Detruit la boite de dialogue                           */
/*==========================================================================*/

DialBox::~DialBox ()
{
}



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

signed int	DialBox::check_ok (void) const
{
	int				ret_val = 0;

	if (_nbr_buttons < 1)
	{
		LOG_printf ("DialBox::check_ok: Error: no button in dialog box.\n");
		ret_val = -1;
	}

	return (ret_val);
}



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

void	DialBox::self_display (void) const
{

	/*** A faire ***/

}



/*==========================================================================*/
/*      Nom: manage                                                         */
/*      Description: Gere la boite de dialogue, apres l'avoir affichee.     */
/*      Parametres en entree:                                               */
/*        - default_button: Numero du bouton sous lequel doit etre la       */
/*                          souris quand la boite de dialogue apparait, ou  */
/*                          -1 si aucun.                                    */
/*        - cancel_button: Bouton d'annulation par defaut (touche ESC). -1  */
/*                         si aucun.                                        */
/*      Retour: 0 a (n-1): numero du bouton clique, ou -1 en cas d'erreur.  */
/*==========================================================================*/

signed int	DialBox::manage (signed int default_button, signed int cancel_button)
{
	int		box_pix_width;
	int		box_pix_height;
	signed int	sel_object;
	signed int	sel_aine;
	signed int	sel_button;
	signed int	button;
	LWORD		key;
	int		scan_code;
	Image		*background_ptr;

/*______________________________________________
 *
 * Calcule les dimensions et coordonnees de
 * la boite.
 *______________________________________________
 */

	/* Largeur: 1 + 1 + ligne + 1 + 1 ou 1 + bouton + 2 + ... + bouton + 1 */
	_pix_width =   MAX (_biggest_line_len + 4,
	                    (_biggest_button_len + 2) * _nbr_buttons)
					 * RSC_CHAR_W;
	if (_slider_flag)
	{
		_pix_width += RSC_CHAR_W + RSC_get_width (RSC_OBJ_AB_EMPTY_BACK_SB_SBAR);
	}

	/* Hauteur:   1 interl + 1 boxtext + 1 interl
					+ (1 interl + 1 char) * lignes + 1 interl
					+ 1 interl + 1 boxtext + 1 interl         */
	_pix_height =   _nbr_disp_lines * (RSC_CHAR_H + RSC_V_SPACE)
	              + RSC_BOXTEXT_H * 2 + RSC_V_SPACE * 5;

	/* Cadre */
	box_pix_width = _pix_width + INTR_SHADOW_CARXDEC * RSC_CHAR_W;
	box_pix_height = _pix_height + INTR_SHADOW_PIXYDEC;
	if (   box_pix_width > INTR_graph_ptr->get_width ()
		 || box_pix_height > INTR_graph_ptr->get_height ())
	{
		LOG_printf ("DialBox::manage: Error: Dialog box doesn't fit in screen.\n");
		return (-1);
	}

	/* Coordonnees de la boite */
	if (default_button >= 0 && default_button < _nbr_buttons)
	{
		_pix_x_pos = INTR_mouse.x - (  default_button * (_biggest_button_len + 2)
									 		 + _biggest_button_len / 2 + 1) * RSC_CHAR_W;
	}
	else
	{
		_pix_x_pos = INTR_mouse.x - _pix_width / 2;
	}
	_pix_x_pos = MAX (MIN (_pix_x_pos, INTR_graph_ptr->get_width () - box_pix_width), 0);
	_pix_y_pos = INTR_mouse.y - _pix_height + RSC_V_SPACE + RSC_BOXTEXT_H / 2;
	_pix_y_pos = MAX (MIN (_pix_y_pos, INTR_graph_ptr->get_height () - box_pix_height), 0);

	/* Sauvegarde du decor */
	background_ptr = INTR_graph_ptr->save_background (_pix_x_pos, _pix_y_pos,
	                                                 box_pix_width, box_pix_height);
	if (background_ptr == NULL)
	{
		LOG_printf ("DialBox::manage: Error: couldn't save background.\n");
		return (-1);
	}

	/* Affichage */
	DialBox::displayed_dialog_box_ptr = this;
	_displayed_flag = true;
	redraw ();

	/* Gestion de la boite */
	sel_object = -1;
	sel_aine = -1;
	sel_button = -1;
	INTR_wait_mouse (true);
	KEYB_flush_keyboard_buffer ();

	while (sel_button < 0)
	{
		/* Souris */
		INTR_graph_ptr->show_mouse ();
		INTR_graph_ptr->get_mouse (&INTR_mouse);
		RSC_gere_resource (RSC_OBJ_AB_EMPTY, &sel_object, &sel_aine);

		switch (sel_object)
		{
		case	RSC_OBJ_AB_EMPTY_BACK_SB_SBAR:
		case	RSC_OBJ_AB_EMPTY_BACK_SB_SBAR_SLIDER:
			SLID_gere_slider (&_slider, sel_object);
			if (_slider.changed_pos_flag)
			{
				change_disp_line (_slider.virtual_pos);
			}
			break;

		case	RSC_OBJ_AB_EMPTY_BACK_SB_UP:
			change_disp_line (_line_offset - 1);
			break;

		case	RSC_OBJ_AB_EMPTY_BACK_SB_DOWN:
			change_disp_line (_line_offset + 1);
			break;

		default:
			if (sel_object >= 0)
			{
				for (button = 0; button < _nbr_buttons; button ++)
				{
					if (DialBox::button_object [button] == sel_object)
					{
						sel_button = button;
						break;
					}
				}
				if (sel_button < 0)
				{
					LOG_printf ("DialBox::manage: Error: bad selection (object # %d).\n", sel_object);
					break;
				}
			}
		}

		/* Touches */
		if (OS_key_pressed ())
		{
			key = OS_get_key ();
			scan_code = (key >> 16) & 0xFF;
			if (scan_code == KEYB_SCANCODE_RETURN)
			{
				sel_button = default_button;
			}
			else if (scan_code == KEYB_SCANCODE_ESC)
			{
				sel_button = cancel_button;
			}
		}
	}

	/* Remet le decor */
	RSC_flush_resource ();
	_displayed_flag = false;
	DialBox::displayed_dialog_box_ptr = NULL;
	INTR_graph_ptr->restore_background (background_ptr);

	return (sel_button);
}



/*==========================================================================*/
/*      Nom: redraw                                                         */
/*      Description: Affiche la boite de dialogue, si celle-ci est prete.   */
/*==========================================================================*/

void	DialBox::redraw (bool content_only_flag)
{
	int		text_pix_width;
	int		text_pix_height;
	int		x;
	int		y;
	int		line;
	int		button;

	if (! _displayed_flag)
	{
		return;
	}

	RSC_flush_resource ();

	/* Titre */
	RSC_set_string (RSC_OBJ_AB_EMPTY_BACK_TITLE, _title.c_str ());

	RSC_set_width (RSC_OBJ_AB_EMPTY_BACK, _pix_width);
	RSC_set_height (RSC_OBJ_AB_EMPTY_BACK, _pix_height);
	RSC_set_absolute_object_position (RSC_OBJ_AB_EMPTY, _pix_x_pos, _pix_y_pos);

	/* Ombre */
	RSC_set_absolute_object_position (RSC_OBJ_AB_EMPTY_SHADOW,
												 _pix_x_pos + INTR_SHADOW_CARXDEC * RSC_CHAR_W,
												 _pix_y_pos + INTR_SHADOW_PIXYDEC);
	RSC_set_width (RSC_OBJ_AB_EMPTY_SHADOW, _pix_width);
	RSC_set_height (RSC_OBJ_AB_EMPTY_SHADOW, _pix_height);

	/* Boite du texte */
	text_pix_width = _pix_width - RSC_CHAR_W * 2;
	text_pix_height = _nbr_disp_lines * (RSC_CHAR_H + RSC_V_SPACE) + RSC_V_SPACE;
	if (_slider_flag)
	{
		text_pix_width -= RSC_CHAR_W + RSC_get_width (RSC_OBJ_AB_EMPTY_BACK_SB_SBAR);
	}
	RSC_set_width (RSC_OBJ_AB_EMPTY_BACK_TBOX, text_pix_width);
	RSC_set_height (RSC_OBJ_AB_EMPTY_BACK_TBOX, text_pix_height);

	/* Les lignes */
	x = _pix_x_pos + RSC_CHAR_W * 2;
	y = _pix_y_pos + RSC_BOXTEXT_H + RSC_V_SPACE * 3;
	for (line = 0; line < DialBox_NBR_LIN_MAX; line ++)
	{
		const int		cur_line = _line_offset + line;
		if (cur_line < int (_line_arr.size ()))
		{
			RSC_set_string (DialBox::line_object [line], _line_arr [cur_line].c_str ());
			RSC_set_absolute_object_position (DialBox::line_object [line], x, y);
			RSC_clear_flag (DialBox::line_object [line], RSC_ATTR_NOTDISP);
			y += RSC_CHAR_H + RSC_V_SPACE;
		}
		else
		{
			RSC_set_flag (DialBox::line_object [line], RSC_ATTR_NOTDISP);
		}
	}

	/* Les boutons */
	x = _pix_x_pos + RSC_CHAR_W;
	y = _pix_y_pos + _pix_height - RSC_BOXTEXT_H - RSC_V_SPACE;
	for (button = 0; button < DialBox_NBR_BUT_MAX; button ++)
	{
		if (button < _nbr_buttons)
		{
			RSC_set_string (DialBox::button_object [button], _button_arr [button].c_str ());
			RSC_set_absolute_object_position (DialBox::button_object [button], x, y);
			RSC_set_width (DialBox::button_object [button], _biggest_button_len * RSC_CHAR_W);
			RSC_clear_flag (DialBox::button_object [button], RSC_ATTR_NOTDISP | RSC_ATTR_SELECTED);
			x += (_biggest_button_len + 2) * RSC_CHAR_W;
		}
		else
		{
			RSC_set_flag (DialBox::button_object [button], RSC_ATTR_NOTDISP);
		}
	}

	/* Slider */
	if (_slider_flag)
	{
		int			tbox_x;
		int			tbox_y;
		RSC_get_relative_object_position (
			RSC_OBJ_AB_EMPTY_BACK_TBOX,
			tbox_x,
			tbox_y
		);
		RSC_set_relative_object_position (
			RSC_OBJ_AB_EMPTY_BACK_SB,
			tbox_x + text_pix_width + RSC_CHAR_W,
			tbox_y
		);

		const int	up_height = RSC_get_height (RSC_OBJ_AB_EMPTY_BACK_SB_UP);
		const int	down_height = RSC_get_height (RSC_OBJ_AB_EMPTY_BACK_SB_DOWN);
		const int	sbar_height = text_pix_height - up_height - down_height;
		RSC_set_height (RSC_OBJ_AB_EMPTY_BACK_SB_SBAR, sbar_height);

		const int		down_y = up_height + sbar_height;
		RSC_set_relative_object_position (
			RSC_OBJ_AB_EMPTY_BACK_SB_DOWN,
			0,
			down_y
		);

		_slider.virtual_pos = _line_offset;

		RSC_clear_flag (RSC_OBJ_AB_EMPTY_BACK_SB, RSC_ATTR_NOTDISP);
	}
	else
	{
		RSC_set_flag (RSC_OBJ_AB_EMPTY_BACK_SB, RSC_ATTR_NOTDISP);
	}

	/* Affichage */
	if (content_only_flag)
	{
		RSC_display_object (RSC_OBJ_AB_EMPTY_BACK_TBOX);
	}
	else
	{
		RSC_display_object (RSC_OBJ_AB_EMPTY);
	}
	if (_slider_flag)
	{
		SLID_display_slider (&_slider);
	}
}



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



void	DialBox::change_disp_line (int disp_line)
{
	const int		limit = int (_line_arr.size ()) - _nbr_disp_lines;
	_line_offset = MAX (MIN (disp_line, limit), 0);
	redraw (true);
}



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



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