//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// rendfe.cc
//
// Vincent LE PRINCE <vincentleprince@users.sourceforge.net>
// Copyright (C) 2000-2001 Vincent LE PRINCE
// This file is part of the TRUEVISION Package

//   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., 675 Mass Ave, Cambridge, MA 02139, USA.  */ 
//*******************************************************************************************
#include <sys/fcntl.h>
#include <unistd.h>
#include <iostream>
#include "include/preferences.h"
#include "include/scene.h"
#include "include/rendfe.h"
#include "config.h"
#include "include/dlgutils.h"

//************************************************
// Définitions
//************************************************
#define MAX_RES 10000     // Résolution maximum de l'image
const char *defoutput = "Untitled";      // Nom de l'image par défaut
// Type de fichiers
const int ftype_num = 4;
const char *ftype_list[ftype_num] = { "PNG", "PPM", "TGA", "TGA+RLE" };
const char ftype_output[ftype_num] = { 'N', 'P', 'T', 'C' };
// Méthode d'antialiasing
const int antimet_num = 2;
const char *antimet_list[antimet_num] = { N_("Adaptative"), N_("Recursive") };



//***********************************************
// Constructeur
//***********************************************
RendererFE::RendererFE( app_objs *appref )
{
app_ref = appref;

// Boite d'options
opt_dialog = NULL;
width = new TvWidget_int( _("Width"), "WIDTH", NULL, app_ref, 400 );
	width->set_range( MAX_RES, 1, 10 );
height = new TvWidget_int( _("Height"), "HEIGHT", NULL, app_ref, 300 );
	height->set_range( MAX_RES, 1, 10 );
display = new TvWidget_bool( _("Display"), "DISP", _("Display computed image during calculation"), app_ref, true );
antialias = new TvWidget_bool( _("Use antialiasing"), "ANTIALIAS", NULL, app_ref, true );
alpha = new TvWidget_bool( _("Use alpha"), "ALPHA", _("Set background as a transparent color"), app_ref, false );
radiosity = new TvWidget_bool( _("Use radiosity (experimental)"), "RADIO", NULL, app_ref, false );
jitter = new TvWidget_bool( _("Use jitter"), "JITTER", NULL, app_ref, true );
filename = new TvWidget_path( _("Output file"), "OFILE", NULL, app_ref, defoutput );
fimage_type = new TvWidget_option_combo( _("File format"), "FORMAT", NULL, app_ref );
	fimage_type->set_list( ftype_list, ftype_num, 0 );
anti_met = new TvWidget_option_combo( _("Method"), "AMET", NULL, app_ref );
	anti_met->set_list( antimet_list, antimet_num, 0 );
quality = new TvWidget_int( _("Quality"), "QUALITY", NULL, app_ref, 9 );
	quality->set_range( 9, 1, 1 );
anti_depth = new TvWidget_int( _("Depth (adaptative mode only)"), "ADEPTH", NULL, app_ref, 2 );
	anti_depth->set_range( 10, 1, 1 );
threshold = new TvWidget_float( _("Threshold"), "THRES", NULL, app_ref, 0.3 );
	threshold->set_range( 1, 0, 0.1 );
jamount = new TvWidget_float( _("Jitter amount"), "JAMOUNT", NULL, app_ref, 0.5 );
	jamount->set_range( 1, 0, 0.1 );

// Boite de rendu
tempfile = NULL;
rend_dialog = NULL;

// Console
cons_dialog = NULL;
cons_id = -1;
console = NULL;
}



