//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// povfe.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/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#include "include/povfe.h"
#include "include/dlgutils.h"
#include "include/preferences.h"
#include <sys/wait.h>
#include <zlib.h>
#include "config.h"
#include "include/scene.h"
#include "include/tvio.h"

// Types de fichiers en sortie
const int file_type_num = 4;
const char *file_type_list[file_type_num] = { "PNG", "PPM", "TGA", "TGA+RLE" };
const char *file_type_output[file_type_num] = { "N", "P", "T", "C" };
const char *default_ofile = "Untitled";
const int anti_met_num = 2;
const char *anti_met_list[anti_met_num] = { N_("Adaptative"), N_("Recursive") };
const int image_max_size = 5000;

// Image size presets
const int size_presets_num = 7;
const char *size_presets_names[ size_presets_num ] = { "None", "160x120", "320x240", "640x480", "800x600", "1024x768", "1280x1024" };
const int size_presets_width[ size_presets_num ] = { 0, 160, 320, 640, 800, 1024, 1280 };
const int size_presets_heigth[ size_presets_num ] = { 0, 120, 240, 480, 600, 768, 1024 };

//ofstream debug( "debug.txt" );


//**************************************************************
// Constructeur
//**************************************************************
const int predef_obj_num = 6;
const char *predef_obj_list[predef_obj_num] = { N_("Sphere"), N_("Box"), N_("Cone"),  N_("Ground"), N_("Sky sphere"), N_("Fisheye sky") };

PovrayFE::PovrayFE( app_objs *appref )
{
app_ref = appref;
PREF_DEF
widget = NULL;
image = NULL;
current_material = NULL;
start_button = NULL;
progress_bar = NULL;
pipe_console_name = NULL;
predef_sizes = NULL;
options_box = NULL;
render_box = NULL;
statusbar = NULL;
fifo_console = NULL;
cons_dlg = NULL;
render_status = false;
options_selected_page = 0;

filename = new TvWidget_path( N_("Output file"), "OFILE", NULL, app_ref, default_ofile );
tv_widgets.push_back( filename );
fimage_type = new TvWidget_option_combo( N_("File format"), "FORMAT", NULL, app_ref );
fimage_type->set_list( file_type_list, file_type_num, 0 );
tv_widgets.push_back( fimage_type );

width = new TvWidget_int( N_("Width"), "W", NULL, app_ref,  640 );
width->set_range( image_max_size, 1, 1 );
tv_widgets.push_back( width );

height = new TvWidget_int( N_("Height"), "H", NULL, app_ref, 480 );
height->set_range( image_max_size, 1, 1 );
tv_widgets.push_back( height );

start_col = new TvWidget_int( N_("Starting column"), "SC", NULL, app_ref, 1 );
start_col->set_range( image_max_size, 1, 1 );
tv_widgets.push_back( start_col );
start_row = new TvWidget_int( N_("Starting row"), "SR", NULL, app_ref, 1 );
start_row->set_range( image_max_size, 1, 1 );
tv_widgets.push_back( start_row );
end_col = new TvWidget_int( N_("Ending column"), "EC", NULL, app_ref, image_max_size );
end_col->set_range( image_max_size, 1, 1 );
tv_widgets.push_back( end_col );
end_row = new TvWidget_int( N_("Ending row"), "ER", NULL, app_ref, image_max_size );
end_row->set_range( image_max_size, 1, 1 );
tv_widgets.push_back( end_row );

quality = new TvWidget_int( N_("Quality"), "QUA", NULL, app_ref, 9 );
quality->set_range( 9, 1, 1 );
tv_widgets.push_back( quality );
alpha = new TvWidget_bool( N_("Use alpha (png & tga only)"), "ALPHA", _("Set background as a transparent color"), app_ref, false );
tv_widgets.push_back( alpha );
bits_per_color = new TvWidget_int( N_("Bits per color (png only)"), "BPC", NULL, app_ref, 8 );
bits_per_color->set_range( 16, 5, 1 );
tv_widgets.push_back( bits_per_color );
radiosity = new TvWidget_bool( N_("Use radiosity (experimental)"), "RADIO", NULL, app_ref, false );
tv_widgets.push_back( radiosity );

antialias = new TvWidget_bool( N_("Antialiasing"), "ANTI", NULL, app_ref, true );
tv_widgets.push_back( antialias );
anti_met = new TvWidget_option_combo( N_("Method"), "AMET", NULL, app_ref );
anti_met->set_list( anti_met_list, anti_met_num, 0 );
tv_widgets.push_back( anti_met );
anti_depth = new TvWidget_int( N_("Depth (adaptative mode only)"), "ADEPTH", NULL, app_ref, 2 );
anti_depth->set_range( 10, 1, 1 );
tv_widgets.push_back( anti_depth );
threshold = new TvWidget_float( N_("Threshold"), "THRES", NULL, app_ref, 0.3 );
threshold->set_range( 1, 0, 0.1 );
tv_widgets.push_back( threshold );
jitter = new TvWidget_bool( N_("Use jitter"), "JITTER", NULL, app_ref, true );
tv_widgets.push_back( jitter );
jamount = new TvWidget_float( N_("Jitter amount"), "JAMOUNT", NULL, app_ref, 0.5 );
jamount->set_range( 1, 0, 0.1 );
tv_widgets.push_back( jamount );

display = new TvWidget_bool( N_("Display"), "DISP", _("Display computed image during calculation"), app_ref, true );
tv_widgets.push_back( display );
mosaic  = new TvWidget_bool( N_("Mosaic preview"), "MOS", NULL, app_ref, false );
tv_widgets.push_back( mosaic );
iconified  = new TvWidget_bool( N_("Start iconified"), "ICO", NULL, app_ref, false );
tv_widgets.push_back( iconified );
greyscale = new TvWidget_bool( N_("Display as greyscale"), "GREY", NULL, app_ref, false );
tv_widgets.push_back( greyscale );
owncmap  = new TvWidget_bool( N_("Use own color map"), "CMAP", NULL, app_ref, false );
tv_widgets.push_back( owncmap );

bounding_thres = new TvWidget_int( N_("Bounding box threshold"), "BBT", NULL, app_ref, 25 );
bounding_thres->set_range( 50, 1, 1 );
tv_widgets.push_back( bounding_thres );
light_buff = new TvWidget_bool( N_("Disable light buffer"), "LIBU", NULL, app_ref, false );
tv_widgets.push_back( light_buff );
vista  = new TvWidget_bool( N_("Disable vista buffer"), "VIBU", NULL, app_ref, false );
tv_widgets.push_back( vista );
draw_vista  = new TvWidget_bool( N_("Draw vista"), "ICO", NULL, app_ref, false );
tv_widgets.push_back( draw_vista );

adc_bailout = new TvWidget_float(  N_("Adc bailout"), "ADCBO", NULL, app_ref, 0.0039 );
adc_bailout->set_range( 1, 0, 0.1, 4 );
tv_widgets.push_back( adc_bailout );
ambient_light  = new TvWidget_color( N_("Ambient Light"), "ALIGHT", NULL, app_ref );
ambient_light->set( 1, 1, 1 );
tv_widgets.push_back( ambient_light );
hf_gray_16 = new TvWidget_bool( N_("heightfield  output"), "HF", NULL, app_ref, false );
tv_widgets.push_back( hf_gray_16 );
irid_wavelength = new TvWidget_color( N_("Irid wavelength"), "IVL", NULL, app_ref );
irid_wavelength->set( 0.25, 0.18, 0.14 );
tv_widgets.push_back( irid_wavelength );
max_trace_level = new TvWidget_int( N_("Max trace level"), "MTL", NULL, app_ref, 5 );
max_trace_level->set_range( 100, 1, 1 );
tv_widgets.push_back( max_trace_level );
max_intersections = new TvWidget_int( N_("Max intersections"), "MITS", NULL, app_ref, 64 );
max_intersections->set_range( 1024, 32, 16 );
tv_widgets.push_back( max_intersections );
number_of_waves = new TvWidget_int( N_("Number of waves"), "NOW", NULL, app_ref, 10 );
number_of_waves->set_range( 100, 0, 1 );
tv_widgets.push_back( number_of_waves );

brightness = new TvWidget_float( N_("Brightness"), "RBRI", NULL, app_ref, 3.3 );
brightness->set_range( 100, 0, 0.11, 3 );
tv_widgets.push_back( brightness );
count = new TvWidget_int( N_("Count"), "COUNT", NULL, app_ref, 100 );
count->set_range( 1000, 1, 10 );
tv_widgets.push_back( count );
distance_maximum = new TvWidget_float( N_("Distance maximum"), "DISTM", NULL, app_ref, 0 );
distance_maximum->set_range( 100, 0, 0.1, 3 );
tv_widgets.push_back( distance_maximum );
error_bound = new TvWidget_float( N_("Error bound"), "EBOUND", NULL, app_ref, 0.4 );
error_bound->set_range( 1, 0, 0.01, 4 );
tv_widgets.push_back( error_bound );
gray_threshold = new TvWidget_float( N_("Gray threshold"), "GRAYT", NULL, app_ref, 0.5 );
gray_threshold->set_range( 1, 0, 0.01, 4 );
tv_widgets.push_back( gray_threshold );
low_error_factor = new TvWidget_float( N_("Low error factor"), "LEF", NULL, app_ref, 0.8 );
low_error_factor->set_range( 1, 0, 0.01, 4 );
tv_widgets.push_back( low_error_factor );
minimum_reuse = new TvWidget_float( N_("Minimum reuse"), "MINIR", NULL, app_ref, 0.015 );
minimum_reuse->set_range( 1, 0, 0.1, 4 );
tv_widgets.push_back( minimum_reuse );
nearest_count = new TvWidget_int( N_("Nearest count"), "NCOUNT", NULL, app_ref, 6 );
nearest_count->set_range( 10, 0, 1 );
tv_widgets.push_back( nearest_count );
recursion_limit = new TvWidget_int( N_("Recursion limit"), "RECLIM", NULL, app_ref, 1 );
recursion_limit->set_range( 2, 1, 1 );
tv_widgets.push_back( recursion_limit );


// Predefined scenes
predef_scene = new TvWidget_option_combo( N_("Object"), "OBJ",NULL, app_ref );
predef_scene->set_list( predef_obj_list, predef_obj_num, pref->preview_object->value() );
tv_widgets.push_back(predef_scene  );
render_wall = new TvWidget_bool( N_("Render wall"), "WALL", NULL, app_ref, false );
tv_widgets.push_back( render_wall );
render_wall->set( pref->preview_wall->value() );
render_floor = new TvWidget_bool( N_("Render floor"), "FLOOR", NULL, app_ref, true );
tv_widgets.push_back( render_floor );
render_floor->set( pref->preview_floor->value() );
render_sky = new TvWidget_bool( N_("Render sky"), "SKY", NULL, app_ref, false );
tv_widgets.push_back( render_sky );
render_sky->set( pref->preview_sky->value() );

// Scene description
author = new TvWidget_entry( N_("Author"), "AUTH", NULL, app_ref );
author->set( pref->author->value() );
tv_widgets.push_back( author );
description = new TvWidget_text( N_("Description"), "DESC", NULL, app_ref );
tv_widgets.push_back( description );
}


