//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// scene.cc
//
// Vincent LE PRINCE <vincentleprince@users.sourceforge.net>
// Copyright (C) 2000-2005 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 "config.h"
#include "include/scene.h"
#include "include/interface.h"
#include "include/dlgutils.h"
#include "include/povfe.h"
#include "include/tvio.h"
#include "include/undo.h"
#include "include/heightfield.h"
#include "include/text.h"

// Définitions
const gchar *def_filename = N_("Untitled.tvs");
const char *scene_file_ext = ".tvs";

//***********************************************
// Constructeur
//***********************************************
Scene::Scene( app_objs *appref )
{
app_ref = appref;
app_ref->scene = this;
modified = false;
load_file_size = -1;
save_file_state = -1;

filename = NULL;
set_filename( def_filename );
default_path = NULL;
objlist = new ObjectList( app_ref );
texlist = new MatList( app_ref );

filebox = NULL;
tbw_save = NULL;
mew_save = NULL;
}


//***********************************************
// Nom
//***********************************************
void Scene::set_filename( const char *fname )
{
if ( filename != NULL ) delete filename;

int fname_len = strlen(fname);
filename = new char[ strlen(fname) + 5 ];
strcpy( filename, fname );
if ( strcmp( filename + fname_len - 4, scene_file_ext ) )
	strcat( filename, scene_file_ext );
set_win_title();
}

void Scene::set_win_title()
{
INTERF_DEF
if ( interf == NULL ) return;

int filename_len = strlen( filename );
int path_len = filename_len;
while ( path_len > 0 && filename[ --path_len ] != '/' );

char *title = new char[ strlen(APP_NAME) + filename_len - path_len + 5 ];
strcpy( title, APP_NAME );
strcat( title, " - " );
strcat( title, filename + (( path_len == 0 ) ? 0 : path_len+1 ) );
if ( modified ) strcat( title, "*" );
gtk_window_set_title( &(GNOME_APP(interf->get_gtkwin())->parent_object), title );
delete title;
}

void Scene::set_modified()
{
if ( modified == true ) return;
modified = true;
set_win_title();
save_widgets_active( TRUE );
}


void Scene::save_widgets_active( gboolean var )
{
//gtk_widget_set_sensitive( tbw_save, var );
//gtk_widget_set_sensitive( mew_save, var );
	gtk_action_set_sensitive( save_menu_action, var );
}


//***********************************************
// Output to povray
//***********************************************
void Scene::output_to_povray( char *filename, char *ini_name  )
{
ofstream file( filename, ios::out );
if ( file == NULL ) { app_warning( _("Cannot open file : "), filename ); return; }
file.setf( ios::fixed, ios::floatfield );
//file.setf( ios::showpoint, ios::floatfield );
file << "//---------------------------------------------\n";
file << "// Created with Truevision, version " << VERSION;
file << "\n// Scene file from povray output filter";
file << "\n// Scene : " << filename;
file << "\n//---------------------------------------------\n";
POVFE_DEF
povfe->output_to_povray( file );
texlist->output_to_povray( file );
objlist->output_to_povray( file );
file.close();

if ( ini_name != NULL )
	{
	ofstream file2( ini_name, ios::out );
	if ( file2 == NULL ) { app_warning( _("Cannot open file : "), ini_name ); return; }
	file2.setf( ios::fixed, ios::floatfield );
	file2 << "; ---------------------------------------------\n";
	file2 << ";  Created with Truevision, version " << VERSION;
	file2 << "\n;  Ini file from povray output filter";
	file2 << "\n;  Scene : " << filename;
	file2 << "\n; ---------------------------------------------\n";
	povfe->output_to_povray_ini( file2, filename );
	file2.close();
	}	
}


char *Scene::get_temp_povray_file()
{
char *res = get_temp_filename();
output_to_povray( res );
return res;
}


//***********************************************
// Export to povray
//***********************************************
void Scene::export_to_povray()
{
INTERF_DEF
filebox = gtk_file_chooser_dialog_new( _("Export to..."), (GtkWindow*)interf->get_gtkwin(), 
	GTK_FILE_CHOOSER_ACTION_SAVE,  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
	GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,  NULL);

// Try to guess filename
if ( filename != NULL ) 
	{
	int len = strlen(filename);
	char *guess = new char[ len+ 1 ];
	int i = len;
	while ( filename[i] != '/'  && i != 0 ) i--; 
	if ( i !=  0 ) i++;
	strncpy( guess, filename+i, len - i - 4 );
	guess[ len - i - 4 ] = '\0';
	strcat( guess, ".pov" );
	gtk_file_chooser_set_current_name( GTK_FILE_CHOOSER(filebox), guess );
	delete guess;
	}
else
	gtk_file_chooser_set_current_name( GTK_FILE_CHOOSER(filebox), "export.pov" );
if ( default_path != NULL ) gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(filebox), default_path );