//***********************************************
// Options Dialog
//***********************************************
void RendererFE::raise_opt_dlg()
{
if ( GNOME_IS_DIALOG(opt_dialog) ) return;
PREF_DEF
opt_applied = false;
bool tt = pref->tooltips->value();

opt_dialog = gnome_dialog_new( _("Rendering options"), GNOME_STOCK_BUTTON_OK, GNOME_STOCK_BUTTON_APPLY, GNOME_STOCK_BUTTON_CANCEL,NULL );
gtk_window_set_wmclass( GTK_WINDOW(opt_dialog), PACKAGE, "Rendering options" );
gtk_signal_connect( GTK_OBJECT(opt_dialog), "clicked", GTK_SIGNAL_FUNC(sign_opt_dlg_click), this );
gtk_signal_connect( GTK_OBJECT(opt_dialog), "delete_event", GTK_SIGNAL_FUNC(sign_opt_dlg_destroy), this );

// Conteneur
GtkWidget *notebk = gtk_notebook_new();
gtk_box_pack_start( GTK_BOX(GNOME_DIALOG(opt_dialog)->vbox), notebk, FALSE, TRUE, 5 );
GtkWidget *box1, *box2, *vbox, *hbox;

// PAGE OUTPUT
vbox = dlg_new_page( notebk, N_("Output") );
	// Frame File
	hbox = dlg_simple_box_frame( _("File options"), vbox );
		filename->get_widget( hbox, tt );	
		dlg_double_box( hbox, box1, box2 );
		fimage_type->get_widget( box1, tt );
		width->get_widget( box2, tt );
		height->get_widget( box2, tt );
		quality->get_widget( box1, tt );
		
	// Frame antialiasing
	dlg_double_box_frame( _("Antialiasing"), vbox, box1, box2 );
		antialias->get_widget( box1, tt );
		anti_met->get_widget( box1, tt );
		anti_depth->get_widget( box1, tt );
		jitter->get_widget( box1, tt );	
		jamount->get_widget( box2, tt );
		threshold->get_widget( box2, tt );

	// Frame Misc.
	dlg_double_box_frame( _("Misc."), vbox, box1, box2 );	
		display->get_widget( box1, tt );
		alpha->get_widget( box1, tt );
		radiosity->get_widget( box2, tt );

gtk_widget_show_all(opt_dialog);
}

//***********************************************
// Flush options dialog
//***********************************************
void RendererFE::flush_opt_dlg()
{
filename->flush();
width->flush();
height->flush();
fimage_type->flush();
anti_met->flush();
display->flush();
alpha->flush();
antialias->flush();
radiosity->flush();
jitter->flush();
quality->flush();
anti_depth->flush();
threshold->flush();
jamount->flush();
}

//***********************************************
// Flush options dialog
//***********************************************
void RendererFE::undo_opt_dlg()
{
filename->undo();
width->undo();
height->undo();
fimage_type->undo();
anti_met->undo();
display->undo();
alpha->undo();
antialias->undo();
radiosity->undo();
jitter->undo();
quality->undo();
anti_depth->undo();
threshold->undo();
jamount->undo();
}


//***********************************************
// Clicked
//***********************************************
void RendererFE::clicked_opt_dlg( gint button )
{
switch( button )
	{
	case 0:
		flush_opt_dlg();
		close_opt_dlg();
		break;

	case 1:
		flush_opt_dlg();
		opt_applied = true;
		break;

	default:
	case 2:
		if ( opt_applied ) undo_opt_dlg();
		close_opt_dlg();
		break;
	}
}




//***********************************************
// Render dialog
//***********************************************
void RendererFE::raise_rend_dlg()
{
if ( GNOME_IS_DIALOG(rend_dialog) ) return;

rend_dialog = gnome_dialog_new (_("Render"), NULL);
gtk_window_set_wmclass( GTK_WINDOW(rend_dialog), PACKAGE, "Render" );
gtk_signal_connect( GTK_OBJECT(rend_dialog), "clicked", GTK_SIGNAL_FUNC(sign_rend_dlg_click), this );
gtk_signal_connect( GTK_OBJECT(rend_dialog), "delete_event", GTK_SIGNAL_FUNC(sign_rend_dlg_destroy), this );

GtkWidget *vbox = gtk_vbox_new (FALSE, 5);
gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (rend_dialog)->vbox), vbox, TRUE, TRUE, 0);

// Statut
GtkWidget *hbox = gtk_hbox_new (FALSE, 2);
gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
	GtkWidget *label = gtk_label_new (_(" Status : "));
	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  	stat_label = gtk_label_new ( "" );
  	gtk_box_pack_start (GTK_BOX (hbox), stat_label, FALSE, FALSE, 0);

// Progress bar
progressbar = gtk_progress_bar_new ();
gtk_box_pack_start (GTK_BOX (vbox), progressbar, FALSE, FALSE, 0);
gtk_progress_set_show_text( GTK_PROGRESS(progressbar), TRUE );
gtk_progress_set_text_alignment( GTK_PROGRESS(progressbar), 0.5, 0.3 );