PovrayFE::~PovrayFE()
{
if ( fifo_console != NULL ) { pclose( fifo_console ); fifo_console = NULL; }
if ( image != NULL ) delete image;
if ( predef_sizes != NULL ) delete predef_sizes;
for ( register unsigned int i = 0 ; i < tv_widgets.size() ; i++ )
	delete tv_widgets[i];
}


void PovrayFE::reset_defaults()
{
options_selected_page = 0;

filename->set( (char*)default_ofile );
fimage_type->set( 0 );

width->set( 640 );
height->set( 480 );

start_col->set( 1 );
start_row->set( 1 );
end_col->set( image_max_size );
end_row->set( image_max_size );

quality->set( 9 );
alpha->set( false );
bits_per_color->set( 8 );
radiosity->set( false );

antialias->set( true );
anti_met->set( 0 );
anti_depth->set( 2 );
threshold->set( 0.3 );
jitter->set( true );
jamount->set(0.5);

display->set( true );
mosaic->set( false );
iconified->set( false );
greyscale->set( false );
owncmap->set( false );

bounding_thres->set( 25 );
light_buff->set( false );
vista->set( false );
draw_vista->set( false );

adc_bailout->set( 0.0039 );
ambient_light->set( 1, 1, 1 );
hf_gray_16->set( false );
irid_wavelength->set( 0.25, 0.18, 0.14 );
max_trace_level->set( 5 );
max_intersections->set( 64 );
number_of_waves->set(10);

brightness->set( 3.3 );
count->set( 100 );
distance_maximum->set( 0 );
error_bound->set( 0.4 );
gray_threshold->set( 0.5 );
low_error_factor->set( 0.8 );
minimum_reuse->set( 0.015 );
nearest_count->set( 6 );
recursion_limit->set( 1 );

// Scene description
description->clear();
PREF_DEF
author->set( pref->author->value() );
}


void PovrayFE::flush()
{
if ( predef_sizes != NULL ) predef_sizes->flush();
for ( register unsigned int i = 0 ; i < tv_widgets.size() ; i++ )
	tv_widgets[i]->flush();
}


void PovrayFE::pref_changed()
{
if ( predef_sizes != NULL ) predef_sizes->pref_changed();
for ( register unsigned int i = 0 ; i < tv_widgets.size() ; i++ )
	tv_widgets[i]->pref_changed();
}


//**************************************************************
// Show image
//**************************************************************
void PovrayFE::show_image_as_widget( GtkWidget *box, bool tt )
{
widget = gtk_frame_new(NULL);
gtk_box_pack_start( GTK_BOX(box), widget, FALSE, FALSE, 5 );
preview_widget = gtk_image_new( );
//gtk_preview_size( GTK_PREVIEW(preview_widget), width->value(), height->value() );
gtk_container_add( GTK_CONTAINER(widget), preview_widget );
}


void PovrayFE::get_predef_scene_widget( GtkWidget *box, bool tt )
{
predef_scene->get_widget( box, tt );
render_wall->get_widget( box, tt );
render_floor->get_widget( box, tt );
render_sky->get_widget( box, tt );
}

