//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// matpov.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 "include/matpov.h"
#include "include/matlist.h"
#include "include/tvio.h"

//**************************************
//
// Pov Material
// 
//**************************************
PovMaterial::PovMaterial( app_objs *appref, char *tagname ) : Material( appref, tagname )
{
type = TV_MAT_POV;
set_name( _("Material#1") );
interior = new PovTexInterior( app_ref, "INTERIOR" );
texture = new PovTexture( app_ref, "TEXTURE" );
interior_texture = new PovTexture( app_ref, "INTERIORTEX" );
interior_texture->set_name( N_("Interior texture") );
quick_color = new TvWidget_glmat( N_("OpenGL material"), "GLMAT", NULL, app_ref );
transfo = new TvWidget_transformation( N_("Transformation"), "TRANSFO", NULL, app_ref );
}

PovMaterial::~PovMaterial()
{
destroy_editor();
delete interior;
delete texture;
delete interior_texture;
delete quick_color;
delete transfo;
}

//**************************************
// Edit Material
//**************************************
void PovMaterial::get_editor(  GtkTreeView *view, GtkTreeStore *store, GtkTreeSelection *selection, GtkWidget *box )
{
Material::get_editor( view, store, selection, box );
texture->add_to_tree( view, store, selection, &node_iter );
interior_texture->add_to_tree( view, store, selection, &node_iter );
interior->add_to_tree( view, store, selection, &node_iter );
//gtk_ctree_expand_recursive( ctree, ctree_node );
//gtk_ctree_select( ctree, ctree_node );
set_node_state();
select_tree_row();
}

void PovMaterial::edit_widget( GtkWidget *box )
{
bool tt = true;
Material::edit_widget( box );

GtkWidget *vbox = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(edit_cont), vbox, TRUE, TRUE, 0 );
quick_color->get_widget( vbox, tt );
transfo->get_widget( vbox, tt );
gtk_widget_show_all( widget );
}

void PovMaterial::destroy_widget()
{
MaterialItem::destroy_widget();
transfo->clear_widget();
quick_color->clear_widget();
}

void PovMaterial::flush()
{
transfo->flush();
quick_color->flush();
}

void PovMaterial::remove_from_tree()
{
texture->remove_from_tree();
interior_texture->remove_from_tree();
interior->remove_from_tree();
MaterialItem::remove_from_tree();
}


//**************************************
// output
//**************************************
void PovMaterial::output_to_povray( ofstream & file )
{
texture->output_to_povray_define_layered(file);
file << "\n#declare ";
get_underscore_name( file );
file << " = material {";
texture->output_to_povray(file);
if ( interior_texture->is_used() ) interior_texture->output_to_povray_as_interior(file);
interior->output_to_povray(file);
transfo->output_to_povray(file);
file << "}\n";
}

//***************************************************************
// Save
//***************************************************************
void PovMaterial::save( ofstream & file )
{
file << "\n\nMATERIAL{\n";
name->save( file ); file << "\n";
quick_color->save( file ); file << "\n";
transfo->save( file ); file << "\n";
texture->save( file ); file << "\n";
interior_texture->save( file ); file << "\n";
interior->save( file ); file << "\n";
file << "\n}\n";
}


bool PovMaterial::load( ifstream & file )
{
char * tag = NULL;
char *val = NULL;

do {
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) { app_warning( _("Corrupted file, sorry.") ); return false; }
	
	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 );
				//cout << "\nGot size of preview -> " << size;
				//cout.flush();
				break;
				}
			}
		while( val != NULL );
	tvio_skip_section( file );	
	continue;
	}
	
	if ( author->load( file, tag ) ) continue;
	if ( comment->load( file, tag ) ) continue;
	
	if ( ! strcmp( "MATERIAL", tag ) ) 
		{	
		while ( tag != NULL )
			{
			tag = tvio_get_next_tag( file );
			if ( tag == NULL ) break;

			if ( name->load( file , tag ) ) { set_name( name->value() ); continue; }
			if ( quick_color->load( file , tag ) ) continue;
			if ( transfo->load( file , tag ) ) continue;
			if ( interior->load( file , tag ) ) continue;
			if ( texture->load( file , tag ) ) continue;
			if ( interior_texture->load( file , tag ) ) continue;
			tvio_skip_section( file );
			}
		}
	tvio_skip_section(file );
	} while ( tag != NULL );