// Run the dialog box
if (gtk_dialog_run (GTK_DIALOG (filebox)) == GTK_RESPONSE_ACCEPT)
  	{
  	char *fname;
  	fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filebox));
	if ( fname == NULL ) return;
	
	// Get Both ini & pov filename from dilog return	
	int len = strlen( fname );
	char *pov_name, *ini_name;
	if ( !strncmp( fname+len-4, ".pov", 4 ) ||  !strncmp( fname+len-4, ".POV", 4 ) )
		{
		pov_name = new char[ len + 1 ];
		strcpy( pov_name, fname );
		ini_name = new char[ len + 1 ];
		strcpy( ini_name, fname );
		strncpy( ini_name+len-4, ".ini", 4 );
		}
	else
		{
		pov_name = new char[ len + 5 ];
		strcpy( pov_name, fname );
		strcat( pov_name, ".pov" );
		ini_name = new char[ len + 5 ];
		strcpy( ini_name, fname );
		strcat( ini_name, ".ini" );
		}
		
	// Check if ini or pov file already exists - Confirmation dialog
	ifstream file( pov_name );
	ifstream file2( ini_name );
	if ( file || file2 )
		{
		file.close();
		file2.close();
		GtkWidget *dialog =  gtk_message_dialog_new ( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("File already exists, overwrite ?"));	
		gint button = gtk_dialog_run( GTK_DIALOG(dialog));
		gtk_widget_destroy( dialog );
		if ( button == GTK_RESPONSE_NO ) { g_free( fname); interf->set_hourglass( false ); gtk_widget_destroy( filebox );	return; }
		}
	
	// Export 
	interf->set_hourglass( true );
	output_to_povray( pov_name, ini_name );
	delete pov_name;
	delete ini_name;
	interf->set_hourglass( false );

  	g_free (fname);
  	}
gtk_widget_destroy( filebox );	
	
}

typedef struct _OBJ_FNAME {
	Object3D* obj;
	char* fname;	
} Obj_Fname;
//***********************************************
// Export scene pack
//***********************************************
void Scene::export_scene_pack()
{
	INTERF_DEF
	filebox = gtk_file_chooser_dialog_new( _("Export to..."), (GtkWindow*)interf->get_gtkwin(), 
	GTK_FILE_CHOOSER_ACTION_SAVE,  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
	GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,  NULL);

	char *extracted_fname, *old_fname;
	char *exp_name, *tvs_name, *pov_name, *ini_name, *tmp_fname, *tmp_fname2 = NULL;
	int pid, len, n;
	char *args[256];
	vector<Obj_Fname*>tmp_obj_list;
	Obj_Fname *of;
	Object3D *obj;
	unsigned int i;
	char cp_path[512];
		
// Try to guess filename
	if ( filename != NULL ) {
		int len = strlen(filename);
		char *guess = new char[ len+ 1 ];
		int i = len;

		while ( filename[i] != '/'  && i != 0 )
			i--;
		
		if ( i !=  0 )
			i++;

		strncpy( guess, filename+i, len - i - 4 );
		guess[ len - i - 4 ] = '\0';
		strcat( guess, ".tar.gz" );
		gtk_file_chooser_set_current_name( GTK_FILE_CHOOSER(filebox), guess );
		delete guess;
	} else 
		gtk_file_chooser_set_current_name( GTK_FILE_CHOOSER(filebox), "export.pov" );
	
	if ( default_path != NULL )
		gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(filebox), default_path );