//**************************************************************
// Predefined scene
//**************************************************************
char * PovrayFE::get_predef_scene_file( Material *mat )
{
char *tempfile = get_temp_filename();
ofstream file( tempfile, ios::out );
file.setf( ios::fixed, ios::floatfield );
if ( ! file ) return NULL;
//debug << "\nCreating temp scene file -> " << tempfile;
//debug.flush();
mat->output_to_povray( file );
output_to_povray( file );

// Camera - special one for sky_sphere preview
switch ( predef_scene->value() )
	{
	// sky sphere	
	case 4:
		file << "\n\ncamera { perspective location <0, .6, -0.5>  angle 90.0 up <0,1.0,0> right <1.0,0,0> sky <0,1.0,0> look_at <0,.8,0>}";
		break;
		
	case 5:
		file << "\n\ncamera { fisheye location <0, .6, 0>  angle 180.0 up <0,1.0,0> right <1.0,0,0> sky <0,1.0,0> look_at <0,1,0>}";
		break;
	
	// Normal
	default:
		file << "\n\ncamera { perspective location <0.47, .6, -0.47>  angle 40.0 up <0,1.0,0> right <1.0,0,0> sky <0,1.0,0> look_at <0,.2,0>}";
		break;		
	}

// Lights
file << "\nlight_source { <-0.4,1.6,-0.6> color rgb<1,1,1> }";
file << "\n\nlight_source { <1.29,.36,-0.6> color rgb<0.94, 0.94, 0.94> shadowless }";

// Floor and wall
if ( render_floor->value() )
	file << "\nplane { <0,1,0>, 0 pigment{ checker color rgb <0.6, 0.6, 0.6>, color rgb <0.7, 0.7, 0.7> scale 0.3 }  }";
if ( render_wall->value() && predef_scene->value() != 4 )
	file << "\nplane { <0,1,0>, 0 texture { pigment{ checker color rgb<.43,.58,.45>, color rgb<.29,.44,.33> }  scale 0.3  finish { ambient 0.65 } } rotate z*-90 rotate y*45  translate <-.5,0,.5>}";

// Decorative sky sphere ( not for texture preview ! )
if ( render_sky->value()   && predef_scene->value() != 4 )
	{
	file << "\nsky_sphere {";
	file << "\n\tpigment { gradient y";
	file << "\n\tpigment_map {  \
		[ 0.010000 rgbft<0.847000,0.749000,0.847000,0.000000,0.000000> ] \
		[ 0.250000 wrinkles \
			color_map { \
			[ 0.000000 rgbft<0.850000,0.850000,0.850000,0.000000,0.000000> ] \
			[ 0.100000 rgbft<0.750000,0.750000,0.750000,0.000000,0.000000> ] \
			[ 0.500000 rgbft<0.258000,0.258000,0.435000,0.000000,0.000000> ] \
			[ 1.000000 rgbft<0.258000,0.258000,0.435000,0.000000,0.000000> ] 	}";
	file << "\n\tturbulence <0.650000,0.650000,0.650000> \
					 omega 0.700000 translate <0.000000,0.000000,0.000000> scale <8.000000,1.250000,8.000000> rotate <0.000000,0.000000,5.000000> ]";
	file << "[ 1.000000 bozo \
			color_map { \
				[ 0.000000 rgbft<0.850000,0.850000,0.850000,0.000000,0.000000> ] \
				[ 0.100000 rgbft<0.550000,0.600000,0.650000,0.000000,0.000000> ]  \
				[ 0.500000 rgbft<0.184000,0.184000,0.309000,0.000000,0.000000> ] \
				[ 1.000000 rgbft<0.100000,0.100000,0.200000,0.000000,0.000000> ] \
			}";
			
	file << "\nturbulence <0.650000,0.650000,0.650000> \
		 omega 0.707000 translate <0.000000,0.000000,0.000000> scale <8.000000,4.500000,8.000000> rotate <0.000000,0.000000,10.000000> ] \
			} } }";
     }


// Preview objects
switch ( predef_scene->value() )
	{
	case 0:
		file << "\nsphere { <0,0,0>, 1\nscale <0.2,0.2,0.2>";
		file << "\n\tmaterial { ";
		mat->get_underscore_name(file);
		file << " } translate <0,.2,0> }";
		break;

	case 1:
		file << "\nbox { <-0.125,-0.125,0.125>, <0.125,0.125,-0.125>";
		file << "\n\tmaterial { ";
		mat->get_underscore_name(file);
		file << " } rotate z*-30 rotate y*10 translate <0, 0.2, -0.02> }";
		break;
	
	case 2:
		file << "\ncone { <0,-0.13,0>, 1, <0,0.17,0>, 0\n scale <0.21,0,0.21> ";
		file << "\n\tmaterial { ";
		mat->get_underscore_name(file);
		file << " } translate <-0.02,0.23,0.02> }";
		break;
		
	case 3:
		file << "\n\nplane {\n\t<0,1,0>,0.01";
		file << "\n\tmaterial { ";
		mat->get_underscore_name(file);
		file << " } }";
		break;
		
	case 4:
	case 5:
		file << "\n\nsky_sphere{\n\t";
		PovMaterial *tex = ((PovMaterial*)mat);
		if ( tex->has_pigment() ) tex->output_to_povray_pigment( file );
		else
			{
			file << "pigment { color 0.3 }";
			app_warning( _("Sky sphere requires a pigment in texture.") );
			}
		file << "\n}";
		break;
	}

file.close();
return tempfile;
}



//**************************************************************
// Povray command
//**************************************************************
char shell[] = "/bin/sh";
char *shell_cmd[4] = { shell, "-bc", NULL, NULL };

bool PovrayFE::launch_povray_for_preview( char * source_file )
{
// Taille prédéfinies
if ( predef_sizes != NULL )
	{
	int nsize = 80;
	predef_sizes->flush();
	switch( predef_sizes->value() )
		{
		case 0: nsize = 80; break;
		case 1: nsize = 140; break;
		case 2: nsize = 200; break;
		case 3: nsize = 300; break;		
		case 4: nsize = 400; break;
		}
	if ( width->value() != nsize )
		{
		set_size( nsize, nsize );
		if ( image != NULL ) delete image;
		image_size = width->value() * height->value() * 3;
		image = new char[image_size];
		}
	}

// Création des pipes
int res;
pipe_console_name = get_temp_filename();
res = mkfifo( pipe_console_name, S_IRUSR | S_IWUSR | O_NDELAY );
pipe_output_name = get_temp_filename();
res += mkfifo( pipe_output_name, S_IRUSR | S_IWUSR | O_NDELAY );
if ( res != 0 ) 	return false;
//debug << "\ncreated console pipe -> " << pipe_console_name;
//debug.flush();
//debug << "\ncreated output pipe -> " << pipe_output_name;
//debug.flush();

// Commande
PREF_DEF
char *povray_cmd = new char [500];
char cmdb[500];
strcpy( povray_cmd, pref->povcmd->value() );
sprintf( cmdb, " -P -V -C -UD +GA%s +O%s +I%s +H%u +W%u +FP +Q%u", pipe_console_name, pipe_output_name, source_file, height->value(), width->value(), quality->value() );
strcat( povray_cmd, cmdb );
if ( antialias->value() ) strcat( povray_cmd, " +A" );

sprintf( cmdb, " -D ; echo Done > %s", pipe_console_name );
strcat( povray_cmd, cmdb );
//strcat( povray_cmd, " ; echo Done" );
shell_cmd[2] = povray_cmd;


// Launch
povray_pid = fork();
if ( povray_pid == -1 ) return false;
if ( povray_pid == 0 )
	{
	if ( execve( shell, shell_cmd, app_ref->envp ) == -1 )
		{
		exit(-1);
		unlink( pipe_console_name );
		unlink( pipe_output_name );
		}
	exit(-1);
	}

//debug << "\nOpening pipes...";
//debug.flush();
pipe_console_id = open( pipe_console_name, O_RDONLY | O_NONBLOCK );
if ( pipe_console_id == -1 ) return false;
pipe_output_id = open( pipe_output_name, O_RDONLY  | O_NONBLOCK );
//debug << "\nPipes opened...";
//debug.flush();
delete povray_cmd;
return true;
}