return false;
}


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

char * tag = NULL;
do {
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
		
	if ( name->load( file , tag ) ) { set_name( name->value() ); continue; }
	if ( quick_color->load( file , tag ) ) continue;
	if ( transfo->load( file , tag ) ) continue;
	if ( interior->load( file , tag ) ) continue;
	if ( texture->load( file , tag ) ) continue;
	if ( interior_texture->load( file , tag ) ) continue;
	tvio_skip_section(file );
	} while ( tag != NULL );
return true;
}


bool PovMaterial::paste( MaterialItem *item )
{
switch( item->get_type() )
	{
	case TV_MITEM_INTERIOR:
		return interior->paste( item );
		break;
			
	default:
		return false;
	}
return false;
}







//******************************************************************
//
//    POVRAY TEXTURE
// 
//******************************************************************
int PovTexture::count = 0;
const int tex_type_num = 5;
const char *tex_type_list[tex_type_num] = { 
	N_("Plain"),
	N_("Layered"),
	N_("Texture list"),
	N_("Texture map"),
	N_("Material map"),
};

MapItem *mapedit_feeder_tex( gpointer data )
{ return (MapItem*)( new MapItem_texture( 0, 0, ((PovTexture*)data)->app_ref ) ); }


void PovTexture::constructor_base()
{
texture_num = count++;
set_name( "Texture" );
item_type = TV_MITEM_TEXTURE;
mother = NULL;
pigment = new PovTexPigment( app_ref, "PIGMEN" );
normal = new PovTexNormal( app_ref, "NORMAL" );
finish = new PovTexFinish( app_ref, "FINISH" );
changing_box1 = changing_box2 = NULL;

type = new TvWidget_option_combo( N_("Type"), "TYPE", NULL, app_ref );
type->set_list( tex_type_list, tex_type_num, 0 );
transfo = new TvWidget_transformation_rb( N_("Transformation"), "TRANSFO", NULL, app_ref );
bitmap = NULL;
mapedit = NULL;
blendmap = NULL;
blendmap_mod = NULL;
blockpattern = NULL;
warp = NULL;
noise = NULL;
}

PovTexture::PovTexture( app_objs *appref, char *tagname ) : MaterialItem( appref, tagname )
{
constructor_base();
is_mother = true;
}

PovTexture::PovTexture( app_objs *appref, char *tagname, void (*asuicidal_func)(gpointer, gpointer), void (*amove_func)(gpointer, gpointer, int ), gpointer amother ) : MaterialItem( appref, tagname )
{
constructor_base();
is_mother = false;
mother = (PovTexture*)amother;
suicidal_func = asuicidal_func;
move_func = amove_func;
}

PovTexture::PovTexture( PovTexture & ref ) : MaterialItem( ref )
{
texture_num = count++;
set_name( ref.get_name() );
item_type = TV_MITEM_TEXTURE;
is_mother = true;
pigment = new PovTexPigment( *ref.pigment );
normal = new PovTexNormal( *ref.normal );
finish = new PovTexFinish( *ref.finish );
changing_box1 = changing_box2 = NULL;

type = new TvWidget_option_combo( *ref.type );
transfo = new TvWidget_transformation_rb( *ref.transfo );
if ( ref.bitmap != NULL ) bitmap = new TvWidget_bitmap( *ref.bitmap ); else bitmap = NULL;
if ( ref.mapedit != NULL ) mapedit = new TvWidget_map_editor( *ref.mapedit ); else mapedit = NULL;
if ( ref.blendmap != NULL ) blendmap = new TvWidget_blendmap( *ref.blendmap ); else blendmap = NULL;
if ( ref.blendmap_mod != NULL ) blendmap_mod = new TvWidget_blendmap_mod( *ref.blendmap_mod ); else blendmap_mod = NULL;
if ( ref.blockpattern != NULL ) blockpattern = new TvWidget_blockpattern( *ref.blockpattern ); else blockpattern = NULL;
if ( ref.noise != NULL ) noise = new TvWidget_noise_rb( *ref.noise ); else noise = NULL;
if ( ref.warp != NULL ) warp = new TvWidget_warp_rb( *ref.warp ); else warp = NULL;

for ( unsigned int i = 0 ; i < ref.texture_list.size() ; i++ )
	{
	PovTexture *item =  new PovTexture( *ref.texture_list[i] );
	texture_list.push_back( item );
	}
}

