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

//**************************************
// Définitions
//**************************************
const int normal_type_num = 4;
const char *normal_type_list[normal_type_num] = {
	N_("Plain"),
	N_("Normal list"),
	N_("Normal map"),
	N_("Bump map")
	};
	



//**************************************
// Constructeurs & Destructeurs
//**************************************
PovTexNormal::PovTexNormal( app_objs *appref, char *tagname, bool mother  ) : MaterialItem( app_ref, tagname )
{
set_name( _("Normal") );
item_type = TV_MITEM_NORMAL;
is_mother = mother;
changing_box1 = changing_box2 = NULL;
normal1 = normal2 = normal3 = NULL;
pattern = NULL;
blendmod = NULL;
blockpattern = NULL;
mapedit = NULL;
bumpmap =  new TvWidget_bumpmap( N_("Bump map"), "BUMPMAP", NULL, app_ref );
use = new TvWidget_bool_activator( N_("Use normal"), "USED", NULL, app_ref, !is_mother );
type = new TvWidget_option_combo( N_("Type"), "TYPE", NULL, app_ref );
type->set_list( normal_type_list, normal_type_num, 0 );
bump_depth = new TvWidget_float( N_("Bump_depth"), "BUMP", NULL, app_ref, 0.5 );
bump_depth->set_range( 100, -100, 0.1, 3 );
no_bump_scale = new TvWidget_bool( N_("No bump scale"), "NOSCALE", NULL, app_ref, false );
transform = new TvWidget_transformation_rb( _("Transformation"), "TRANSFO", NULL, app_ref );
noise = new TvWidget_noise_rb( N_("Noise"), "NOISE", NULL, app_ref );
warp = new TvWidget_warp_rb( N_("Warp"), "WARP", NULL, app_ref );
slopemap = new TvWidget_slopemap_rb( N_("Slope map"), "SMAP", NULL, app_ref );
accuracy = new TvWidget_float( N_("Accuracy"), "ACCUR", NULL, app_ref, 0.02 );
accuracy->set_range( 100, 0, 0.01, 3 );
}

PovTexNormal::PovTexNormal( PovTexNormal & ref ) : MaterialItem( ref )
{
if ( &ref == NULL ) return;
set_name( ref.get_name() );
item_type = TV_MITEM_NORMAL;
is_mother = true;
changing_box1 = changing_box2 = NULL;

use = new TvWidget_bool_activator( *ref.use );
type = new TvWidget_option_combo( *ref.type );
bump_depth = new TvWidget_float( *ref.bump_depth );
no_bump_scale = new TvWidget_bool( *ref.no_bump_scale );
transform = new TvWidget_transformation_rb( *ref.transform );
noise = new TvWidget_noise_rb( *ref.noise );
warp = new TvWidget_warp_rb( *ref.warp );
slopemap = new TvWidget_slopemap_rb( *ref.slopemap );
accuracy = new TvWidget_float( *ref.accuracy );
bumpmap = new TvWidget_bumpmap( *ref.bumpmap ); 

if ( ref.pattern != NULL ) pattern = new TvWidget_blendmap( *ref.pattern ); else pattern = NULL;
if ( ref.mapedit != NULL ) mapedit = new TvWidget_map_editor( *ref.mapedit ); else mapedit = NULL;
if ( ref.blendmod != NULL ) blendmod = new TvWidget_blendmap_mod( *ref.blendmod ); else blendmod = NULL;
if ( ref.blockpattern != NULL ) blockpattern = new TvWidget_blockpattern( *ref.blockpattern ); else blockpattern = NULL;
if ( ref.normal1 != NULL ) normal1 = new PovTexNormal( *ref.normal1 ); else normal1 = NULL;
if ( ref.normal2 != NULL ) normal2 = new PovTexNormal( *ref.normal2 ); else normal2 = NULL;
if ( ref.normal3 != NULL ) normal3 = new PovTexNormal( *ref.normal3 ); else normal3 = NULL;
}


PovTexNormal::~PovTexNormal()
{
delete use;
delete type;
delete bump_depth;
delete no_bump_scale;
delete transform;
delete noise;
delete warp;
delete slopemap;
delete accuracy;
delete bumpmap;
if ( pattern != NULL ) delete pattern;
if ( blockpattern != NULL ) delete blockpattern;
if ( mapedit != NULL ) delete mapedit;
if ( blendmod != NULL ) delete blendmod;
if ( normal1 != NULL )
	{
	delete normal1;
	delete normal2;
	delete normal3;
	}
}