void PovrayFE::delete_povray_cmd()
{
kill( povray_pid, 3 );
close( pipe_output_id );
close( pipe_console_id );
unlink( pipe_output_name );
unlink( pipe_console_name );
waitpid( povray_pid, NULL, 0 );
unlink( pipe_output_name );
unlink( pipe_console_name );
delete pipe_output_name;
delete pipe_console_name; pipe_console_name = NULL;
}


bool PovrayFE::get_pipe_line( int file, char *buffer, int size )
{
//debug << "\nStart reading from pipe !"; debug.flush();
char ch;
int i = 0;
while ( i < size )
	{
	int res = read( file, &ch, 1 );
	if ( res == 0 ) continue; //return false;
	if ( res == -1 && errno == EAGAIN ) continue;
	if ( res == -1 ) return false;
	if ( ch == '\n' || ch == '\r' ) break;
	buffer[i++] = ch;
	}
buffer[i] = '\0';
//debug << "\nRead one line from pipe !"; debug.flush();
return true;
}


//**************************************************************
// Start rendering
//**************************************************************
bool PovrayFE::start_rendering_predef( Material *mat, bool save, bool ismodal )
{
if ( mat  == NULL ) return false;
if ( mat->get_type() != TV_MAT_POV ) return false;
modal = ismodal;
flush();
scene_file = get_predef_scene_file( mat );
if ( scene_file == NULL )
		{
		app_warning( _("Cannot create temporary scene file ") );
		return false;
		}
render_status = false;
//debug << "\nSetting size !";
//debug.flush();	

// Tampon image
if ( image != NULL ) delete image;
image = NULL;
//if ( save )
	{
	image_size = width->value() * height->value() * 3;
	image = new char[image_size];
	}

//debug << "\nLaunching preview !";
//debug.flush();	
// Execution de povray
if ( launch_povray_for_preview( scene_file ) == false )
		{ 
		app_warning( _("Cannot execute povray !") ); 
		delete_povray_cmd();
		unlink( scene_file );
		delete scene_file;
		return false; 
		}

//debug << "\nCreating threads !";
//debug.flush();
// Thread de lecture de l'image
pthread_attr_t thread_attr;
pthread_attr_init( &thread_attr );
pthread_attr_setdetachstate( &thread_attr,  PTHREAD_CREATE_DETACHED );
if( pthread_create( &output_thread, &thread_attr, reading_image_thread, this ) )
	{
	app_warning( _("Cannot create image reading thread !") ); 
	delete_povray_cmd();
	unlink( scene_file ); 
	delete scene_file;
	return false; 	
	}

// Thread de lecture de la console
pthread_attr_t thread_attr2;
pthread_attr_init( &thread_attr2 );
pthread_attr_setdetachstate( &thread_attr2, modal ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED  );
if( pthread_create( &console_thread, &thread_attr2, reading_console_thread, this ) )
	{
	app_warning( _("Cannot create status reading thread !") ); 
	delete_povray_cmd();
	unlink( scene_file );
	delete scene_file;
	return false; 	
	}

pthread_join( console_thread, NULL );
return render_status;
}


//*******************************************************
// Thread de lecture de la console
//*******************************************************
void PovrayFE::read_console()
{
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL );
pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL );

//debug << "\nStarted console reading thread !!";
//debug.flush();
char console_line[255] = "\0";
bool finished = false;
if ( get_pipe_line( pipe_console_id, console_line, 254 ) == false || strcmp( console_line, "Parsing Options" ) )
	{
	//debug << "\nError : read_console init planting !";
	//debug << "\nline ->" << console_line << "<-";
	//debug.flush();
	close_render( false );
	if ( ! modal ) gdk_threads_enter();
	app_warning( _("Error while executing povray ! ( init error )") );
	if ( !modal ) gdk_threads_leave();
	pthread_exit(NULL);
	}

// lecture de la console
int console_line_offset = 0;
int res;
char ch;
while ( finished == false )
	{
	pthread_testcancel();
	console_line[console_line_offset] = '\0';
	res = read( pipe_console_id, &ch, 1 );
	if ( res == -1 || res == 0 ) continue;
	console_line[console_line_offset++] = ch;
	pthread_testcancel();
	if ( console_line_offset == 254 || ch == '\n' )
		{
		// Analyse et stockage ( gestion des erreurs )
		if ( strstr( console_line, "Done Tracing" ) != NULL ) finished = true;

		bool error = false;
		if ( strstr( console_line, "Error" ) != NULL ) error = true;
		if ( strstr( console_line, "error" ) != NULL ) error = true;
		if ( error )
			{
			close_render( false );
			if ( !modal ) gdk_threads_enter();
			app_warning( _("Error while executing povray ! ( source error )") );
			if ( !modal ) gdk_threads_leave();
			pthread_exit(NULL);
			}
		console_line_offset = 0;
		}
	}
render_status = true;
while (  render_finished == false ) sleep(1);
close_render( false );
//debug << "\nLeaving console thread !! ";
//debug.flush();
pthread_exit(NULL);
}


void PovrayFE::close_render( bool close_console )
{
delete_povray_cmd();
unlink( scene_file );
delete scene_file;
/*if ( close_console ) */ pthread_cancel( output_thread );
if ( close_console ) pthread_cancel( console_thread );

//debug << "\n===== Closing render ======";
//debug.flush();
if ( !modal ) gdk_threads_enter();
//debug << "\nEntered gdk threads !";
//debug.flush();
if ( start_button != NULL ) gtk_label_set_text( GTK_LABEL(start_label), _("Preview") );
if ( progress_bar != NULL ) gtk_progress_bar_update( GTK_PROGRESS_BAR(progress_bar), 0 );
if ( !modal ) gdk_threads_leave();
//debug << "\nLabel set !";
//debug.flush();
}

void PovrayFE::cancel_preview()
{
if ( pipe_console_name == NULL ) return;
delete_povray_cmd();
pthread_cancel( output_thread );
pthread_cancel( console_thread );
unlink( scene_file );
delete scene_file;
}

//*******************************************************
// Thread de lecture de l'image
//*******************************************************
void PovrayFE::read_output()
{
//debug << "\nStarted output reading thread !!";
//debug.flush();

pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL );
pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL );
render_finished = false;

// Flow control
int largeur = width->value();
int hauteur = height->value();
guchar output_line[largeur*3];
int output_line_num = 0;
int output_line_offset = 0;
int header = 0;
char ch;
int res;
bool finished = false;
GdkRectangle rect;
rect.width = largeur;
rect.height = 1;
rect.x= 0;
while ( finished == false )
	{
	pthread_testcancel();
	res = read( pipe_output_id, &ch, 1 );
	if ( res == -1 || res == 0 ) continue;
	if ( header < 3 ) 
		{
		if ( ch == '\n' ) header++;
		continue; 
		}
	output_line[output_line_offset++] = ch;
	if ( output_line_offset == largeur*3 )
		{
		if ( !modal ) gdk_threads_enter();
		
		//gtk_preview_draw_row( GTK_PREVIEW(preview_widget), output_line, 0, output_line_num, largeur-1 );
		if ( image != NULL ) memcpy( image + (largeur-1) * output_line_num * 3, output_line, (largeur-1)*3 );
		GdkPixbuf *buffer = gdk_pixbuf_new_from_data( (const guchar*)image, GDK_COLORSPACE_RGB, FALSE, 8, largeur, hauteur, (largeur-1)*3, NULL, NULL );
		gtk_image_set_from_pixbuf( GTK_IMAGE(preview_widget), buffer );
		g_object_unref( buffer );
		
		if ( progress_bar != NULL ) gtk_progress_bar_update( GTK_PROGRESS_BAR(progress_bar), (float)output_line_num / (float)hauteur );
		//debug << "\nProgress -> " << (float)output_line_num / (float)height->value();
		//debug.flush();
		rect.y = output_line_num++;
		//gtk_widget_draw( preview_widget, &rect );
		if ( !modal ) gdk_threads_leave();
		output_line_offset = 0;
		if ( output_line_num == hauteur -1 ) finished = true;
		}
	}