// Console  button
console_chk = gtk_check_button_new_with_label (_("Show console"));
gtk_signal_connect( GTK_OBJECT(console_chk), "clicked", GTK_SIGNAL_FUNC(sign_cons_open), this );
gtk_box_pack_start (GTK_BOX (vbox), console_chk, FALSE, FALSE, 0);

// Bouttons
//GtkWidget *dialog_action_area1 = GNOME_DIALOG (rend_dialog)->action_area;
  gnome_dialog_append_button_with_pixmap (GNOME_DIALOG (rend_dialog), _("Start"), GNOME_STOCK_PIXMAP_EXEC);
  rend_button_start = (GtkWidget*)g_list_last (GNOME_DIALOG (rend_dialog)->buttons)->data;
  GTK_WIDGET_SET_FLAGS (rend_button_start, GTK_CAN_DEFAULT);
  gnome_dialog_append_button_with_pixmap (GNOME_DIALOG (rend_dialog), _("Stop"), GNOME_STOCK_PIXMAP_STOP);
  gnome_dialog_append_button (GNOME_DIALOG (rend_dialog), GNOME_STOCK_BUTTON_CLOSE);

// Console text
cons_box = gtk_hbox_new( TRUE, 5 );
cons_text = gtk_text_new( NULL, NULL );
gtk_container_add( GTK_CONTAINER(cons_box), cons_text );

gtk_widget_show_all( rend_dialog );
}

//***********************************************
// Rend Clicked
//***********************************************
void RendererFE::clicked_rend_dlg( gint button )
{
switch( button )
	{
	case 0:
		start_rendering();
		break;

	case 1:
		finish_render();
		break;		

	default:
	case 2:
		destroy_cons_dlg();
		if ( GTK_IS_WIDGET(cons_text) ) gtk_widget_destroy( cons_text );
		close_rend_dlg();
		finish_render();
		break;
	}
}

//***********************************************
// Start rendering
//***********************************************
void RendererFE::start_rendering()
{
// Export au format pov - fichier temporaire
SCENE_DEF
gtk_label_set_text( GTK_LABEL(stat_label), _("Exporting to Povray ...") );
tempfile = scene->get_temp_povray_file();

// Commande à passer au terminal
char cmd[500];
char cmbp[50];
PREF_DEF
strcpy( cmd, pref->povcmd->value() );
sprintf( cmbp, " +V +I%s +H%u +W%u +O%s +F%c +Q%u +AM%u", 
	tempfile, height->value(), width->value(), filename->value(), ftype_output[fimage_type->value()], quality->value(), anti_met->value()+1 );
strcat( cmd, cmbp );
if ( display->value() ) strcat( cmd, " +D" );
if ( radiosity->value() ) strcat( cmd, " +QR" );
if ( alpha->value() ) strcat( cmd, " +UA" );
if ( antialias->value() )
	{
	sprintf( cmbp, " +A%1.1f +R%u", threshold->value(), anti_depth->value() );
	strcat( cmd, cmbp );
	if ( jitter->value() )
		{
		sprintf( cmbp, " +J%1.1f", jamount->value() );
		strcat( cmd, cmbp );		
		}
	}
strcat( cmd, " &> /dev/stdout &" );
//cout << "\nCommand to invoke pov : " << cmd;

// Lancement du processus
console = popen( cmd, "r" );
if ( console == NULL ) { app_warning( _("Can't open pipe") ); return; }
cons_id = gtk_idle_add( sign_povray_cons, this );
gtk_widget_set_sensitive( rend_button_start, FALSE );

// Effacement de la console
gtk_text_backward_delete( GTK_TEXT(cons_text), gtk_text_get_length(GTK_TEXT(cons_text)) );
}

//************************************
// Fin du rendu
//************************************
void RendererFE::finish_render()
{
if ( console != NULL ) pclose( console );
if ( cons_id != -1 ) gtk_idle_remove( cons_id );
cons_id = -1;
console = NULL;
if ( GTK_IS_WIDGET(progressbar) ) gtk_progress_bar_update( GTK_PROGRESS_BAR(progressbar), 0 );
if ( GTK_IS_WIDGET(rend_button_start) ) gtk_widget_set_sensitive( rend_button_start, TRUE );
unlink( tempfile );
}