//**************************************
// Editeur
//**************************************
void PovTexNormal::edit_widget( GtkWidget *box )
{
PREF_DEF
bool tt = pref->tooltips->value();
MaterialItem::edit_widget( box, _("Normal") );
activated_box = gtk_vbox_new( FALSE, 0 );
use->set_target( activated_box );
if ( is_mother ) use->get_widget_no_toggle( edit_cont, tt );
gtk_box_pack_start( GTK_BOX(edit_cont), activated_box, TRUE, TRUE, 0 );
	type->get_widget( activated_box, tt );
	type->connect_signal( GTK_SIGNAL_FUNC(sign_normal_type_changed), this );
	set_changing_box();

gtk_widget_show_all( widget );
if ( is_mother ) use->update_widget();
}


//--------------------
// Add to tree
//--------------------
void PovTexNormal::add_to_tree( GtkTreeView *view, GtkTreeStore *store, GtkTreeSelection *selection, GtkTreeIter *parent, GtkTreeIter *sibling )
{
MaterialItem::add_to_tree( view, store, selection, parent, sibling );
if ( normal1 != NULL )
	{
	normal1->add_to_tree( view, store, selection, &node_iter );
	normal2->add_to_tree( view, store, selection, &node_iter );
	if ( blockpattern->value() == 2 ) 
		normal3->add_to_tree( view, store, selection, &node_iter );
	}
if ( mapedit != NULL ) mapedit->add_to_tree( view, store, selection, &node_iter );
set_node_state();
}

void PovTexNormal::remove_from_tree()
{
if ( normal1 != NULL ) normal1->remove_from_tree();
if ( normal2 != NULL ) normal2->remove_from_tree();
if ( normal3 != NULL ) normal3->remove_from_tree();
if ( mapedit != NULL ) mapedit->remove_from_tree();
MaterialItem::remove_from_tree();	
}

void PovTexNormal::save_node_state()
{
if ( normal1 != NULL ) normal1->save_node_state();
if ( normal2 != NULL ) normal2->save_node_state();
if ( normal3 != NULL ) normal3->save_node_state();
if ( mapedit != NULL ) mapedit->save_node_state();
}

//--------------------
// Type changed
//--------------------
void PovTexNormal::type_changed()
{
type->flush();

if ( normal1 != NULL && type->value() != 1 ) 
	{
	normal1->remove_from_tree();
	normal2->remove_from_tree();
	normal3->remove_from_tree();
	delete normal1; normal1 = NULL;
	delete normal2;
	delete normal3;
	}

if ( normal1 == NULL && type->value() == 1  )
	{
	normal1 = new PovTexNormal( app_ref, false );
	normal2 = new PovTexNormal( app_ref, false );
	normal3 = new PovTexNormal( app_ref, false );
	normal1->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
	normal2->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
	}

if ( pattern != NULL )
	{
	delete pattern;
	pattern = NULL;
	}
	
if ( blockpattern != NULL )
	{
	delete blockpattern;
	blockpattern = NULL;
	}

if ( mapedit != NULL )
	{
	mapedit->remove_from_tree();
	delete mapedit;
	mapedit = NULL;
	delete blendmod;
	blendmod = NULL;
	}

set_changing_box();
}


//--------------------
// Set changing box
//--------------------
void PovTexNormal::set_changing_box()
{
PREF_DEF
bool tt = pref->tooltips->value();

// Changing box 1
if ( changing_box1 != NULL ) gtk_widget_destroy( changing_box1 );
	bump_depth->clear_widget();
	no_bump_scale->clear_widget();
changing_box1 = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(activated_box), changing_box1, FALSE, FALSE, 0 );
GtkWidget *table = new_table_no_frame( changing_box1, 3 );
	bump_depth->get_widget( table, tt, 1 );
	no_bump_scale->get_widget( table, tt, 2 );
	accuracy->get_widget( table, tt, 3 );