//debug << "\nLeaving output thread !!";
//debug.flush();
render_finished = true;
pthread_exit(NULL);
}


//********************************************************
// Save as preview
//********************************************************
void PovrayFE::save_as_preview( ofstream & file )
{
if ( image == NULL ) return;

// compression
char *zimage = new char[image_size+30];
long int zimage_size = image_size+30;
compress2( (Bytef*)zimage, (uLongf*)&zimage_size, (Bytef*)image, (uLong)image_size, 9 ); 
delete image;
image = NULL;

// sauvegarde
file << "\nPREVIEW{ SIZE=" << zimage_size << " DATA=";
for( int i = 0 ; i < zimage_size ; i++ )
	file << zimage[i];
file << " }\n";

delete zimage;
}



//********************************************************
// Start button
//********************************************************
void PovrayFE::get_start_button( GtkWidget *box, bool tt )
{
start_label = gtk_label_new( _("Preview") );
start_button = gtk_button_new();
gtk_container_add( GTK_CONTAINER(start_button), start_label );
gtk_box_pack_start( GTK_BOX(box), start_button, FALSE, FALSE, 5 );
gtk_signal_connect( GTK_OBJECT(start_button), "clicked", GTK_SIGNAL_FUNC(sign_start_clicked), this );
}


void PovrayFE::start_clicked()
{
if ( pipe_console_name == NULL )
	{
	// Start
	current_material->flush_material();
	flush();
	gtk_label_set_text( GTK_LABEL(start_label), _("Cancel") );
	start_rendering_predef( current_material, false, false );
	}
else
	{
	// Stop
	modal = true;
	close_render( true );
	modal = false;
	}
}



//********************************************************
// Progress bar 
//********************************************************
void PovrayFE::get_progress_bar( GtkWidget *box, bool tt )
{
progress_bar = gtk_progress_bar_new();
gtk_box_pack_start( GTK_BOX(box), progress_bar, FALSE, FALSE, 5 );
gtk_progress_bar_update( GTK_PROGRESS_BAR(progress_bar), 0 );
gtk_progress_set_show_text( GTK_PROGRESS(progress_bar), TRUE );
gtk_progress_set_text_alignment( GTK_PROGRESS(progress_bar), 0.5, 0.3 );
}


//********************************************************
// Predefined image sizes
//********************************************************
const int predef_sizes_num = 5;
const char *predef_sizes_lab[predef_sizes_num] = { "80x80", "140x140", "200x200", "300x300", "400x400" };

void PovrayFE::get_predef_sizes( GtkWidget *box, bool tt )
{
PREF_DEF
predef_sizes = new TvWidget_option_combo( _("Size"), NULL, NULL, app_ref );
predef_sizes->set_list( predef_sizes_lab, predef_sizes_num, pref->preview_size->value() );
predef_sizes->get_widget( box, tt );
antialias->set( pref->preview_antialias->value() );
}


void PovrayFE::save_preview_settings()
{
PREF_DEF
if ( ! pref->save_preview_settings->value() ) return;
predef_sizes->flush();
predef_scene->flush();
render_wall->flush();
render_floor->flush();
render_sky->flush();
antialias->flush();
pref->preview_size->set( predef_sizes->value() );
pref->preview_object->set( predef_scene->value() );
pref->preview_wall->set( render_wall->value() );
pref->preview_floor->set( render_floor->value() );
pref->preview_sky->set( render_sky->value() );
pref->preview_antialias->set( antialias->value() );
}



