/*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// matlib.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 "include/matlib.h"
#include "include/matlist.h"
#include "include/preferences.h"
#include "config.h"
#include "include/tvio.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include <zlib.h>
#include <algorithm>

const char *sep = "/";
bool compare_items( MatLibItem *item1, MatLibItem *item2 ) { return ( strcmp( item1->name, item2->name ) < 0 ) ? true : false; }
bool compare_nodes( MatLibNode *node1, MatLibNode *node2 ) { return ( strcmp( node1->name, node2->name ) < 0 ) ? true : false; }

//*******************************************************
// Node
//*******************************************************
app_objs *MatLibNode::app_ref = NULL;

MatLibNode::MatLibNode( char *syspath, char *hpath, char *nom, app_objs *appref )
{
app_ref = appref;

// Noms de répertoires
if ( nom == NULL )
	{
	// Root
	char *matlibname = _("Material Library");
	name = new char[ strlen(matlibname) +1 ];
	strcpy( name, matlibname );
	
	if ( syspath != NULL )
		{
		systempath = new char[ strlen(syspath) + 1 ];
		strcpy( systempath, syspath );
		}
	else systempath = NULL;
	if ( hpath != NULL )
		{
		homepath = new char[ strlen(hpath) + 1 ];
		strcpy( homepath, hpath );
		}
	else homepath = NULL;
	}
else
	{
	// SubNode
	int nsize = strlen( nom ) +2;
	name = new char[nsize-1];
	strcpy( name, nom );

	if ( syspath != NULL )
		{
		systempath = new char[ strlen(syspath) + nsize ];
		strcpy( systempath, syspath );
		strcat( systempath, sep );
		strcat( systempath, nom );
		}
	else systempath = NULL;
	
	if ( hpath != NULL )
		{
		homepath = new char[ strlen(hpath) + nsize ];
		strcpy( homepath, hpath );
		strcat( homepath, sep );
		strcat( homepath, nom );
		}
	else homepath = NULL;
	}

// Chargement des fichiers et des sous répertoires		
char *path[2] = { systempath, homepath };
for ( int i = 0 ; i < 2 ; i++ )
	{
	if ( path[i] == NULL ) continue;
	int psize = strlen( path[i] ) + 2;
	DIR *dir = opendir( path[i] );
	 if ( dir == NULL ) continue;
	 struct dirent *dp;
	 do  {
	 		dp = readdir( dir );
	 		if ( dp == NULL ) break;
	 		if ( dp->d_name[0] == '.' ) continue;
	 		char *tmp = new char[ psize + strlen( dp->d_name )  ];
	 		strcpy( tmp, path[i] );
	 		strcat( tmp, sep );
	 		strcat( tmp, dp->d_name );
	 		
	 		DIR *subdir = opendir( tmp );
	 		if ( subdir == NULL )
	 			{
		 		//cout << "\n\tWe got this file : " << tmp << " on dir " << dp->d_name;	 cout.flush();
		 		MatLibItem *item = new MatLibItem( tmp, dp->d_name, app_ref );	
		 		Children.push_back( item );
		 		//cout << "\n\tFile registered  !  "; cout.flush();	 				 		
		 		}
			else
				{
		 		//cout << "\nWe got this dir : " << tmp; cout.flush();
		 		closedir( subdir );
		 		bool exist = false;
		 		for ( register unsigned int i = 0 ; i < SubNodes.size() ; i ++ )
		 			if ( !strcmp( SubNodes[i]->name, dp->d_name ) ) { exist = true ; break; }
		 		if ( ! exist )
		 			{	 			
			 		MatLibNode *node = new MatLibNode( path[0], path[1],  dp->d_name, app_ref );
			 		SubNodes.push_back( node );
			 		}
				}		 				 		
	 		//cout << "\nLoop iteration..."; cout.flush();
	 		delete tmp;
			 } while ( dp != NULL );
	//cout << "\nClosing dir..."; cout.flush();
	closedir( dir );
	}
	
// Tri
//cout << "\nSorting " << name << "... ";             cout.flush();
sort( Children.begin(), Children.end(), compare_items );
sort( SubNodes.begin(), SubNodes.end(), compare_nodes );
//cout << "Done."; cout.flush();
}


// Destructeur
MatLibNode::~MatLibNode()
{
// TREE !!
delete name;
if ( homepath != NULL ) delete homepath;
if ( systempath != NULL ) delete systempath;
}


void MatLibNode::add_to_tree( GtkWidget *tree, GtkCTreeNode *parent  )
{
char *text[] = { name, NULL };
node = gtk_ctree_insert_node( GTK_CTREE(tree), parent, NULL, text, 0, NULL, NULL, NULL, NULL, FALSE, ( parent == NULL ) ? TRUE : FALSE );
gtk_ctree_node_set_row_data( GTK_CTREE(tree), node, (gpointer)this );

for ( register unsigned int i = 0 ; i < SubNodes.size() ; i ++ )
	SubNodes[i]->add_to_tree( tree, node );
}


void MatLibNode::draw_thumbs( GtkWidget *box, GtkWidget *viewp, GtkWidget *appbar )
{
const char *mesg1 = N_("Display");
char  *mesg = new char[ strlen( mesg1 ) + strlen(name) + 10 ];
sprintf( mesg, " %s %s ...", mesg1, name );
gnome_appbar_set_status(  GNOME_APPBAR(appbar), mesg );
delete mesg;

unsigned int node_size = Children.size();
selected = NULL;
unsigned int xsize = viewp->allocation.width / 120;
unsigned int ysize = node_size / xsize +1;
GtkWidget *hbox;
unsigned int k = 0;
for ( unsigned int i = 0 ; i < ysize ; i++ )
	{
	if ( k == node_size ) break;
	hbox = gtk_hbox_new( FALSE, 2 );
	gtk_box_pack_start( GTK_BOX(box), hbox, FALSE, TRUE, 10 );
	for ( unsigned int j = 0 ; j < xsize ; j++ )
		{
		Children[k++]->draw_thumb( hbox );
		gnome_appbar_set_progress_percentage( GNOME_APPBAR(appbar), (float)k/(float)node_size );
		while ( gtk_events_pending() > 0 ) gtk_main_iteration();
		if ( k == node_size ) break;
		}
	}
		
gnome_appbar_set_progress_percentage( GNOME_APPBAR(appbar), 0 );
const char *mesg2 = N_("Node :");
char  *mesg3 = new char[ strlen( mesg1 ) + strlen(name) + 50 ];
sprintf( mesg3, " %s %s , %u materials", mesg2, name, node_size );
gnome_appbar_set_status(  GNOME_APPBAR(appbar), mesg3 );
delete mesg3;
}


void MatLibNode::change_selection( MatLibItem *selection )
{
if ( selected != NULL ) selected->unselect();
if ( selected != selection ) selected = selection;
else selected = NULL;
}


//*******************************************************
// Item
//*******************************************************
app_objs *MatLibItem::app_ref = NULL;
GtkStyle *MatLibItem::old_style = NULL;
GtkStyle *MatLibItem::new_style = NULL;


MatLibItem::MatLibItem( char *file, char *nom, app_objs *appref )
{
app_ref = appref;
path = new char[ strlen( file ) +1 ];
strcpy( path, file );
name = new char[ strlen( nom ) + 1 ];
strcpy( name, nom );
real_name = NULL;
comment = NULL;
author = NULL;
}

MatLibItem::~MatLibItem()
{
delete path;
delete name;
}


void sign_thumb_data_free( guchar *data, gpointer sender ) { delete data; }

void MatLibItem::draw_thumb( GtkWidget *box )
{
GtkWidget *widget = gtk_vbox_new( FALSE, 2 );
gtk_box_pack_start( GTK_BOX(box), widget, FALSE, TRUE, 10 );

GtkWidget *preview_widget = gtk_image_new();
gtk_box_pack_start( GTK_BOX(widget), preview_widget, TRUE, TRUE, 2 );
bool has_preview = true;

ifstream file( path, ios::binary );
if ( ! file )  has_preview = false;
char * tag = NULL;
char *val = NULL;
unsigned long size = 0;

do {
    if ( !has_preview ) break;
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) { has_preview = false; break; }
	
	if ( ! strcmp( "PREVIEW", tag ) )
		{
      do
      		{
			val = tvio_get_next_val( file );
			if ( val == NULL ) break;
			if ( ! strcmp( val, "SIZE" ) )
				{
				size = tvio_get_value_as_int( file );
				continue;
				}
			if ( !strcmp( val, "DATA" ) )
				{
				if ( size == 0 ) { has_preview = false; break; }
				char *zdata = new char[ size+1 ];
				for ( unsigned long i = 0 ; i < size ; i ++ ) zdata[i] = file.get();
				unsigned long destlen = 19200;
				char *data = new char[ destlen ];
				if ( uncompress( (Bytef*)data, (uLongf*)&destlen, (Bytef*)zdata, size ) != Z_OK ) 	{ has_preview= false; break; }
				GdkPixbuf *image_buffer = gdk_pixbuf_new_from_data( (guchar*)data, GDK_COLORSPACE_RGB, FALSE, 8, 80, 80, 79*3, (sign_thumb_data_free), this );
				gtk_image_set_from_pixbuf( GTK_IMAGE(preview_widget), image_buffer );
				gtk_widget_realize( preview_widget );
				gtk_widget_show( preview_widget );
				g_object_unref( image_buffer );	
				delete zdata;
				break;
				}
			}    while( val != NULL );
	break;
	tvio_skip_section( file );	
	}  // IF PREVIEW	
	
	if ( !strcmp( tag, "MATERIAL" ) )
		{
		has_preview = false;
		break;
		}
		
	tvio_skip_section( file );
  }  while( tag != NULL );
file.close();

if ( !has_preview ) { gtk_widget_destroy(preview_widget); load_unknown_icon( widget ); }
label = gtk_toggle_button_new_with_label( name );
gtk_button_set_relief( GTK_BUTTON(label), GTK_RELIEF_NONE );
gtk_box_pack_start( GTK_BOX(widget), label, TRUE, TRUE, 2 );
gtk_signal_connect( GTK_OBJECT(label), "clicked", GTK_SIGNAL_FUNC(sign_matlib_item_clicked), this );

if ( old_style == NULL )
	{
	old_style = gtk_widget_get_style( label );
	new_style = gtk_style_copy( old_style );
	GdkColor col = { 0, 0x8888, 0x8888, 0xdddd };
	GdkColormap *cmap = gdk_colormap_get_system();
	gdk_colormap_alloc_color( cmap, &col, FALSE, TRUE );
	for (int i = 0 ; i < 4 ; i ++ )
		{
		new_style->bg[i] = col;
		new_style->base[i] = col;		
		new_style->fg[i] = col;		
		}
	}
gtk_widget_show_all( widget );
while ( gdk_events_pending() ) gtk_main_iteration();
}


void MatLibItem::load_unknown_icon( GtkWidget *vbox )
{
char *fname = tv_get_pixmap( "nopreview.xpm" );
GtkWidget *pix = gtk_image_new_from_file( fname );
delete fname;
gtk_box_pack_start( GTK_BOX(vbox), pix, TRUE, TRUE, 2 );
}


void MatLibItem::selected()
{
MATLIB_DEF
matlib->sign_mat_selected( this );
gtk_widget_set_style( label , new_style );
}


void MatLibItem::load()
{
TEXLIST_DEF
texlist->mat_load_from_file( path );
}

			
void MatLibItem::show_info(TvWidget_entry *name_label, TvWidget_entry *author_label, TvWidget_text *description  )
{
ifstream file( path, ios::binary );
if ( ! file )  return;
char * tag = NULL;
char *val = NULL;

do {
	tag = tvio_get_next_tag( file );
	
	if ( tag == NULL ) { break; }
 	if (author_label->load( file , tag ) )  continue;
	if (description->load( file , tag ) ) continue;		

    if ( ! strcmp( "MATERIAL", tag ) )
		{
      do
      		{
			val = tvio_get_next_tag( file );
			if ( val == NULL ) break;
			if (name_label->load( file , val ) ) continue;	
			tvio_skip_section( file );				
			}
		while( val != NULL );
	}
    	
    if ( ! strcmp( "PREVIEW", tag ) )
		{
      do
      		{
			val = tvio_get_next_val( file );
			if ( val == NULL ) break;
			if ( ! strcmp( val, "SIZE" ) )
				{
				int size = tvio_get_value_as_int( file );
				file.seekg( size+6, ios::cur );
				break;
				}
			}
		while( val != NULL );
	
	tvio_skip_section( file );	
	continue;
	}

	tvio_skip_section( file );
  }  while( tag != NULL );
file.close();
author_label->update_widget();
name_label->update_widget();
description->update_widget();
}



//*******************************************************
// Library
//*******************************************************
MatLib::MatLib( app_objs *appref )
{
app_ref = appref;
app_ref->matlib = this;
dialog = NULL;
PREF_DEF

// Chemins des bases de matériels
char *tmp = tv_get_data( "materials" );

if ( tmp != NULL )
	{
	systemlib = new char[strlen( tmp ) + 1];
	strcpy( systemlib, tmp );
	delete tmp;
	}
else systemlib = NULL;
char *home = pref->get_home_dir();
if ( home != NULL )
	{
	homelib = new char[ strlen(pref->get_home_dir()) + 12 ];
	strcpy( homelib, pref->get_home_dir() );
	strcat( homelib, "/materials" );
	if ( access( homelib, F_OK ) == -1 ) mkdir( homelib, S_IRWXU );
	}
else homelib = NULL;

root = new MatLibNode( systemlib, homelib, NULL, app_ref );
}

MatLib::~MatLib()
{
if ( homelib != NULL ) delete homelib;
if ( systemlib != NULL ) delete systemlib;
delete root;
}



void MatLib::raise_dlg_box()
{
if ( GTK_IS_DIALOG(dialog) )
	{
	gdk_window_raise( dialog->window );
	return;
	}
PREF_DEF
bool tt = pref->tooltips->value();

//dialog = gnome_dialog_new( _("Material Library"), GNOME_STOCK_BUTTON_OK, GNOME_STOCK_BUTTON_APPLY, GNOME_STOCK_BUTTON_CLOSE, NULL );
dialog =  gtk_dialog_new_with_buttons( _("Material Library"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL );
//gtk_window_set_policy( GTK_WINDOW(dialog), TRUE, TRUE, FALSE );
//gtk_window_set_wmclass( GTK_WINDOW(dialog), PACKAGE, "Material Library" );
//gtk_window_set_position( GTK_WINDOW(dialog),  GTK_WIN_POS_NONE );
g_signal_connect( G_OBJECT(dialog), "response", G_CALLBACK(sign_matlib_dlg_click), this );
g_signal_connect( G_OBJECT(dialog), "close", G_CALLBACK(sign_matlib_dlg_destroy), this );
if ( pref->save_dlg_geo->value() )
	{
	if ( pref->mlib_xpos->value() < 0 ) pref->mlib_xpos->set(0);
	if ( pref->mlib_ypos->value() < 0 ) pref->mlib_ypos->set(0);	
	gtk_widget_set_uposition( dialog, pref->mlib_xpos->value(), pref->mlib_ypos->value() );
	gtk_widget_set_usize( dialog, pref->mlib_xsize->value(), pref->mlib_ysize->value() );
	}

GtkWidget *hbox = gtk_hbox_new( FALSE, 2 );
gtk_box_pack_start( GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 2 );

// Arbre
	GtkWidget *scrolled = gtk_scrolled_window_new( NULL, NULL );
	gtk_widget_set_usize( scrolled, 150, -1 );
	gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
	gtk_box_pack_start( GTK_BOX(hbox), scrolled, TRUE, TRUE, 4 );
	GtkWidget *viewp = gtk_viewport_new( NULL, NULL );
	gtk_viewport_set_shadow_type( GTK_VIEWPORT(viewp), GTK_SHADOW_ETCHED_IN );
	gtk_container_add( GTK_CONTAINER(scrolled), viewp );

	dlg_tree = gtk_ctree_new( 1, 0 );
	gtk_ctree_set_expander_style( GTK_CTREE(dlg_tree), GTK_CTREE_EXPANDER_CIRCULAR );
	gtk_clist_set_selection_mode( GTK_CLIST(dlg_tree), GTK_SELECTION_SINGLE );
	gtk_container_add( GTK_CONTAINER(viewp), dlg_tree );
	gtk_signal_connect( GTK_OBJECT(dlg_tree), "tree_select_row", GTK_SIGNAL_FUNC(sign_matlib_tree_select), this );
    root->add_to_tree( dlg_tree, NULL );
	

// Visualiseur
GtkWidget *vbox = gtk_vbox_new( FALSE, 3 );
gtk_box_pack_start( GTK_BOX(hbox), vbox, TRUE, TRUE, 2 );

	// Thumbs
	scrolled = gtk_scrolled_window_new( NULL, NULL );
	gtk_widget_set_usize( scrolled, 600, 280 );
	gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
	gtk_box_pack_start( GTK_BOX(vbox), scrolled, TRUE, TRUE, 4 );
	thumbs_viewp = gtk_viewport_new( NULL, NULL );
	gtk_viewport_set_shadow_type( GTK_VIEWPORT(thumbs_viewp), GTK_SHADOW_ETCHED_OUT );
	gtk_container_add( GTK_CONTAINER(scrolled), thumbs_viewp );
	thumbs_table = NULL;
	
	// Statusbar
	app_bar = gnome_appbar_new( TRUE, TRUE, GNOME_PREFERENCES_NEVER );
	gtk_box_pack_start( GTK_BOX(vbox), app_bar, TRUE, TRUE, 2 );	
	
	// Description
	GtkWidget *dframe = gtk_frame_new( NULL );
	//gtk_widget_set_usize( dframe, -1, 70 );
	gtk_box_pack_start( GTK_BOX(vbox), dframe, TRUE, TRUE, 4 );	
	GtkWidget *dbox = gtk_vbox_new( FALSE, 5 );
	gtk_container_set_border_width( GTK_CONTAINER(dbox), 3 );
	gtk_container_add( GTK_CONTAINER(dframe), dbox );
		name_label = new TvWidget_entry( 	 _("Name"), "NAME", NULL, app_ref );
		name_label->get_widget( dbox, tt );
	
	author_label = new TvWidget_entry( _("Author"), "AUTHOR", NULL, app_ref );
	author_label ->get_widget( dbox, tt );

	description = new TvWidget_text( _("Comment"), "COMMENT", NULL, app_ref );
	description->get_widget( dbox, tt );
	
gtk_widget_show_all(dialog);
root->select( dlg_tree );
}



void MatLib::clicked_dlg( int button )
{
switch( button )
	{
	// Load & Close			
	case GTK_RESPONSE_OK:
		selected_node->load_selected();
		clicked_dlg( -1 );
		break;
		
	// Load ( apply )
	case GTK_RESPONSE_APPLY:
		selected_node->load_selected();	
		break;
	
	// Close
	default:
	case GTK_RESPONSE_CLOSE:
		PREF_DEF
		if ( pref->save_dlg_geo->value() )
			{
			gint x,y;
			gdk_window_get_root_origin( dialog->window, &x, &y );
			pref->mlib_xpos->set( x );
			pref->mlib_ypos->set( y );
			pref->mlib_xsize->set( dialog->allocation.width );
			pref->mlib_ysize->set( dialog->allocation.height );
			}
		gtk_widget_destroy( dialog );
		delete name_label;
		delete author_label;
		delete description;
		dialog = NULL;
		break;
	}
}

  			
  			
  			
void MatLib::tree_select( GtkCTree *tree, GtkCTreeNode *node, int col )
{
if ( thumbs_table != NULL ) gtk_widget_destroy( thumbs_table );
selected_node = (MatLibNode*)gtk_ctree_node_get_row_data( tree, node );
thumbs_table = gtk_vbox_new( FALSE, 25 );
gtk_container_add( GTK_CONTAINER(thumbs_viewp), thumbs_table );
selected_node->draw_thumbs( thumbs_table, thumbs_viewp, app_bar );
gtk_widget_show_all( thumbs_table );
}



void MatLib::sign_mat_selected( MatLibItem *selection )
{
selected_node->change_selection( selection );
selection->show_info( name_label, author_label, description );
}