switch ( type->value() )
	{
	case 0:
		if ( pattern == NULL ) pattern = new TvWidget_blendmap( N_("Pattern"), "BMAP", NULL, app_ref, true );
		pattern->get_widget( changing_box1, tt );
		break;

	case 1:
		{
		if ( blockpattern == NULL ) blockpattern = new TvWidget_blockpattern( N_("Block Pattern"), "BLKPAT", NULL, app_ref );
		blockpattern->get_widget( changing_box1, tt );
		blockpattern->connect_signal( sign_normblocktype_changed, this );
		set_normblock_items();
		}
		break;

	case 2:
		{
		if ( pattern == NULL ) pattern = new TvWidget_blendmap( N_("Map definition"), "BMAP", NULL, app_ref );		
		pattern->get_widget( changing_box1, tt );
		if ( mapedit == NULL ) 
			{
			mapedit = new TvWidget_map_editor( N_("Map editor"), "MAPEDIT", NULL, app_ref, mapedit_feeder_normal, this );
			MapItem_normal *norm = new MapItem_normal( 0.0, 0, app_ref );
			mapedit->add( norm );
			norm = new  MapItem_normal( 1.0, 255, app_ref );
			mapedit->add( norm );		
			mapedit->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
			}
		mapedit->get_widget( changing_box1, tt );
		}
		break;
		
	case 3:
		{
	//	if ( bumpmap == NULL ) bumpmap = new TvWidget_bumpmap( N_("Bump map"), "BUMPMAP", NULL, app_ref );
		bumpmap->get_widget( changing_box1, tt );
		}
		break;
	}

// Changing box 2
if ( changing_box2 != NULL ) gtk_widget_destroy( changing_box2 );
changing_box2 = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(activated_box), changing_box2, FALSE, FALSE, 0 );
transform->get_widget_rb( changing_box2, tt );
noise->get_widget_rb( changing_box2, tt );
warp->get_widget_rb( changing_box2, tt );
if ( type->value() == 0 || type->value() == 2 ) slopemap->get_widget_rb( changing_box2, tt );
if ( type->value() == 2 )
	{
	if ( blendmod == NULL ) blendmod = new TvWidget_blendmap_mod( N_("Modifiers"), "BMAPMOD", NULL, app_ref );
	blendmod->get_widget( changing_box2, tt );
	}

gtk_widget_show_all( changing_box1 );
gtk_widget_show_all( changing_box2 );
}


void PovTexNormal::destroy_widget()
{
MaterialItem::destroy_widget();
	use->clear_widget();
	type->clear_widget();
	bump_depth->clear_widget();
	no_bump_scale->clear_widget();
	transform->clear_widget();
	noise->clear_widget();
	warp->clear_widget();
	slopemap->clear_widget();
	accuracy->clear_widget();
	if ( pattern != NULL ) pattern->clear_widget();
	if ( blockpattern != NULL ) blockpattern->clear_widget();	
	if ( mapedit != NULL )  mapedit->clear_widget();
	bumpmap->clear_widget();
	if ( blendmod != NULL ) blendmod->clear_widget();
changing_box1 = changing_box2 = NULL;
}

void PovTexNormal::flush()
{
use->flush();
type->flush();
bump_depth->flush();
no_bump_scale->flush();
transform->flush();
noise->flush();
warp->flush();
accuracy->flush();
slopemap->flush();
if ( pattern != NULL ) pattern->flush();
if ( blockpattern != NULL ) blockpattern->flush();
if ( mapedit != NULL )  mapedit->flush();
bumpmap->flush();
if ( blendmod != NULL ) blendmod->flush();
}


void PovTexNormal::set_normblock_items()
{
if ( normal3 == NULL ) return;
blockpattern->flush();
if ( blockpattern->value() == 2 )
		normal3->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
else
		normal3->remove_from_tree();
}