//********************************************************
// Options box
//********************************************************
void PovrayFE::raise_options_box()
{
if ( GTK_IS_DIALOG(options_box) )
	{
	gdk_window_raise( options_box->window );
	return;
	}
opt_applied = false;

PREF_DEF
bool tt = pref->tooltips->value();

for ( register unsigned int i = 0 ; i < tv_widgets.size() ; i++ )
	tv_widgets[i]->store();

//options_box = gnome_dialog_new( _("Povray options"), GNOME_STOCK_BUTTON_OK, GNOME_STOCK_BUTTON_APPLY, GNOME_STOCK_BUTTON_CANCEL,NULL );
options_box = gtk_dialog_new_with_buttons( _("Povray options"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL );
//gtk_window_set_wmclass( GTK_WINDOW(options_box), PACKAGE, "Povray options" );
//gtk_window_set_policy( GTK_WINDOW(options_box), TRUE, TRUE, FALSE );
//gtk_window_set_position( GTK_WINDOW(options_box),  GTK_WIN_POS_NONE );
g_signal_connect( G_OBJECT(options_box), "response", G_CALLBACK(sign_povfe_opt_click), this );
g_signal_connect( G_OBJECT(options_box), "close", G_CALLBACK(sign_povfe_opt_destroy), this );
if ( pref->save_dlg_geo->value() )
	{
	if ( pref->ropt_xpos->value() < 0 ) pref->ropt_xpos->set(0);
	if ( pref->ropt_ypos->value() < 0 ) pref->ropt_ypos->set(0);	
	gtk_widget_set_uposition( options_box, pref->ropt_xpos->value(), pref->ropt_ypos->value() );
	gtk_widget_set_usize( options_box, pref->ropt_xsize->value(), pref->ropt_ysize->value() );
	}

// Conteneur
options_notebk = gtk_notebook_new();
gtk_notebook_set_scrollable( GTK_NOTEBOOK(options_notebk), TRUE );
gtk_box_pack_start( GTK_BOX(GTK_DIALOG(options_box)->vbox), options_notebk, FALSE, TRUE, 5 );
GtkWidget *box1, *box2, *vbox, *hbox;

// PAGE Quality
vbox = dlg_new_page( options_notebk, N_("Quality") );
	hbox = dlg_simple_box_frame( _("General"), vbox );
		dlg_double_box( hbox, box1, box2 );		
		quality->get_widget( box1, tt );
		bits_per_color->get_widget( box1, tt );
		alpha->get_widget( box2, 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 );


// PAGE Global settings
vbox = dlg_new_page( options_notebk, _("Scene settings") );
	hbox = dlg_simple_box_frame( _("General"), vbox );
		ambient_light->get_widget( hbox, tt );
		number_of_waves->get_widget( hbox, tt );
		irid_wavelength->get_widget( hbox, tt );


// PAGE Radiosity
vbox = dlg_new_page( options_notebk, _("Radiosity") );		
	hbox = dlg_simple_box_frame( _("General"), vbox );
		dlg_double_box( hbox, box1, box2 );		
		radiosity->get_widget( box1, tt );
		brightness->get_widget( box1, tt );	
		gray_threshold->get_widget( box1, tt );
		count->get_widget( box1, tt );
		nearest_count->get_widget( box1, tt );
		minimum_reuse->get_widget( box2, tt );
		distance_maximum->get_widget( box2, tt );
		error_bound->get_widget( box2, tt );
		low_error_factor->get_widget( box2, tt );

// PAGE Performances
vbox = dlg_new_page( options_notebk, _("Performances") );
	hbox = dlg_simple_box_frame( _("General"), vbox );
	dlg_double_box( hbox, box1, box2 );		
	bounding_thres->get_widget( box1, tt );
	light_buff->get_widget( box1, tt );
	vista->get_widget( box2, tt );
	draw_vista->get_widget( box2, tt );
	
	// Frame Limits
	hbox = dlg_simple_box_frame( _("Limits"), vbox );
		dlg_double_box( hbox, box1, box2 );
		adc_bailout->get_widget( box1, tt );
		max_trace_level->get_widget( box1, tt );
		max_intersections->get_widget( box2, tt );

// PAGE Display
vbox = dlg_new_page( options_notebk, _("Display") );
	hbox = dlg_simple_box_frame( _("General"), vbox );
		display->get_widget( hbox, tt );
		mosaic->get_widget( hbox, tt );
		iconified->get_widget( hbox, tt );
		owncmap->get_widget( hbox, tt );
		greyscale->get_widget( hbox, tt );

// PAGE OUTPUT
vbox = dlg_new_page( options_notebk, _("Output") );
	// Frame File
	hbox = dlg_simple_box_frame( _("File options"), vbox );
		filename->get_widget( hbox, tt );	
		fimage_type->get_widget( hbox, tt );
		hf_gray_16->get_widget( hbox, tt );

	// Frame Size
	hbox = dlg_simple_box_frame( _("Size"), vbox );
		dlg_double_box( hbox, box1, box2 );
		width->get_widget( box1, tt );
		height->get_widget( box2, tt );
       presets = new TvWidget_option_combo( N_("Size presets"), NULL, NULL, app_ref );
		presets->set_list( size_presets_names, size_presets_num, 0 );
		for ( int i = 0 ; i < size_presets_num ; i++ )
			if ( size_presets_width[i] == width->value() && size_presets_heigth[i] == height->value() )
				{
				presets->set(i);
				break;
				}
		presets->get_widget( box1, tt );
		presets->connect_signal( GTK_SIGNAL_FUNC(sign_povfe_presets), this );

		
	// Frame partial
	hbox = dlg_simple_box_frame( _("Partial render"), vbox );
		dlg_double_box( hbox, box1, box2 );
		start_col->get_widget( box1, tt );
		start_row->get_widget( box2, tt );
		end_col->get_widget( box1, tt );
		end_row->get_widget( box2, tt );


// PAGE OUTPUT
vbox = dlg_new_page( options_notebk, _("About scene") );
	// Frame partial
	hbox = dlg_simple_box_frame( _("Description"), vbox );
	author->get_widget( hbox, tt );
	description->get_widget( hbox, tt );

gtk_widget_show_all(options_box);
gtk_notebook_set_page( GTK_NOTEBOOK(options_notebk), options_selected_page );
}


void PovrayFE::opt_box_clicked( int button )
{
switch( button )
	{
	case GTK_RESPONSE_OK:
		flush();
		close_opt_box();
		break;

	case GTK_RESPONSE_APPLY:
		flush();
		opt_applied = true;
		break;

	default:
	case GTK_RESPONSE_CANCEL:
		if ( opt_applied ) unapply_opt_box();
		close_opt_box();
		break;
	}
}

void PovrayFE::close_opt_box()
{
options_selected_page = gtk_notebook_get_current_page( GTK_NOTEBOOK(options_notebk) );
PREF_DEF
if ( pref->save_dlg_geo->value() )
	{
	gint x,y;
	gdk_window_get_root_origin( options_box->window, &x, &y );
	pref->ropt_xpos->set( x );
	pref->ropt_ypos->set( y );
	pref->ropt_xsize->set( options_box->allocation.width );
	pref->ropt_ysize->set( options_box->allocation.height );
	}
if ( options_box != NULL ) gtk_widget_destroy( options_box );
options_box = NULL;
for ( register unsigned int i = 0 ; i < tv_widgets.size() ; i++ )
	tv_widgets[i]->clear_widget();
delete presets;
}

void PovrayFE::unapply_opt_box()
{
for ( register unsigned int i = 0 ; i < tv_widgets.size() ; i++ )
	tv_widgets[i]->restore();
}


void PovrayFE::size_preset_choice()
{
if ( presets == NULL ) return;
presets->flush();
int val = presets->value();
if ( val < 1 ) return;
width->set( size_presets_width[ val ] );
width->update_widget();
height->set( size_presets_heigth[ val ] );
height->update_widget();
}



//****************************************************************************
// Render box
//****************************************************************************
void PovrayFE::raise_render_box()
{
if ( GTK_IS_WIDGET(render_box) )
	{
	gdk_window_raise( render_box->window );
	return;
	}
PREF_DEF
bool tt = pref->tooltips->value();

render_box = gtk_window_new( GTK_WINDOW_TOPLEVEL );
gtk_window_set_decorated( GTK_WINDOW(render_box), TRUE );
gtk_window_set_title( GTK_WINDOW(render_box), _("Render") );
gtk_window_set_policy( GTK_WINDOW(render_box), FALSE, FALSE, TRUE );
gtk_signal_connect( GTK_OBJECT(render_box), "delete_event", GTK_SIGNAL_FUNC(sign_povfe_rend_destroy), this );
if ( pref->save_dlg_geo->value() )
	{
	if ( pref->rdlg_xpos->value() < 0 ) pref->rdlg_xpos->set(0);
	if ( pref->rdlg_ypos->value() < 0 ) pref->rdlg_ypos->set(0);	
	gtk_widget_set_uposition( render_box, pref->rdlg_xpos->value(), pref->rdlg_ypos->value() );
	}

GtkWidget *vbox = gtk_vbox_new( FALSE, 0 );
gtk_container_set_border_width( GTK_CONTAINER(vbox), 7 );
gtk_container_add( GTK_CONTAINER(render_box), vbox );

	
GtkWidget *lab = gtk_label_new( _("Persistence of Vision Front-end") );
gtk_box_pack_start( GTK_BOX(vbox), lab, TRUE, TRUE, 0 );

get_progress_bar( vbox, tt );

status = gtk_label_new( _("Click 'start' to render the scene") );
gtk_box_pack_start( GTK_BOX(vbox), status, TRUE, TRUE, 0 );

GtkWidget *hbox = gtk_hbox_new( FALSE, 5 );
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 4 );
time_lab = gtk_label_new( _("Elapsed time : ") );
gtk_box_pack_start( GTK_BOX(hbox), time_lab, FALSE, TRUE, 20 );
time_lab = gtk_label_new( "--:--:--" );
gtk_box_pack_start( GTK_BOX(hbox), time_lab, FALSE, TRUE, 0 );

GtkWidget *sep = gtk_hseparator_new();
gtk_box_pack_start( GTK_BOX(vbox), sep, TRUE, TRUE, 8 );

GtkWidget *button_box = gtk_hbutton_box_new();
gtk_button_box_set_layout( GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_SPREAD );
gtk_button_box_set_spacing( GTK_BUTTON_BOX(button_box), 10 );
gtk_box_pack_start( GTK_BOX(vbox), button_box, FALSE, FALSE, 0 );

	start_label = gtk_label_new( _("Start") );
	start_button = gtk_button_new();
	gtk_signal_connect( GTK_OBJECT(start_button), "clicked", GTK_SIGNAL_FUNC(sign_povfe_start_stop_clicked), this );
	gtk_container_add( GTK_CONTAINER(start_button), start_label );
	gtk_container_add( GTK_CONTAINER(button_box), start_button );

	GtkWidget *button = gtk_button_new_with_label( _("Console") );
	gtk_container_add( GTK_CONTAINER(button_box), button );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_povfe_console), this );

	button = gtk_button_new_with_label( _("Options") );
	gtk_container_add( GTK_CONTAINER(button_box), button );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_options_box), this );

	button = gtk_button_new_with_label( _("Close") );
	gtk_container_add( GTK_CONTAINER(button_box), button );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_povfe_rend_close), this );