void PovTexture::set_params( void (*asuicidal_func)(gpointer, gpointer), void (*amove_func)(gpointer, gpointer, int ), gpointer amother )
{
is_mother = false;
mother = (PovTexture*)amother;
suicidal_func = asuicidal_func;
move_func = amove_func;
}


PovTexture::~PovTexture()
{
delete pigment;
delete normal;
delete finish;
delete type;
delete transfo;
for ( unsigned int i = 0 ; i < texture_list.size() ; i++ ) delete texture_list[i];
if ( bitmap != NULL ) delete bitmap;
if ( blockpattern != NULL ) delete blockpattern;
if ( mapedit != NULL ) delete mapedit;
if ( blendmap != NULL ) { delete blendmap; delete blendmap_mod; }
if ( noise != NULL ) delete noise;
if ( warp != NULL ) delete warp;
}

//****************************************************
// Edit Widget
//****************************************************
void PovTexture::edit_widget( GtkWidget *box )
{
MaterialItem::edit_widget( box, _("Texture") );
bool tt = true;
//GtkWidget *frame_box;
vbox = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(edit_cont), vbox, TRUE, TRUE, 0 );

// Check wether we are not a patterned texture in a layered one ( forbidden ! )
bool must_be_plain = false;
if ( mother != NULL )  	if ( mother->is_layered() ) must_be_plain = true;

if ( !must_be_plain )
	{
	type->get_widget( vbox, tt );
	type->connect_signal( GTK_SIGNAL_FUNC(sign_texture_type_changed), this );
	}
else type->set(0);


if ( !is_mother && suicidal_func != NULL )
	{
	GtkWidget *del_button = gtk_button_new_with_label( _("Delete this texture") );
	gtk_box_pack_start( GTK_BOX(vbox), del_button, FALSE, TRUE, 2 );
	gtk_signal_connect( GTK_OBJECT(del_button), "clicked", GTK_SIGNAL_FUNC(sign_texture_delete), this );
	}
set_changing_box();

gtk_widget_show_all( widget );
}


//***************************************************************
// flush
//***************************************************************
void PovTexture::flush()
{
transfo->flush();
type->flush();
if ( bitmap != NULL ) bitmap->flush();
if ( blockpattern != NULL ) blockpattern->flush();
if ( mapedit != NULL ) mapedit->flush();
if ( blendmap != NULL ) { blendmap->flush(); blendmap_mod->flush(); }
if ( noise != NULL ) noise->flush();
if ( warp != NULL ) warp->flush();
}


//***************************************************************
// Destroy widget
//***************************************************************
void PovTexture::destroy_widget()
{
MaterialItem::destroy_widget();
	transfo->clear_widget();
	type->clear_widget();
	if ( bitmap != NULL ) bitmap->clear_widget();
	if ( blockpattern != NULL ) blockpattern->clear_widget();
	if ( mapedit != NULL ) mapedit->clear_widget();
	if ( blendmap != NULL ) { blendmap->clear_widget(); blendmap_mod->clear_widget(); }
	if ( noise != NULL ) noise->clear_widget();
	if ( warp != NULL ) warp->clear_widget();
changing_box1 = NULL;
}