// Run the dialog box
	if (gtk_dialog_run (GTK_DIALOG (filebox)) == GTK_RESPONSE_ACCEPT) {
		char *fname;
		fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filebox));
		
		if ( fname == NULL )
			return;

		char *tmp_dir = NULL;

		tmp_dir = get_temp_directory();

		if ( tmp_dir == NULL ) {
			printf("Could not create tmp_directory\n"); fflush(stdout);return;
		}

		// Get Both ini & pov filename from dilog return	
		len = strlen( fname );
		if ( ( ends_with(fname, ".tar.gz") == 0 ) && ( ends_with(fname,".tgz") == 0 ) ) {
			exp_name = new char[ len + 8 ];
			strcpy( exp_name, fname );
			strcat( exp_name, ".tar.gz" );
			tmp_fname = extract_filename( fname );
		} else {
			exp_name = new char[ len + 1 ];
			strcpy( exp_name, fname );

			int tail = 7;
			if ( ends_with(fname, ".tgz") == 1 )
				tail = 4;

			tmp_fname2 = new char[ len + 5 - tail];
			
			strncpy( tmp_fname2, fname, (len - tail) );
			tmp_fname2[(len-tail)] = '\0';
			tmp_fname = extract_filename( tmp_fname2 );
		}
		pov_name = new char[ strlen( tmp_dir ) + strlen( tmp_fname ) + 5 ];
		ini_name = new char[ strlen( tmp_dir ) + strlen( tmp_fname ) + 5 ];
		tvs_name = new char[ strlen( tmp_dir ) + strlen( tmp_fname ) + 5 ];
		
		strcpy( pov_name, tmp_dir );
		strcpy( ini_name, tmp_dir );
		strcpy( tvs_name, tmp_dir );

		strcat( pov_name, tmp_fname );
		strcat( ini_name, tmp_fname );
		strcat( tvs_name, tmp_fname );
		
		strcat( pov_name, ".pov" );		
		strcat( ini_name, ".ini" );
		strcat( tvs_name, ".tvs" );

// Check if exp_file already exists - Confirmation dialog
		ifstream file( exp_name );
		if ( file ) {
			file.close();
			GtkWidget *dialog = gtk_message_dialog_new ( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("File already exists, overwrite ?"));	
			gint button = gtk_dialog_run( GTK_DIALOG(dialog));
			gtk_widget_destroy( dialog );
			if ( button == GTK_RESPONSE_NO ) {
				g_free( fname);
				return;
			}
		}
			
		// Export 
		interf->set_hourglass( true );

		char tar[]= TAR_PATH;
		char arg1[]="-czf";
		char arg2[]="-C";
		
		args[0] = tar;
		args[1] = arg1;
		args[2] = exp_name;
		args[3] = arg2;
		args[4] = tmp_dir;
		args[5] = extract_filename(pov_name);
		args[6] = extract_filename(ini_name);
		args[7] = extract_filename(tvs_name);
		int args_cnt = 8;
		for ( int w = 8 ; w < 256 ; w++ ) 
			args[w] = NULL;
		
		for ( i  = 0 ; i < (unsigned int)objlist->get_size() ; i++ ) {
			obj = (Object3D*)objlist->get_object( i );
			if( obj == NULL )
				continue;
			
			if ( obj->get_type() == TV_OBJ3D_TEXT ) {
				of = new Obj_Fname;
				of->fname = strdup(((Text*)obj)->get_font());
				of->obj = obj;

				extracted_fname = extract_filename( of->fname );
				((Text*)obj)->set_font( extracted_fname );
				tmp_obj_list.push_back( of );

				sprintf(cp_path, "%s%s", tmp_dir, extracted_fname);
				cp(of->fname, cp_path);

				for ( n = 8 ; n < args_cnt +1; n++) {
					if ( args[n] != NULL )
						if ( strcmp( extracted_fname, args[n] ) == 0 ) {
							n = -1;
							break;
						}
				}
				if ( n >= 0 )
					args[args_cnt++]  = extracted_fname;
				
			} /*else if ( obj->get_type() == TV_OBJ3D_HEIGHTFIELD ) {
				of = new Obj_Fname;
				of->fname = strdup(((Heightfield*)obj)->get_filename());
				of->obj = obj;
				extracted_fname = extract_filename( of->fname );
				((Heightfield*)obj)->set_filename( extracted_fname );
				tmp_obj_list.push_back( of );
				sprintf(cp_path, "%s%s", tmp_dir, extracted_fname);
				cp(of->fname, cp_path);
				
				for ( n = 8 ; n < args_cnt + 1 ; n++) {
					if ( strcmp( extracted_fname, args[n] ) == 0 ) {
						n = -1;
						break;
					}
				}
				if ( n >= 0 )
					args[args_cnt++]  = extracted_fname;
					}*/
		
		}
		args[args_cnt++]  = '\0';
		args[args_cnt] = NULL;
		
		output_to_povray( pov_name, ini_name );
				
		old_fname = filename;
		filename = tvs_name;
		save();

		filename = old_fname;

		for ( i = 0 ; i < tmp_obj_list.size() ; i++ ) {
			of = (Obj_Fname*)tmp_obj_list[i];
			obj = (Object3D*)of->obj;
			
			if ( obj->get_type() == TV_OBJ3D_TEXT ) {
				((Text*)obj)->set_font(of->fname);
				delete of;
			}/* else if ( obj->get_type() == TV_OBJ3D_HEIGHTFIELD ) {
				((Heightfield*)obj)->set_filename(of->fname);
				delete of;
				}*/
		}
		
		if (( pid = fork() ) < 0 ) 
			app_fatal_err( N_("Scene pack export fork error") );

		if ( pid == 0 ) { 		
			if ( !chdir(tmp_dir) ) {
				if ( execve( *args, args, app_ref->envp ) == -1) {
					printf("execve failed!\n"); fflush(stdout); 
				}
			}
			exit(1);
		}

		delete exp_name;
		delete pov_name;
		delete ini_name;
		delete tvs_name;
		delete tmp_fname2;
		
		free(tmp_fname);
	
		for ( n = 5 ; n < args_cnt - 1 ; n++ ) {
			free(args[n]);
		}
		
		interf->set_hourglass( false );
		g_free (fname);
  	}
	gtk_widget_destroy( filebox );
}


