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

GRAOUMF TRACKER 2

Copyright (c) 1996 - 2002 Laurent de Soras

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.

Contact the author : laurent@ohmforce.com
More information about this license : http://www.gnu.org/licenses/gpl.html

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



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

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

#include	"base.h"
#include	"base_ct.h"
#include	"Image.h"
#include	"intrface.h"
#include	"log.h"
#include	"memory.h"
#include	"ProgressBox.h"
#include	"resource.h"
#include	"rsc01.h"
#include	"Thread.h"



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



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



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

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



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



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

/*==========================================================================*/
/*      Nom: (constructeur)                                                 */
/*      Description: Initialise une boite de progression.                   */
/*      Parametres en entree:                                               */
/*        - title_0: pointeur sur la chaine de titre de la boite.           */
/*==========================================================================*/

ProgressBox::ProgressBox (const char *title_0)
{
	_title_0 = STRDUP (title_0);
	_total_ptr = NULL;	/* Indique qu'on n'est pas encore en mesure d'afficher la boite */
	_total_bar_size = 0;	/* Indique qu'on n'est pas encore en mesure d'afficher la progression */
}



/*==========================================================================*/
/*      Nom: (destructeur)                                                  */
/*      Description: Detruit une boite de progression                       */
/*==========================================================================*/

ProgressBox::~ProgressBox (void)
{
	if (_title_0 != NULL)
	{
		FREE (_title_0);
		_title_0 = NULL;
	}
}



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

signed int	ProgressBox::check_ok (void) const
{
	if (_title_0 == NULL)
	{
		LOG_printf ("ProgressBox::check_ok: Error: title string pointer is null.\n");
		return (-1);
	}

	MCHECK (_title_0);

	return (0);
}



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

void	ProgressBox::self_display (void) const
{

	/*** A faire ***/

}



/*==========================================================================*/
/*      Nom: manage                                                         */
/*      Description: Affiche et gere une boite de progression destinee a    */
/*                   montrer l'etat d'avancement d'un thread a              */
/*                   l'utilisateur.                                         */
/*      Parametres en entree:                                               */
/*        - total_ptr: pointeur sur un nombre indiquant la longueur totale  */
/*                     de l'operation (unite arbitraire).                   */
/*        - position_ptr: pointeur sur un nombre indiquant la progression   */
/*                        de la tache; ce nombre doit evoluer au cours du   */
/*                        temps entre 0 (0 %) et *total_ptr (100 %).        */
/*        - active_flag_ptr: pointeur sur un flag qui indique si la tache   */
/*                           est active. Ce flag doit etre mis a un avant   */
/*                           de rentrer dans la fonction. Quand la tache se */
/*                           termine (normalement ou a cause d'une erreur), */
/*                           elle doit mettre le flag a 0 pour signaler a   */
/*                           la fonction qu'elle a fini.                    */
/*      Parametres en sortie:                                               */
/*        - cancel_flag_ptr: pointeur sur un flag qui sert a la fonction a  */
/*                           signale a la tache que l'utilisateur a annule  */
/*                           l'operation. Apres avoir recu l'annulation,    */
/*                           *active_flag_ptr doit etre remis a 0 par la    */
/*                           tache. *cancel_flag_ptr doit etre a 0 avant    */
/*                           d'entrer dans la fonction. Si il est interdit  */
/*                           d'annuler,  cancel_flag_ptr doit etre NULL.    */
/*      Retour: 0 si tout s'est bien passe,                                 */
/*              1 en cas d'annulation,                                      */
/*              -1 si une erreur interne s'est produite (sans consequence   */
/*                 sur la tache courante qui se termine normalement).       */
/*==========================================================================*/

signed int	ProgressBox::manage (const volatile long *total_ptr,
                                 const volatile long *position_ptr,
                                 const volatile bool *active_flag_ptr,
                                 volatile bool *cancel_flag_ptr)
{
	int		sel_object;
	int		sel_aine;
	Image		*background_ptr;

	_total_ptr = total_ptr;
	_position_ptr = position_ptr;
	_active_flag_ptr = active_flag_ptr;
	_cancel_flag_ptr = cancel_flag_ptr;

	/* On repere l'heure d'arrivee */
	_start_time = clock ();

	/* Positionne la boite au milieu de l'ecran */
	_pix_width = RSC_get_width (RSC_OBJ_PB_BACK) + INTR_SHADOW_CARXDEC * RSC_CHAR_W;
	_pix_height = RSC_get_height (RSC_OBJ_PB_BACK) + INTR_SHADOW_PIXYDEC;
	_pix_xpos = (INTR_graph_ptr->get_width () - _pix_width) / 2;
	_pix_ypos = (INTR_graph_ptr->get_height () - _pix_height) / 2;

	RSC_flush_resource ();

	INTR_graph_ptr->mouse_bee ();

	background_ptr = INTR_graph_ptr->save_background (_pix_xpos, _pix_ypos,
	                                                 _pix_width, _pix_height);

	/* Impossible de sauver le fond, on n'affiche rien et
	   on attend que la tache se termine d'elle-meme. */
	if (background_ptr == NULL)
	{
		LOG_printf ("ProgressBox::manage: Warning: couldn't save background. Progress box not displayed.\n");
		while (*_active_flag_ptr)
		{
			Thread::sleep (1000);
		}
		INTR_graph_ptr->mouse_arrow ();
		return (-1);	/* Erreur de reservation memoire dans les chunks temporaires */
	}

	_old_percentage = -1;
	_old_bar_size = 1;
	RSC_set_width (RSC_OBJ_PB_BACK_BAR_INDIC, _old_bar_size);
	_total_bar_size = RSC_get_width (RSC_OBJ_PB_BACK_BAR);

	redraw ();
	displayed_progress_box_ptr = this;

	while (*_active_flag_ptr)
	{
		refresh (false);

		/* Annulation ? */
		INTR_graph_ptr->show_mouse ();
		INTR_graph_ptr->get_mouse (&INTR_mouse);
		RSC_gere_resource (RSC_OBJ_PB, &sel_object, &sel_aine);
		if (sel_object == RSC_OBJ_PB_BACK_CANCEL)
		{
			*_cancel_flag_ptr = true;
		}

		/* Rafraichissement tous les 1/4 de seconde */
		Thread::sleep (250);
	}

	displayed_progress_box_ptr = NULL;

	INTR_graph_ptr->restore_background (background_ptr);
	INTR_graph_ptr->mouse_arrow ();

	return (0);
}



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