//***************************************************************
// Type changed
//***************************************************************
void PovTexture::type_changed()
{
type->flush();
if ( type->value() != 0 )
	{
	finish->remove_from_tree();
	pigment->remove_from_tree();
	normal->remove_from_tree();
	}
else
	{
	finish->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
	pigment->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
	normal->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
	}
if ( type->value() > 0  && type->value() != 3 && texture_list.size() < 2 )
	{
	PovTexture *subtex = new PovTexture( app_ref, "TEXTURE", sign_texture_child_delete, sign_texture_child_move, this );
	texture_list.push_back( subtex );
	subtex = new PovTexture( app_ref, "TEXTURE", sign_texture_child_delete, sign_texture_child_move, this );
	texture_list.push_back( subtex );	
	}

for ( unsigned int i = 0 ; i < texture_list.size() ; i++ )
	texture_list[i]->remove_from_tree();
if ( type->value() > 0 && type->value() != 3 ) 
	{
	for ( unsigned int i = 0 ; i < texture_list.size() ; i++ )
		texture_list[i]->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
	//gtk_ctree_expand( ctree, ctree_node );
	}

if ( bitmap != NULL ) { delete bitmap; bitmap = NULL; }
if ( blockpattern != NULL ) { delete blockpattern; blockpattern = NULL; }
if ( mapedit != NULL ) { mapedit->remove_from_tree(); delete mapedit; mapedit = NULL; }
if ( blendmap != NULL )
	{
	delete blendmap; blendmap = NULL; 
	delete blendmap_mod; blendmap_mod = NULL; 
	}
if ( warp != NULL ) { delete warp; warp = NULL; }
if ( noise != NULL ) { delete noise; noise = NULL; }

set_changing_box();
}


//***************************************************************
// Set changing box
//***************************************************************
void PovTexture::set_changing_box()
{
if ( changing_box1 != NULL ) gtk_widget_destroy( changing_box1 );
changing_box1 = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(vbox), changing_box1, TRUE, TRUE, 0 );
bool tt = true;

switch ( type->value() )
	{
	case 1:
		{
		GtkWidget *button = gtk_button_new_with_label( _("Add sub-Texture") );
		gtk_box_pack_start( GTK_BOX(changing_box1), button, FALSE, TRUE, 0 );
		gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_texture_add), this );
		break;
		}

	case 2:
		{
		if ( blockpattern == NULL ) blockpattern = new TvWidget_blockpattern( N_("Block pattern"), "BLKPAT", NULL, app_ref );
		blockpattern->get_widget( changing_box1, tt );
		blockpattern->connect_signal( sign_blockpat_changed, this );
		if ( texture_list.size() < 2 )
			{
			PovTexture *subtex = new PovTexture( app_ref, "TEXTURE", sign_texture_child_delete, sign_texture_child_move, this );
			texture_list.push_back( subtex );
			subtex = new PovTexture( app_ref, "TEXTURE", sign_texture_child_delete, sign_texture_child_move, this );
			texture_list.push_back( subtex );	
		blockpattern_changed();
			}
		}
		break;

	case 3:
		{
		if ( blendmap == NULL ) blendmap = new TvWidget_blendmap( N_("Map definition"), "BMAP", NULL, app_ref );
		blendmap->get_widget( changing_box1, tt );

		if ( mapedit == NULL ) 
			{
			mapedit = new TvWidget_map_editor( N_("Map edition"), "MAPEDIT", NULL, app_ref, mapedit_feeder_tex, this );
			MapItem_texture *tex = new MapItem_texture( 0.0, 0, app_ref );
			mapedit->add( tex );
			tex = new  MapItem_texture( 1.0, 255, app_ref );
			mapedit->add( tex );		
			mapedit->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
			//gtk_ctree_expand( ctree, ctree_node );
			}
		mapedit->get_widget( changing_box1, tt );

		if ( blendmap_mod == NULL ) blendmap_mod = new TvWidget_blendmap_mod( N_("Modifiers"), "BMAPMOD", NULL, app_ref );
		blendmap_mod->get_widget( changing_box1, tt );
		
		if ( noise == NULL ) noise = new TvWidget_noise_rb( N_("Noise"), "NOISE", NULL, app_ref );
		noise->get_widget_rb( changing_box1, tt );
		if ( warp == NULL ) warp = new TvWidget_warp_rb( N_("Warp"), "WARP", NULL, app_ref );
		warp->get_widget_rb( changing_box1, tt );
		}
		break;

	case 4:
		{
		if ( bitmap == NULL ) bitmap = new TvWidget_bitmap( N_("Bitmap"), "BITMAP", NULL, app_ref );
		bitmap->get_widget( changing_box1, tt );
		GtkWidget *button = gtk_button_new_with_label( N_("Add sub-Texture") );
		gtk_box_pack_start( GTK_BOX(changing_box1), button, FALSE, TRUE, 0 );
		gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_texture_add), this );		
		}
		break;

	default:
		break;
	}