//*******************************************
// Save
//*******************************************
void Scene::save()
{
if ( ! strcmp(  def_filename, filename ) ) { save_as(); return; }

ofstream file( filename, ios::out );
if ( file == NULL ) { app_warning( _("Cannot open file : "), filename ); return; }
file.setf( ios::fixed, ios::floatfield );

// Statistics & progress bar
INTERF_DEF
interf->set_hourglass( true );
interf->set_status( _(" Saving..." ) );
save_file_state = 0;
save_file_size = texlist->get_size() + objlist->get_size();

file << "TRUEVISION SCENE";
file << "\nVERSION " << VERSION;

interf->save( file );
POVFE_DEF
povfe->save( file );

texlist->save( file );
objlist->save( file );

file.close();
modified = false;
set_win_title();
save_widgets_active( FALSE );
interf->set_progress( 0 );
interf->clear_status();
save_file_state = -1;
interf->set_hourglass( false );
}

void Scene::set_save_progress()
{
if ( save_file_state == -1 ) return;
save_file_state++;
float val = (float)save_file_state / (float)save_file_size;
INTERF_DEF
interf->set_progress( val );
}

void Scene::save_as()
{
INTERF_DEF
filebox = gtk_file_chooser_dialog_new( _("Save as..."), (GtkWindow*)interf->get_gtkwin(), 
	GTK_FILE_CHOOSER_ACTION_SAVE,  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
	GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,  NULL);
if ( filename != NULL ) gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(filebox), filename );

if (gtk_dialog_run (GTK_DIALOG (filebox)) == GTK_RESPONSE_ACCEPT)
  	{
  	char *fname;
  	fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filebox));
	if ( fname == NULL ) return;
		
	ifstream file( fname );
	if ( file )
		{
		file.close();
		GtkWidget *dialog =  gtk_message_dialog_new ( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("File already exists, overwrite ?"));	
		gint button = gtk_dialog_run( GTK_DIALOG(dialog));
		gtk_widget_destroy( dialog );
		if ( button == GTK_RESPONSE_NO ) { g_free( fname ); interf->set_hourglass( false ); gtk_widget_destroy( filebox ); return; }
		}
	
	set_filename( fname );
	save();
	interf->recent_file_push( fname );
	store_default_path( fname );
  	g_free (fname);
  	}
gtk_widget_destroy( filebox );	
}