// Console text
cons_box = gtk_hbox_new( TRUE, 5 );
cons_text = gtk_text_view_new( );
gtk_container_add( GTK_CONTAINER(cons_box), cons_text );
	
gtk_widget_show_all( render_box );
}

void PovrayFE::close_render_box()
{ 
PREF_DEF
if ( pref->save_dlg_geo->value() )
	{
	gint x,y;
	gdk_window_get_root_origin( render_box->window, &x, &y );
	pref->rdlg_xpos->set( x );
	pref->rdlg_ypos->set( y );
	}
close_render2( false );
if ( render_box != NULL ) gtk_widget_destroy( render_box ); 
render_box = NULL; 
close_console_dlg(); 
gtk_widget_destroy(cons_box); 
}

//*********************************************************
// Start stop
//*********************************************************
void PovrayFE::start_stop_clicked()
{
if ( render_status == false )
	{
	// Start
	gtk_label_set_text( GTK_LABEL(start_label), _("Stop") );
	start_rendering_scene();
	}
else
	{
	// Stop
	close_render2( false );
	//pclose( fifo_console );
	}
}


//*********************************************************
// Start rendering
//*********************************************************
bool PovrayFE::start_rendering_scene()
{
// Scene
SCENE_DEF
scene_file = scene->get_temp_povray_file();
render_status = true;

// Effacement de la console
//gtk_text_backward_delete( GTK_TEXT(cons_text), gtk_text_get_length(GTK_TEXT(cons_text)) );
GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(cons_text) );
GtkTextIter start, end;
gtk_text_buffer_get_bounds( buffer, &start, &end );
gtk_text_buffer_delete( buffer, &start, &end );


// Check if destination directory exists && output file != NULL
bool nodir = false;
if ( filename->value() == NULL ) filename->set( (char*)default_ofile );
int len = strlen( filename->value() );
char *path = new char[ len + 1 ];
strcpy( path,  filename->value() );
for (  ; path[len] != '/' && len != 0 ; len-- )  {}
path[len] = '\0';
//cout << "\npath = " << path; cout.flush();
if ( access( path, F_OK ) == -1 && len != 0 )
	{
	app_warning( _("Can't write output file to directory"), path );
	nodir = true;
	}


// Commande
PREF_DEF
char povcmd[700];
char cmdb[500];
strcpy( povcmd, pref->povcmd->value() );

	// Base & file
	sprintf( cmdb, " +P +V +I%s +H%u +W%u +Q%u +O%s -title \"Persistence of Vision: %s\" +F", scene_file, height->value(), width->value(), quality->value(), nodir ? default_ofile : filename->value(), nodir ? default_ofile : filename->value() );
	strcat( povcmd, cmdb );
	strcat( povcmd, file_type_output[fimage_type->value()] );
	if ( fimage_type->value() == 0 ) 
		{
		sprintf( cmdb, "%u", bits_per_color->value() );
		strcat( povcmd, cmdb );
		}		
	sprintf( cmdb, " +SC%u +SR%u +EC%u +ER%u", start_col->value(), start_col->value(), end_col->value(), end_row->value() );
	strcat( povcmd, cmdb );
	
	// Display
	if ( display->value() ) strcat( povcmd, " +D" );
	if ( greyscale->value() ) strcat( povcmd, "0G" );
	if ( iconified->value() ) strcat( povcmd, " -iconic" );
	if ( owncmap->value() ) strcat( povcmd, " -owncmap" );
	if ( mosaic->value() ) strcat( povcmd, " +SP2" );
	
	// Quality
	if ( radiosity->value() ) strcat( povcmd, " +QR" );
	if ( alpha->value() ) strcat( povcmd, " +UA" );
	

	// Antialiasing			
	if ( antialias->value() ) 
		{
		strcat( povcmd, " +A" );
		sprintf( cmdb, "%1.1f +AM%u +R%u", threshold->value(), anti_met->value()+1, anti_depth->value() );
		strcat( povcmd, cmdb );	
		if ( jitter->value() ) { sprintf( cmdb, " +J%1.1f ", jamount->value() ); strcat( povcmd, cmdb ); }
		}

	// Performances
	if ( bounding_thres->value() != 25 ) 
		{
		sprintf( cmdb, " +MB%u", bounding_thres->value() );
		strcat( povcmd, cmdb );			
		}
	if ( light_buff->value() )  strcat( povcmd, " -UL" );
	if ( vista->value() )  strcat( povcmd, " -UV" );
	if ( draw_vista->value() )  strcat( povcmd, " +UD" );
	

	strcat( povcmd, " &> /dev/stdout &" );

//cout << "\nPovray cmd -> " << povcmd;
//cout.flush();


fifo_console = popen( povcmd, "r" );
if ( fifo_console == NULL )
	{
	app_warning( _("Cannot read povray output !") );
	close_render2( false );	 	
	return false;
	}

// Thread de lecture de la console
pthread_attr_t thread_attr2;
pthread_attr_init( &thread_attr2 );
pthread_attr_setdetachstate( &thread_attr2, PTHREAD_CREATE_DETACHED  );
if( pthread_create( &console_thread, &thread_attr2, reading_console_thread2, this ) )
	{
	app_warning( _("Cannot create status reading thread !") ); 
	close_render2( false );
	return false; 	
	}

return true;
}


//******************************************************************
// Closing render 2
//******************************************************************
void PovrayFE::close_render2( bool from_thread )
{
if ( render_status == false ) return;
//kill( povray_pid, 9 );
unlink( scene_file );
delete scene_file;
if ( ! from_thread ) pthread_cancel( console_thread );

//cout << "\n===== Closing render ======";
//cout.flush();
if ( from_thread ) gdk_threads_enter();
//cout << "\nEntered gdk threads !";
//cout.flush();
gtk_label_set_text( GTK_LABEL(start_label), _("Start") );
gtk_progress_bar_update( GTK_PROGRESS_BAR(progress_bar), 0 );
gtk_label_set_text( GTK_LABEL(status), _("Done.") );
gtk_label_set_text( GTK_LABEL(time_lab), "--:--:--" );

if ( from_thread ) gdk_threads_leave();
//cout << "\nLabel set !";
//cout.flush();
pclose( fifo_console );
render_status = false;
}


//******************************************************************
// Read povray output 2
//******************************************************************
void PovrayFE::read_console2()
{
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL );
pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL );


//cout << "\nStarted console reading thread !!";
//cout.flush();
char console_line[255];
bool finished = false;
bool print;
int image_line = 0;
char time[20];
int passes = 0;
bool use_radiosity = radiosity->value();
bool use_mosaic = mosaic->value();
int hauteur = height->value();
int line_num = 0;