transfo->get_widget_rb( changing_box1, tt );
gtk_widget_show_all( changing_box1 );
}


//***************************************************************
// Blockpattern
//***************************************************************
void PovTexture::blockpattern_changed()
{
blockpattern->flush();

if ( blockpattern->value() == 2 && texture_list.size() < 3 )
	{
	PovTexture *subtex = new PovTexture( app_ref, "TEXTURE", sign_texture_child_delete, sign_texture_child_move, this );
	texture_list.push_back( subtex );
	}
for ( unsigned int i = 0 ; i < texture_list.size() ; i++ )
	{
	texture_list[i]->remove_from_tree();
	}
unsigned int j = ( blockpattern->value() == 2 ) ? 3 : 2;

for ( unsigned int i = 0 ; i < j ; i++ )
	texture_list[i]->add_to_tree( tree_view, tree_store, tree_selection, &node_iter, NULL );
//gtk_ctree_expand( ctree, ctree_node );
}

	


//***************************************************************
// Add to tree
//***************************************************************
void PovTexture::add_to_tree( GtkTreeView *view, GtkTreeStore *store, GtkTreeSelection *selection, GtkTreeIter *parent, GtkTreeIter *sibling  )
{
MaterialItem::add_to_tree( view, store, selection, parent, sibling );
switch ( type->value() )
	{
	case 0:
		finish->add_to_tree( view, store, selection, &node_iter );
		pigment->add_to_tree( view, store, selection, &node_iter );
		normal->add_to_tree( view, store, selection, &node_iter );
		break;

	case 2:
	for ( unsigned int i = 0 ; i < texture_list.size() ; i++ )
			texture_list[i]->clear_tree_node();
		blockpattern_changed();
		break;

	case 3:
		mapedit->add_to_tree( view, store, selection, &node_iter );
		break;

	case 1:
	case 4:
		for ( unsigned int i = 0 ; i < texture_list.size() ; i++ )
			texture_list[i]->add_to_tree( view, store, selection, &node_iter );		
		break;
		
	default:
		break;
	}
set_node_state();
}

void PovTexture::remove_from_tree()
{
finish->remove_from_tree();
pigment->remove_from_tree();
normal->remove_from_tree();
if ( mapedit != NULL ) mapedit->remove_from_tree( );

for ( unsigned int i = 0 ; i < texture_list.size() ; i++ )
	texture_list[i]->remove_from_tree();
MaterialItem::remove_from_tree();
}

void PovTexture::save_node_state()
{
for ( unsigned int i = 0 ; i < texture_list.size() ; i++ )
	texture_list[i]->save_node_state();		
if ( mapedit != NULL ) mapedit->save_node_state();
MaterialItem::save_node_state();
finish->save_node_state();
pigment->save_node_state();
normal->save_node_state();
}

//***************************************************************
// Output to povray
//***************************************************************
void PovTexture::output_to_povray_define_layered( ofstream & file )
{
for ( unsigned int i = 0 ; i < texture_list.size() ; i ++ )
	texture_list[i]->output_to_povray_define_layered( file );
if ( type->value() != 1 || is_mother  ) return;

file << "\n// Sub texture " << get_name();	
file << "\n#declare subtexture_" << texture_num << " = \n";
for ( unsigned int i = 0 ; i < texture_list.size() ; i ++ )
	texture_list[i]->output_to_povray( file, false );
transfo->output_to_povray( file );	
file << "\n// End of sub texture " << get_name();	
}

bool PovTexture::is_layered()
{
if ( type->value() == 1 ) return true;
if ( mother != NULL ) return ( mother->is_layered() );
return false;
}