//****************************************
// New
//****************************************
void Scene::new_scene()
{
if ( modified )
	{
	GtkWidget *dialog =  gtk_message_dialog_new ( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Current scene is unsaved, close anyway ?"));	
	gint button = gtk_dialog_run( GTK_DIALOG(dialog));
	gtk_widget_destroy( dialog );
	if ( button == GTK_RESPONSE_NO ) return;
	}

clear_scene();
modified = false;
filename = NULL;
set_filename( def_filename );
objlist->new_scene();
save_widgets_active( FALSE );
}


void Scene::clear_scene()
{
INTERF_DEF
interf->set_hourglass( true );
texlist->clear();
objlist->clear();
interf->clear();
POVFE_DEF
povfe->reset_defaults();
UNDO_DEF
undoman->init();
interf->set_hourglass( false );
}

//***************************************
// Load
//***************************************
void Scene::load_as()
{
	GtkFileFilter *filter1 = gtk_file_filter_new();
		gtk_file_filter_add_pattern( filter1, "*.tvs" );	
		gtk_file_filter_set_name( filter1, "Truevision scene file" );	
	GtkFileFilter *filter2 = gtk_file_filter_new();
		gtk_file_filter_add_pattern( filter2, "*.*" );	
		gtk_file_filter_set_name( filter2, "All files" );	

	INTERF_DEF
	filebox = gtk_file_chooser_dialog_new( _("Open..."), (GtkWindow*)interf->get_gtkwin(), 
	GTK_FILE_CHOOSER_ACTION_OPEN,  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
	GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,  NULL);
	if ( default_path != NULL ) 
		gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(filebox), default_path );
	gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(filebox), filter1 );
	gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(filebox), filter2 );

	if (gtk_dialog_run (GTK_DIALOG (filebox)) == GTK_RESPONSE_ACCEPT) {
  		char *fname;
  		fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filebox));
		if ( fname == NULL ) { 
			g_free( fname ); interf->set_hourglass( false ); 
			gtk_widget_destroy( filebox );	return; 
		}
		load( fname );	
  		g_free (fname);
  	}
	gtk_widget_destroy( filebox );	
}


void Scene::load( gchar *gname )
{
	VMAN_DEF
	INTERF_DEF
	interf->set_hourglass( true );
	char *fname = new char[ strlen(gname) + 1 ];
	strcpy( fname,  gname );
	store_default_path( gname );

	ifstream file( fname, ios::binary );
	if ( file == NULL ) {
		app_warning( _("Cannot open : "), fname );
		delete fname;
		interf->recent_file_pop( gname );
		interf->set_hourglass( false );
		return;
	}
	if ( ! tvio_check_file_sig( file, "TRUEVISION SCENE" ) ) {
		app_warning( _("File type unknown : "), fname );
		file.close();
		delete fname;
		interf->recent_file_pop( gname );
		interf->set_hourglass( false );
		return;
	}
	tvio_get_soft_version( file );

	if ( modified ) {
		GtkWidget *dialog =  gtk_message_dialog_new ( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Current scene is unsaved, close anyway ?"));	
		gint button = gtk_dialog_run( GTK_DIALOG(dialog));
		gtk_widget_destroy( dialog );
		if ( button == GTK_RESPONSE_NO ) {
			interf->set_hourglass( false );
			return;
		}
	}

	// Statistics
	interf->set_status( _(" Loading...") );
	file.seekg( 0, ios::end );	
	load_file_size = file.tellg();
	file.seekg( 0, ios::beg );	
	
	// Clear scene	
	texlist->clear();
	objlist->clear();
	POVFE_DEF
	UNDO_DEF
	undoman->init();
	povfe->reset_defaults();
	invalid_font_path = false;
	
	// Load objects
	char * tag = NULL;
	do {
		tag = tvio_get_next_tag( file );
		set_load_progress( file );
		if ( tag == NULL ) { break; }
		if( interf->load( file, tag ) ) continue;
		if( povfe->load( file, tag ) ) continue;
		if( texlist->load( file, tag ) ) continue;
		if( objlist->load( file, tag ) ) continue;
		tvio_skip_section(file );
	} while ( tag != NULL );	

	file.close();
	modified = false;
	set_filename( fname );
	delete fname;
	save_widgets_active( FALSE );

	interf->clear_status();
	interf->set_progress( 0 );
	interf->recent_file_push( gname );
	load_file_size = -1;
	//vmanager->invalidate();
	vmanager->refresh();
	interf->set_hourglass( false );
	while ( gtk_events_pending() ) 
		gtk_main_iteration();
	//interf->jitter();
	gtk_widget_queue_draw( interf->get_gtkwin() );

	// If an invalid path to a font file was detected, warn the user
	if ( invalid_font_path ) {
		app_warning( "Scene uses font(s) that was not found on system, default font used instead" );
	}
}


void Scene::set_load_progress( ifstream & file )
{
if (  load_file_size == -1 ) return;
int state = file.tellg();
float val = (float)state / (float)load_file_size;
if ( val > 1.0 || val < 0 ) return;
INTERF_DEF
interf->set_progress( val );
}



void Scene::store_default_path( char *path )
{
if ( default_path != NULL ) delete default_path;
int len = strlen( path );
while ( len > 0 && path[len] != '/' ) len--;
if ( len == 0 ) return;
default_path = new char[ len +2 ];
strncpy( default_path, path, len+1 );
default_path[len+1] = '\0';
}