//**************************************
// Output / Export
//**************************************
void PovTexNormal::output_to_povray( ofstream & file, bool in_map )
{
if ( !use->value() ) return;
if ( !in_map ) file << "\n\t\tnormal {\n\t\t\t";

switch( type->value() )
	{
	case 0:
		{
		pattern->output_to_povray( file );
		int pty = pattern->get_type();
		if ( pty != 3 && pty != 6 && pty != 16 && pty != 20 && pty != 22 )
			slopemap->output_to_povray( file );
		}
		break;

	case 1:
		{
		blockpattern->output_to_povray( file );
		file << "\n\t\t\t\t";
		normal1->output_to_povray( file );
		file << ",\n\t\t\t\t";
		normal2->output_to_povray( file );		
		if ( blockpattern->value() == 2 )
			{
			file << ",\n\t\t\t\t";
			normal3->output_to_povray( file );
			}
		blockpattern->output_to_povray_options( file );				
		}
		break;

	case 2:
		{
		pattern->output_to_povray( file );
		file << "\n\t\t\tnormal_map {";
		mapedit->output_to_povray( file );
		file << "\n\t\t\t}\n\t\t\t";
		if ( blendmod != NULL ) blendmod->output_to_povray( file );
		}
		break;

	case 3:
		bumpmap->output_to_povray( file );
		break;
	}

file << "\n\t\t\tbump_size ";
bump_depth->output_to_povray( file );
file << "\n\t\t\taccuracy ";
accuracy->output_to_povray( file );
if ( no_bump_scale->value() ) file << "\n\t\tno_bump_scale";
file << "\n\t\t\t";
noise->output_to_povray( file );
file << "\n\t\t\t";
warp->output_to_povray( file );
file << "\n\t\t\t";
slopemap->output_to_povray( file );
file << "\n\t\t\t";
transform->output_to_povray( file );
if ( !in_map ) file << "\n\t\t}";
}


//**************************************
// SAve
//**************************************
void PovTexNormal::save( ofstream & file )
{
if ( !use->value() ) return;
file << "NORMAL{\n";
use->save( file );
type->save( file );
name->save( file );
expand->save( file );

switch( type->value() )
	{
	case 0:
		pattern->save( file );
		if ( slopemap != NULL ) slopemap->save( file );
		break;

	case 1:
		{
		blockpattern->save( file );
		normal1->save( file );
		normal2->save( file );		
		if ( blockpattern->value() == 2 )
			normal3->save( file );
		}
		break;

	case 2:
		{
		pattern->save( file );
		mapedit->save( file );
		if ( blendmod != NULL ) blendmod->save( file );
		if ( slopemap != NULL ) slopemap->save( file);
		}
		break;

	case 3:
		bumpmap->save( file );
		break;
	}
bump_depth->save( file );
no_bump_scale->save( file );
accuracy->save( file );
noise->save( file );
warp->save( file );
slopemap->save( file );
transform->save( file );
file << "}\n";

}


bool PovTexNormal::load( ifstream & file, char * ltag )
{
if ( strcmp( "NORMAL", ltag ) ) return false;
char * tag = NULL;
use->set( true );
do
	{
	tag = tvio_get_next_tag( file );

	if ( tag == NULL ) break;
	if (type->load( file , tag ) ) continue;	
	if (bump_depth->load( file , tag ) ) continue;	
	if (no_bump_scale->load( file , tag ) ) continue;	
	if (accuracy->load( file , tag ) ) continue;	
	if (noise->load( file , tag ) ) continue;	
	if (warp->load( file , tag ) ) continue;	
	if (slopemap->load( file , tag ) ) continue;	

	if (transform->load( file , tag ) ) continue;
	if (name->load( file , tag ) ) continue;
	if (expand->load( file , tag ) ) continue;
	if (bumpmap->load( file , tag ) ) continue;
	if (use->load( file , tag ) ) continue;

	if ( !strcmp( tag, "BMAP" ) )
		{
		if ( pattern == NULL ) pattern = new TvWidget_blendmap( _("Pattern"), "BMAP", NULL, app_ref );
		pattern->load( file, tag );
		continue;
		}
		
	if ( !strcmp( tag, "BMAPMOD" ) )
		{
		if ( blendmod == NULL ) blendmod = new TvWidget_blendmap_mod( _("Pattern"), "BMAPMOD", NULL, app_ref );
		blendmod->load( file, tag );
		continue;
		}

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

	if ( !strcmp( tag, "NORMAL" ) )
		{
		if ( normal1 == NULL ) { normal1 = new PovTexNormal( app_ref, false ); normal1->load( file, tag ); continue; }
		if ( normal2 == NULL ) { normal2 = new PovTexNormal( app_ref, false ); normal2->load( file, tag ); continue; }
		if ( normal3 == NULL ) { normal3 = new PovTexNormal( app_ref, false ); normal3->load( file, tag ); continue; }
		tvio_skip_section( file );
		}

	if ( !strcmp( tag, "MAPEDIT" ) )
		{
		mapedit = new TvWidget_map_editor( _("Map editor"), "MAPEDIT", NULL, app_ref, mapedit_feeder_normal, this );
		mapedit->load( file, tag );
		continue;
		}


	tvio_skip_section( file );
	}
while( tag != NULL );
return true;

}