void PovTexture::output_to_povray( ofstream & file, bool inmap )
{
//if ( !is_used() ) return;

switch ( type->value() )
	{
	case 0:
		if ( !inmap ) file << "\n\ttexture {";
		pigment->output_to_povray( file );
		normal->output_to_povray( file );
		finish->output_to_povray( file );
		transfo->output_to_povray( file );
		if ( !inmap ) file << "\n\t}\n";
		break;

	case 1:
		if ( is_mother )
			{
			for ( unsigned int i = 0 ; i < texture_list.size() ; i ++ )
				texture_list[i]->output_to_povray( file, false );
			transfo->output_to_povray( file );		
			}
		else file << "\n\ttexture { subtexture_" << texture_num << " }";
		break;

	case 2:
		if ( !inmap ) file << "\n\ttexture {";
		blockpattern->output_to_povray( file );
		texture_list[0]->output_to_povray( file ); file << ", ";
		texture_list[1]->output_to_povray( file );
		if ( blockpattern->value() == 2 ) { file << ", "; texture_list[2]->output_to_povray( file ); }
		if ( blockpattern->value() == 0 ) blockpattern->output_to_povray_options( file );
		transfo->output_to_povray( file );		
		if ( !inmap ) file << "\n\t}\n";		
		break;

	case 3:
		if ( !inmap ) file << "\n\ttexture {";
		blendmap->output_to_povray( file );
		if ( blendmap_mod != NULL ) blendmap_mod->output_to_povray( file );
		 file << "\n\t\ttexture_map {";
		mapedit->output_to_povray( file );
		file << "\n\t\t}\n";
		if ( noise != NULL ) noise->output_to_povray( file );	
		if ( warp != NULL ) warp->output_to_povray( file );	
		if ( transfo != NULL ) transfo->output_to_povray( file );				
		if ( !inmap ) file << "\n\t}\n";				
		break;

	case 4:
		if ( !inmap ) file << "\n\ttexture {\n\t\tmaterial_map {\n\t\t";
		bitmap->output_to_povray_nodecl( file );
		for ( unsigned int i = 0 ; i < texture_list.size() ; i ++ )
			texture_list[i]->output_to_povray( file );
		file << "\n\t\t}";
		transfo->output_to_povray( file );		
		if ( !inmap ) file << "\n\t}\n";		
		break;
	}
}

//***********************************************
// Sauvegarde
//***********************************************
void PovTexture::save( ofstream & file )
{
type->flush();
file << "\n\n" << sname << "{\n";
name->save( file ); file << "\n";
expand->save(file);
transfo->save( file ); file << "\n";
type->save( file ); file << "\n";

switch( type->value() )
	{
	case 0:
		{
		pigment->save( file );
		normal->save( file );
		finish->save( file );
		}
		break;

	case 1:
		{
		int size =  texture_list.size();
		for ( int i = 0 ; i < size; i ++ )
			texture_list[i]->save( file );		
		}
		break;

	case 2:
		{
		blockpattern->save( file ); file << "\n";
		texture_list[0]->save( file ); 
		texture_list[1]->save( file ); 
		if ( blockpattern->value() == 2 ) texture_list[2]->save( file );
		}
		break;

	case 3:
		{
		blendmap->save( file );
		blendmap_mod->save( file );
		mapedit->save( file );
		noise->save( file );
		warp->save( file );
		}
		break;

	case 4:
		{
		bitmap->save( file );
		for ( unsigned int i = 0 ; i < texture_list.size() ; i ++ )
			texture_list[i]->save( file );				
		}
	}

file << "\n}";
}