//************************************
// Fonction d'analyse du flux pov
//************************************
void RendererFE::povray_cons()
{
char line[255];
int offset = 0;
char ch;

while ( !feof(console) )
	{
	ch = getc( console );
	if ( ch == '\n' || ch == '\r' ) { line[offset++] = '\n'; break; }
	else line[offset++] = ch;
	if ( rend_done && !strncmp( line, "Click on window to exit...", 26 ) )
		{
		gtk_label_set_text( GTK_LABEL(stat_label), _("Please, close window.") ); 
		break;
		}
	}
if ( offset == 0 ) return;
line[offset] = '\0';
gtk_text_insert( GTK_TEXT(cons_text), NULL, NULL, NULL, line, -1 );
if ( !strncmp( line, "Parsing", 7 ) ) { gtk_label_set_text( GTK_LABEL(stat_label), _("Parsing ...") ); return; }
if ( !strncmp( line, "Displaying", 10 ) ) { gtk_label_set_text( GTK_LABEL(stat_label), _("Displaying ...") ); return; }
if ( !strncmp( line, "Rendering...", 12 ) ) { gtk_label_set_text( GTK_LABEL(stat_label), _("Rendering ...") ); return; }
if ( !strncmp( line, "Done Tracing", 12 ) ) { gtk_label_set_text( GTK_LABEL(stat_label), _("Done") ); return; }
if ( !strncmp( line, "    Total Time", 14 ) ) { finish_render(); return; }
if ( strstr( line, "Rendering line " ) )
	{
	gfloat res;
	unsigned int per, tot;
	sscanf( line + 25, "%u of %u", &per, &tot );
	res = (gfloat)(per) / (gfloat)(tot);
	gtk_progress_bar_update( GTK_PROGRESS_BAR(progressbar), res );
	rend_done = ( res == 1 ) ? true : false;
	return;
	}
if ( strstr( line, "ERROR" ) ) { gtk_label_set_text( GTK_LABEL(stat_label), _("Error") ); finish_render(); return; }
if ( strstr( line, "Cannot" ) ) { gtk_label_set_text( GTK_LABEL(stat_label), _("Error") ); finish_render(); return; }
}


//***********************************************
// Console dialog
//***********************************************
void RendererFE::raise_console_dlg()
{
if ( cons_dialog != NULL && !GTK_TOGGLE_BUTTON(console_chk)->active ) { destroy_cons_dlg(); return; }
if ( GTK_TOGGLE_BUTTON(console_chk)->active )
	{
	cons_dialog = gnome_dialog_new( N_("Rendering console"), GNOME_STOCK_BUTTON_CLOSE, NULL );
	gtk_window_set_policy( GTK_WINDOW(cons_dialog), TRUE, TRUE, FALSE );
	gtk_widget_set_usize( cons_dialog, 500, 350 );
	gtk_window_set_wmclass( GTK_WINDOW(cons_dialog), PACKAGE, "Rendering console" );
	gtk_signal_connect( GTK_OBJECT(cons_dialog), "clicked", GTK_SIGNAL_FUNC(sign_cons_dlg_click), this );
	gtk_signal_connect( GTK_OBJECT(cons_dialog), "delete_event", GTK_SIGNAL_FUNC(sign_cons_dlg_destroy), this );
	
	GtkWidget *hbox = gtk_hbox_new( FALSE, 2 );
	gtk_container_add( GTK_CONTAINER(GNOME_DIALOG(cons_dialog)->vbox), hbox );

	GtkObject *adj = gtk_adjustment_new( 0, 0, 100, 1, 10, 0 );
	gtk_text_set_adjustments( GTK_TEXT(cons_text), NULL, GTK_ADJUSTMENT(adj) );
	gtk_widget_reparent( cons_text, hbox );

	GtkWidget *scroll = gtk_vscrollbar_new( GTK_ADJUSTMENT(adj) );
	gtk_box_pack_start( GTK_BOX(hbox), scroll, FALSE, FALSE, 0 );
	
	gtk_widget_show_all(cons_dialog);
	}
}

//***********************************************
// Destroy console
//***********************************************
void RendererFE::destroy_cons_dlg()
{
if ( GTK_IS_WIDGET(cons_dialog) ) 
	{
	gtk_widget_reparent( cons_text, cons_box );
	gtk_widget_destroy( cons_dialog );
	cons_dialog = NULL;
	gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(console_chk), FALSE );
	return;
	}
}