bool PovTexNormal::paste( MaterialItem *item )
{
	if ( item->get_type() != TV_MITEM_NORMAL ) 
		return false;
	set_name( item->get_name() );

	PovTexNormal *norm = (PovTexNormal*)item;
	if ( normal1 != NULL ) { delete normal1; normal1 = NULL; }
	if ( normal2 != NULL ) { delete normal2; normal2 = NULL; }
	if ( normal3 != NULL ) { delete normal3; normal3 = NULL; }
	if ( pattern != NULL ) { delete pattern; pattern = NULL; }
	if ( blendmod != NULL ) { delete blendmod; blendmod = NULL; }
	if ( blockpattern != NULL ) { delete blockpattern; blockpattern = NULL; }
	if ( mapedit != NULL ) { delete mapedit; mapedit = NULL; }

	if ( GTK_IS_WIDGET(widget) ) {
		gtk_widget_destroy( edit_cont );
		use->clear_widget();
		type->clear_widget();
		transform->clear_widget();
		noise->clear_widget();
		warp->clear_widget();
		slopemap->clear_widget();
		bump_depth->clear_widget();
	}
	use->copy( norm->use );
	type->copy( norm->type );
	transform->copy( norm->transform );
	noise->copy( norm->noise );
	warp->copy( norm->warp );
	slopemap->copy( norm->slopemap );
	bump_depth->copy( norm->bump_depth );
	no_bump_scale->copy( norm->no_bump_scale );
	accuracy->copy( norm->no_bump_scale );

	if ( norm->normal1 != NULL ) normal1 = new PovTexNormal( *norm->normal1 ); else normal1 = NULL;
	if ( norm->normal2 != NULL ) normal2 = new PovTexNormal( *norm->normal2 ); else normal2 = NULL;
	if ( norm->normal3 != NULL ) normal3 = new PovTexNormal( *norm->normal3 ); else normal3 = NULL;
	if ( norm->pattern != NULL ) pattern = new TvWidget_blendmap( *norm->pattern );
	if ( norm->blendmod != NULL ) blendmod = new TvWidget_blendmap_mod( *norm->blendmod );
	if ( norm->blockpattern != NULL ) blockpattern = new TvWidget_blockpattern( *norm->blockpattern );
	if ( norm->mapedit != NULL ) mapedit = new TvWidget_map_editor( *norm->mapedit );

	//TEXLIST_DEF
	GtkTreeIter *old_node = gtk_tree_iter_copy( &(norm->node_iter) );
	add_to_tree( tree_view, tree_store, tree_selection, tree_node_parent, old_node );
	gtk_tree_selection_unselect_iter( tree_selection, &node_iter );
	select_tree_row();
	gtk_tree_iter_free( old_node );
	return true;
}


//**************************************
// Normal map
//**************************************
int MapItem_normal::obj_count = 1;

MapItem *mapedit_feeder_normal( gpointer data )
{ return (MapItem*)( new MapItem_normal( 0, 0, ((PovTexNormal*)data)->app_ref ) ); }

MapItem_normal::MapItem_normal( float val, guint8 c, app_objs *appref ) : MapItem( _("Normal"), val ) 
{
color[0]=color[1]=color[2]=c; color[3]=255; 
normal = new PovTexNormal( appref, false );
char text[25];
sprintf( text, "Map normal #%u", obj_count++ );
normal->set_name( text );
}

MapItem_normal::MapItem_normal( MapItem_normal & ref  ) : MapItem( ref )
{
for ( int i = 0 ; i < 3 ; i ++ ) color[i] = ref.color[i];
color[3]=255;
normal = new PovTexNormal( *ref.normal );
normal->set_name( ref.normal->get_name() );
}

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

bool MapItem_normal::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 ( normal->load( file , tag ) ) continue;
	if ( MapItem::load( file , tag ) ) continue;
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}