bool PovTexture::load( ifstream & file, char * ltag )
{
if ( strcmp( sname, ltag ) ) return false;
char * tag = NULL;

do
	{
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	if (name->load( file , tag ) ) continue;	
	if (expand->load( file , tag ) ) continue;	
	if (type->load( file , tag ) ) continue;
	if (transfo->load( file , tag ) ) continue;	
	if (pigment->load( file , tag ) ) continue;	
	if (normal->load( file , tag ) ) continue;	
	if (finish->load( file , tag ) ) continue;	
	
	if ( ! strcmp( "TEXTURE", tag ) )
		{
		PovTexture *subtex = new PovTexture( app_ref, "TEXTURE", sign_texture_child_delete, sign_texture_child_move, this );
		subtex->load( file, tag );
		texture_list.push_back( subtex );
		continue;
		}

	if ( !strcmp( tag, "BMAP" ) )
		{
		if ( blendmap == NULL ) blendmap = new TvWidget_blendmap( N_("Pattern"), "BMAP", NULL, app_ref );
		blendmap->load( file, tag );
		continue;
		}
	
	if ( !strcmp( tag, "MAPEDIT" ) )
		{
		if ( mapedit == NULL ) mapedit = new TvWidget_map_editor( N_("Map edition"), "MAPEDIT", NULL, app_ref, mapedit_feeder_tex, this );
		mapedit->load( file, tag );
		continue;
		}
		
	if ( !strcmp( tag, "BMAPMOD" ) )
		{
		if ( blendmap_mod == NULL ) blendmap_mod = new TvWidget_blendmap_mod( N_("Pattern"), "BMAPMOD", NULL, app_ref );
		blendmap_mod->load( file, tag );
		continue;
		}
	
	if ( !strcmp( tag, "NOISE" ) )
		{
		if ( noise == NULL ) noise = new TvWidget_noise_rb( N_("Noise"), "NOISE", NULL, app_ref );
		noise->load( file, tag );
		continue;
		}
		
	if ( !strcmp( tag, "WARP" ) )
		{
		if ( warp == NULL ) warp = new TvWidget_warp_rb( N_("Warp"), "WARP", NULL, app_ref );
		warp->load( file, tag );
		continue;
		}

	if ( !strcmp( tag, "BLKPAT" ) )
		{
		if ( blockpattern == NULL ) blockpattern = new TvWidget_blockpattern( N_("Block Pattern"), "BLKPAT", NULL, app_ref );
		blockpattern->load( file, tag );
		continue;
		}

	if ( !strcmp( tag, "BITMAP" ) )
		{
		if ( bitmap == NULL ) bitmap = new TvWidget_bitmap( N_("Bitmap"), "BITMAP", NULL, app_ref );
		bitmap->load( file, tag );
		continue;
		}
		
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}


//***************************************************************
// Child textures manipulation
//***************************************************************
void PovTexture::add_texture()
{
PovTexture *tex = new PovTexture( app_ref, "TEXTURE", sign_texture_child_delete, sign_texture_child_move, this );
texture_list.push_back( tex );
tex->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
}

void PovTexture::del_texture_child( PovTexture *child )
{
if ( texture_list.size() < 3 ) return;
if ( blockpattern != NULL || mapedit != NULL ) return;
vector<PovTexture*>::iterator it = texture_list.begin();
while ( it != texture_list.end() )
	{
	if ( *it == child )
		{
		//gtk_ctree_select( ctree, ctree_node );
		select_tree_row();
		child->remove_from_tree();
		texture_list.erase( it );
		delete child;		
		break;
		}
	it++;
	}
}

void PovTexture::move_texture_child( PovTexture *child, int sens )
{
if ( texture_list.size() < 2 ) return;
vector<PovTexture*>::iterator it = texture_list.begin();
while ( it != texture_list.end() )
	{
	if ( *it == child )
		{
		if ( sens == 0 )
			{
			if ( it == texture_list.begin() ) break;
			//gtk_ctree_move( ctree, ((PovTexture*)*(it))->get_ctree_node(), ctree_node, ((PovTexture*)*(it-1))->get_ctree_node() );
			texture_list.erase( it );
			texture_list.insert( it-1, child );
			}
		else
			{
			if ( it == texture_list.end() ) break;
			if ( blockpattern != NULL )
				{
				if ( *it == texture_list[1] && blockpattern->value() < 2 ) break;
				if ( *it == texture_list[2] && blockpattern->value() == 2 ) break; 
				}
			//gtk_ctree_move( ctree, ((PovTexture*)*(it+1))->get_ctree_node(), ctree_node, ((PovTexture*)*(it))->get_ctree_node() );			
			texture_list.erase( it++ );
			texture_list.insert( it, child );
			}
		break;
		}
	it++;
	}
}


bool PovTexture::paste( MaterialItem *item )
{
switch( item->get_type() )
	{
	case TV_MITEM_FINISH:
		return finish->paste( item );
		break;
	
	case TV_MITEM_PIGMENT:
		return pigment->paste( item );
		break;
	
	case TV_MITEM_NORMAL:
		return normal->paste( item );
		break;

	case TV_MITEM_TEXTURE:
		{
		// Add to a layered texture
		if ( type->value() == 1 )
			{
			PovTexture *tex = new PovTexture(  *(PovTexture*)item );
			texture_list.push_back( tex );
			tex->set_params( sign_texture_child_delete, sign_texture_child_move, this );
			tex->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );			
			break;
			}
		
		// Replace the current texture
		PovTexture *tex = (PovTexture*)item;
		for ( unsigned int i = 0 ; i < texture_list.size() ; i ++ ) delete texture_list[i];
		texture_list.clear();
       for ( unsigned int i = 0 ; i < tex->texture_list.size() ; i++ )
			{
			PovTexture *it =  new PovTexture( *(tex->texture_list[i]) );
			it->set_params( sign_texture_child_delete, sign_texture_child_move, this );
			texture_list.push_back( it );
			}
		delete pigment;
		delete normal;
		delete finish;
		if ( bitmap != NULL ) { delete bitmap; bitmap = NULL; }
		if ( mapedit != NULL ) { delete mapedit; mapedit = NULL; }
		if ( blendmap != NULL ) { delete blendmap; blendmap = NULL; }
		if ( blendmap_mod != NULL ) { delete blendmap_mod; blendmap_mod = NULL; }
		if ( noise != NULL ) { delete noise; noise = NULL; }
		if ( warp != NULL ) { delete warp; warp = NULL; }
		if ( blockpattern != NULL ) { delete blockpattern; blockpattern = NULL; }			
		if ( GTK_IS_WIDGET(edit_cont)  )
			{
			gtk_widget_destroy( edit_cont );
			type->clear_widget();
			transfo->clear_widget();
			name->clear_widget();
			}
			
		type->copy( tex->type  );
		transfo->copy( tex->transfo );
		pigment = new PovTexPigment( *tex->pigment );
		normal = new PovTexNormal( *tex->normal );
		finish = new PovTexFinish( *tex->finish );
		if ( tex->bitmap != NULL ) bitmap = new TvWidget_bitmap( *tex->bitmap );				
		if ( tex->mapedit != NULL ) mapedit = new TvWidget_map_editor( *tex->mapedit );
		if ( tex->blendmap != NULL ) blendmap = new TvWidget_blendmap( *tex->blendmap );
		if ( tex->blendmap_mod != NULL ) blendmap_mod = new TvWidget_blendmap_mod( *tex->blendmap_mod );
		if ( tex->noise != NULL ) noise = new TvWidget_noise_rb( *tex->noise );
		if ( tex->warp != NULL ) warp = new TvWidget_warp_rb( *tex->warp );
		if ( tex->blockpattern != NULL ) blockpattern = new TvWidget_blockpattern( *tex->blockpattern );

 //TEXLIST_DEF
GtkTreeIter *old_node = gtk_tree_iter_copy( &node_iter );
add_to_tree( tree_view, tree_store, tree_selection, tree_node_parent, old_node );
gtk_tree_store_remove( tree_store, old_node );
select_tree_row();
gtk_tree_iter_free( old_node );

		}
		break;
					
	default:
		return false;
	}
return true;
}