void	ProgressBox::redraw (void)
{
	if (_total_ptr == NULL)
	{
		return;
	}

	/* Positionne la boite au milieu de l'ecran */
	RSC_set_absolute_object_position (RSC_OBJ_PB, _pix_xpos, _pix_ypos);
	RSC_set_relative_object_position (RSC_OBJ_PB_SHADOW,
	                                  INTR_SHADOW_CARXDEC * RSC_CHAR_W,
	                                  INTR_SHADOW_PIXYDEC);

	/* Titre */
	RSC_set_string (RSC_OBJ_PB_BACK_TITLE, _title_0);

	/* Annulation possible ou pas */
	RSC_pos_flag (RSC_OBJ_PB_BACK_CANCEL,
	              RSC_ATTR_DISABLE | RSC_ATTR_NOCLICK,
	              _cancel_flag_ptr == NULL);

	RSC_display_object (RSC_OBJ_PB);

	refresh ();
}



/*==========================================================================*/
/*      Nom: refresh                                                        */
/*      Description: Rafraichit l'affichage dynamique de la boite.          */
/*      Parametres en entree:                                               */
/*        - force_flag: force le redraw de tous les indicateurs.            */
/*==========================================================================*/

void	ProgressBox::refresh (bool force_flag)
{
	int		current_bar_size;
	signed int	new_percentage;
	double		fraction;
	clock_t	current_time;
	char		string_0 [15+1];

	/* On n'a pas encore initialise tous les parametres */
	if (_total_bar_size == 0)
	{
		return;
	}

	/* Le thread a fini */
	if (! *_active_flag_ptr)
	{
		return;
	}

	RSC_set_string (RSC_OBJ_PB_BACK_TREMT_TREMB_TREM, "  :  :  ");
	RSC_set_string (RSC_OBJ_PB_BACK_TTIMT_TTIMB_TTIM, "  :  :  ");

	if (*_total_ptr != 0 && *_position_ptr >= 0)
	{
		fraction = (double) (*_position_ptr) / (*_total_ptr);

		current_bar_size = (int) (_total_bar_size * fraction);
		current_bar_size = MIN (current_bar_size, _total_bar_size);
		if (   current_bar_size > _old_bar_size
		    || force_flag)
		{
			RSC_set_width (RSC_OBJ_PB_BACK_BAR_INDIC, current_bar_size);
			RSC_display_object (RSC_OBJ_PB_BACK_BAR_INDIC);
			_old_bar_size = current_bar_size;
		}

		new_percentage = (int) (fraction * 100);
		new_percentage = MIN (new_percentage, 100);
		if (   new_percentage != _old_percentage
		    || force_flag)
		{
			sprintf (string_0, "%3d %%", new_percentage);
			RSC_set_string (RSC_OBJ_PB_BACK_PCTB_PCT, string_0);
			RSC_display_object (RSC_OBJ_PB_BACK_PCTB);
			_old_percentage = new_percentage;
		}

		current_time = clock () - _start_time;
		if (current_time > CLOCKS_PER_SEC && fraction > 1e-5)
		{
			const long		sec = current_time / CLOCKS_PER_SEC;
			const long		total_sec = (long) (sec / fraction);
			const long		rem_sec = total_sec - sec;

			if (rem_sec >= 0)
			{
				print_time (RSC_OBJ_PB_BACK_TREMT_TREMB_TREM, rem_sec);
			}

			print_time (RSC_OBJ_PB_BACK_TTIMT_TTIMB_TTIM, total_sec);
		}
	}

	/* Informations non disponibles... */
	else
	{
		RSC_set_string (RSC_OBJ_PB_BACK_PCTB_PCT, "    %");
		RSC_display_object (RSC_OBJ_PB_BACK_PCTB);
	}

	RSC_display_object (RSC_OBJ_PB_BACK_TREMT_TREMB);
	RSC_display_object (RSC_OBJ_PB_BACK_TTIMT_TTIMB);
}



void	ProgressBox::print_time (int rsc_obj, long nbr_sec)
{
	char		string_0 [15+1];

	const long		h = nbr_sec / 3600;
	if (h >= 100)
	{
		sprintf (string_0, "Days... ");
	}
	else
	{
		const long		m = (nbr_sec / 60) % 60;
		const long		s = nbr_sec % 60;
		sprintf (string_0, "%2ld:%02ld:%02ld", h, m, s);
	}
	RSC_set_string (rsc_obj, string_0);
}



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



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



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