// lecture de la console
int console_line_offset = 0;
char ch;
while ( finished == false )
	{
	pthread_testcancel();
	ch = getc( fifo_console );
	if ( feof(fifo_console) ) finished = true;
	console_line[console_line_offset++] = ch;
	
	if ( console_line_offset == 254 || ch == '\n' || ch == '\r' )
		{
		console_line[console_line_offset] = '\0';
		print = true;
		line_num++;

		// Analyse et stockage ( gestion des erreurs )
		if ( !strncmp( console_line, "Total Time", 10 ) ) finished = true;
		if ( !strncmp( console_line, "Parsing...", 10 ) )
			{
			gdk_threads_enter();
			gtk_label_set_text( GTK_LABEL(status), _("Parsing...") );
			gdk_threads_leave();
			}
		else
			{			
			bool rendering = strstr( console_line, "Rendering" ) == NULL ?  false : true;
			gdk_threads_enter();
			if ( rendering ) gtk_label_set_text( GTK_LABEL(status), _("Rendering") );
			gdk_threads_leave();
			}
			
		char *substr = strstr( console_line, "Rendering line " );
		if ( substr != NULL )
			{
			print = false;
			sscanf( substr + 15, "%u of", &image_line );
			if ( image_line == 1 ) 
				{
				passes++; 
				if ( passes == 2 && use_radiosity ) 
					{
					gdk_threads_enter();
					gtk_label_set_text( GTK_LABEL(status), _("Rendering...") );
					gdk_threads_leave();
					}
				}
			
			float prog = (float)(image_line) / (float)hauteur;
			if ( use_mosaic && !use_radiosity )
				{
				if ( passes == 1 ) prog /= 4;
				else prog = prog / 4 * 3 + 0.25;
				}
			
			strncpy( time, console_line, 9 );
			
			gdk_threads_enter();			
			gtk_progress_bar_update( GTK_PROGRESS_BAR(progress_bar), prog );
			gtk_label_set_text( GTK_LABEL(time_lab), time );			
			if ( image_line == hauteur ) gtk_label_set_text( GTK_LABEL(status), _("Click on image to close it...") );
			gdk_threads_leave();			
			}

		bool error = false;
		//if ( strstr( console_line, "Error" ) != NULL ) error = true;
		//if ( strstr( console_line, "error" ) != NULL ) error = true;
		if ( error )
			{
			close_render2( true );
			gdk_threads_enter();
			app_warning( _("Error while executing povray ! See console for details") );
			gdk_threads_leave();
			pthread_exit(NULL); 
			}

		// Sortie console
		if ( print )
			{
			gdk_threads_enter();	
			GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(cons_text) );
			GtkTextIter end;
			gtk_text_buffer_get_end_iter( buffer, &end );
			gtk_text_buffer_insert( buffer, &end, console_line, -1 );
			//gtk_text_insert( GTK_TEXT(cons_text), NULL, NULL, NULL, console_line, -1 );
			gdk_threads_leave();
			}

		console_line_offset = 0;
		}
	}
close_render2( true );
//cout << "\nLeaving console thread !! ";
//cout.flush();
pthread_exit(NULL);
}


//***********************************************
// Console dialog
//***********************************************
void PovrayFE::raise_console_dlg()
{
if ( cons_dlg != NULL ) { return; }
PREF_DEF

//cons_dlg = gnome_dialog_new( _("Rendering console"), GNOME_STOCK_BUTTON_CLOSE, NULL );
cons_dlg = gtk_dialog_new_with_buttons( _("Rendering console"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL );
gtk_window_set_policy( GTK_WINDOW(cons_dlg), TRUE, TRUE, FALSE );
//gtk_window_set_wmclass( GTK_WINDOW(cons_dlg), PACKAGE, "Rendering console" );
//gtk_window_set_position( GTK_WINDOW(cons_dlg),  GTK_WIN_POS_NONE );
g_signal_connect( G_OBJECT(cons_dlg), "close", G_CALLBACK(sign_povfe_cons_click), this );
g_signal_connect( G_OBJECT(cons_dlg), "response", G_CALLBACK(sign_povfe_cons_destroy), this );
if ( pref->save_dlg_geo->value() )
	{
	if ( pref->con_xpos->value() < 0 ) pref->con_xpos->set(0);
	if ( pref->con_ypos->value() < 0 ) pref->con_ypos->set(0);	
	gtk_widget_set_uposition( cons_dlg, pref->con_xpos->value(), pref->con_ypos->value() );
	gtk_window_resize( GTK_WINDOW(cons_dlg), pref->con_xsize->value(), pref->con_ysize->value() );
	}
	
GtkWidget *scroll = gtk_scrolled_window_new( NULL, NULL );
gtk_container_set_border_width( GTK_CONTAINER(scroll), 5 );
gtk_container_add( GTK_CONTAINER(GTK_DIALOG(cons_dlg)->vbox), scroll );
gtk_widget_reparent( cons_text, scroll );
gtk_widget_show_all(cons_dlg);
}

//***********************************************
// Destroy console
//***********************************************
void PovrayFE::close_console_dlg()
{
if ( ! GTK_IS_DIALOG(cons_dlg) ) return;
PREF_DEF
if ( pref->save_dlg_geo->value() )
	{
	gint x,y;
	gdk_window_get_root_origin( cons_dlg->window, &x, &y );
	pref->con_xpos->set( x );
	pref->con_ypos->set( y );
	pref->con_xsize->set( cons_dlg->allocation.width );
	pref->con_ysize->set( cons_dlg->allocation.height );
	}

gtk_widget_reparent( cons_text, cons_box );
gtk_widget_destroy( cons_dlg );
cons_dlg = NULL;
}



//***********************************************
// Save
//***********************************************
void PovrayFE::save( ofstream & file )
{
file << "\nRENDER_OPTIONS{\n";
for ( unsigned int i = 0 ; i < tv_widgets.size() ; i++ )
	tv_widgets[i]->save( file );
file << "\n}";
}


bool PovrayFE::load( ifstream & file, char *ltag )
{
if ( strcmp( ltag, "RENDER_OPTIONS" ) ) return false;

char * tag = NULL;
do {
	tag = tvio_get_next_tag( file );
	bool found = false;
	if ( tag == NULL ) break;
	for ( unsigned int i = 0 ; i < tv_widgets.size() ; i++ )
		if( tv_widgets[i]->load( file, tag ) ) { found = true; break; }
	if ( found ) continue;
	tvio_skip_section(file );
	} while ( tag != NULL );

return true;
}


//***********************************************
// Output to povray
//***********************************************
void PovrayFE::output_to_povray( ofstream & file )
{
PREF_DEF
file << "\n\n// Global settings\nglobal_settings {";
file << "\n\tadc_bailout " << adc_bailout->value();
file << "\n\tambient_light "; ambient_light->output_to_povray( file );
file << "\n\tassumed_gamma " << pref->gamma->value();
if ( hf_gray_16->value() ) file << "\n\thf_gray_16 ";
file << "\n\tirid_wavelength "; irid_wavelength->output_to_povray( file );
file << "\n\tmax_trace_level " << max_trace_level->value();
file << "\n\tmax_intersections " << max_intersections->value();
file << "\n\tnumber_of_waves " << number_of_waves->value();

if ( radiosity->value() )
	{
	file << "\n\tradiosity {";
	file << "\n\t\tbrightness " << brightness->value();
	file << "\n\t\tcount " << count->value();
	file << "\n\t\tdistance_maximum " << distance_maximum->value();
	file << "\n\t\terror_bound " << error_bound->value();
	file << "\n\t\tgray_threshold " << gray_threshold->value();
	file << "\n\t\tlow_error_factor " << low_error_factor->value();
	file << "\n\t\tminimum_reuse " << minimum_reuse->value();
	file << "\n\t\tnearest_count " << nearest_count->value();
	file << "\n\t\trecursion_limit " << recursion_limit->value();
	file << "\n\t\t}";
	}
file << "\n\t}\n\n";
}