//***********************************************
//
// MapItem Texture
// 
//***********************************************
int MapItem_texture::obj_count = 1;

MapItem_texture::MapItem_texture( float val, guint8 c, app_objs *appref ) : MapItem( N_("Texture"), val )
{
color[0]=color[1]=color[2]=c; color[3]=255; 
texture = new PovTexture( appref, "TEXTURE", NULL, NULL, NULL );
char text[25];
sprintf( text, "Map texture #%u", obj_count++ );
texture->set_name( text );
}

MapItem_texture::MapItem_texture( float val, guint8 c, PovTexture *tex ) : MapItem( N_("Texture"), val )
{
color[0]=color[1]=color[2]=c; color[3]=255; 
texture = tex;
}

MapItem_texture::MapItem_texture( MapItem_texture & res ) : MapItem( res )
{
for ( int i = 0 ; i < 4 ; i++ ) color[i] = res.color[i];
texture = new PovTexture( *res.texture );
texture->set_name( res.texture->get_name() );
}


void MapItem_texture::save( ofstream & file )
{
MapItem::save( file );
texture->save( file );
file << "} ";
}

bool MapItem_texture::load( ifstream & file, char *ltag )
{
if ( strcmp( "MAPITEM", ltag ) ) return false;
char * tag = NULL;

do
	{
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	if ( texture->load( file , tag ) ) continue;
	if ( MapItem::load( file , tag ) ) continue;
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}
