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

//**************************************
// Définitions
//**************************************
const int dens_type_num = 4;
const char *dens_type_list[dens_type_num] = { 
	N_("Pattern"),
	N_("Density List"),
	N_("Color map"),
	N_("Density map") };

MapItem *mapedit_density_feeder_col( gpointer data )
{ return (MapItem*)( new MapItem_color( 0, 0, ((PovTexDensity*)data)->app_ref ) ); }

MapItem *mapedit_feeder_dens( gpointer data )
{ return (MapItem*)( new MapItem_density( 0, 0, ((PovTexDensity*)data)->app_ref ) ); }

int PovTexDensity::count=0;


//**************************************
// Constructeur
//**************************************
PovTexDensity::PovTexDensity( app_objs *appref, char *tagname, void (*asuicidal_func)(gpointer, gpointer), gpointer amother, bool is_top ) : MaterialItem( app_ref, tagname )
{
num = ++count;
char text[25];
sprintf( text, "Density %u", num );
set_name( text );
item_type = TV_MITEM_DENSITY;
suicidal_func = asuicidal_func;
mother = amother;
is_mother = is_top;
changing_box = NULL;
changing_box2 = NULL;
blockdens1 = blockdens2 = blockdens3 = NULL;
blendmap = new TvWidget_blendmap( N_("Pattern"), "BMAP", NULL, app_ref );
blendmap_mod = NULL;
blockpattern = NULL;
map_editor = NULL;

type = new TvWidget_option_combo( N_("Type"), "TYPE", NULL, app_ref );
type->set_list( dens_type_list, dens_type_num, 0 );
transformation = new TvWidget_transformation_rb( N_("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 );
}

void PovTexDensity::set_params( void (*asuicidal_func)(gpointer, gpointer), gpointer amother, bool is_top )
{
suicidal_func = asuicidal_func;
mother = amother;
is_mother = is_top;
}


PovTexDensity::PovTexDensity( PovTexDensity & res ) : MaterialItem( res )
{
num = ++count;
//char text[25];
//sprintf( text, "Density %u", num );
//set_name( text );
set_name( res.name->value() );
item_type = TV_MITEM_DENSITY;
//suicidal_func = *res.suicidal_func;
//mother = *res.mother;
//is_mother = *res.is_mother;
is_mother = false;
changing_box = NULL;
changing_box2 = NULL;
if ( res.blockdens1 != NULL ) blockdens1 = new PovTexDensity( *res.blockdens1 ); else blockdens1 = NULL;
if ( res.blockdens2 != NULL ) blockdens2 = new PovTexDensity( *res.blockdens2 ); else blockdens2 = NULL;
if ( res.blockdens3 != NULL ) blockdens3 = new PovTexDensity( *res.blockdens3 ); else blockdens3 = NULL;
if ( res.blendmap != NULL ) blendmap = new TvWidget_blendmap( *res.blendmap ); else blendmap = NULL;
if ( res.blendmap_mod != NULL ) blendmap_mod = new TvWidget_blendmap_mod( *res.blendmap_mod ); else blendmap_mod = NULL;
if ( res.blockpattern != NULL ) blockpattern = new TvWidget_blockpattern( *res.blockpattern ); else blockpattern = NULL;
if ( res.map_editor != NULL ) map_editor = new TvWidget_map_editor( *res.map_editor ); else map_editor = NULL;

type = new TvWidget_option_combo( *res.type );
transformation = new TvWidget_transformation_rb( *res.transformation );
noise = new TvWidget_noise_rb( *res.noise );
warp = new TvWidget_warp_rb( *res.warp );
}


PovTexDensity::~PovTexDensity()
{
delete type;
delete transformation;
delete noise;
delete warp;
if ( blockdens1 != NULL ) delete blockdens1;
if ( blockdens2 != NULL ) delete blockdens2;
if ( blockdens3 != NULL ) delete blockdens3;
if ( blendmap != NULL ) { delete blendmap; delete blendmap_mod; }
if ( blockpattern != NULL ) delete blockpattern;
if ( map_editor != NULL ) delete map_editor;
}		

// Add to tree
void PovTexDensity::add_to_tree( GtkTreeView *view, GtkTreeStore *store, GtkTreeSelection *selection, GtkTreeIter *parent, GtkTreeIter *sibling  )
{
MaterialItem::add_to_tree( view, store, selection, parent, sibling );
if ( blockdens1 != NULL )
	{
	//cout << "\nAdding block densities for " << name->value(); cout.flush();
	blockdens1->add_to_tree( view, store, selection, &node_iter );
	blockdens2->add_to_tree( view, store, selection, &node_iter );
	if ( blockpattern->value() == 1 ) 
		blockdens3->add_to_tree( view, store, selection, &node_iter );
	}
else { /*cout << " \nNo block density for " << name->value(); cout.flush();*/ }
if ( map_editor != NULL ) map_editor->add_to_tree( view, store, selection, &node_iter );
set_node_state();
}

void PovTexDensity::remove_from_tree()
{
if ( blockdens1 != NULL ) blockdens1->remove_from_tree();
if ( blockdens2 != NULL ) blockdens2->remove_from_tree();
if ( blockdens3 != NULL ) blockdens3->remove_from_tree();
MaterialItem::remove_from_tree();
}

void PovTexDensity::save_node_state()
{
MaterialItem::save_node_state();
if ( blockdens1 != NULL ) blockdens1->save_node_state();
if ( blockdens2 != NULL ) blockdens2->save_node_state();
if ( blockdens3 != NULL ) blockdens3->save_node_state();
if ( map_editor != NULL ) map_editor->save_node_state();
}


// Edit widget
void PovTexDensity::edit_widget( GtkWidget *box )
{
PREF_DEF
bool tt = pref->tooltips->value();
MaterialItem::edit_widget( box, _("Density") );

vbox = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(edit_cont), vbox, TRUE, TRUE, 0 );

type->get_widget( vbox, tt );
type->connect_signal( GTK_SIGNAL_FUNC(sign_density_type_changed), this );
if ( is_mother )
	{
	GtkWidget *del_button = gtk_button_new_with_label( _("Delete this density") );
	gtk_box_pack_start( GTK_BOX(vbox), del_button, FALSE, TRUE, 2 );
	gtk_signal_connect( GTK_OBJECT(del_button), "clicked", GTK_SIGNAL_FUNC(sign_density_delete), this );
	}

set_changing_box();
gtk_widget_show_all( widget );
}


void PovTexDensity::type_changed()
{
type->flush();
if ( type->value() != 2 )
	{
	if ( blockdens1 != NULL ) { blockdens1->remove_from_tree(); delete blockdens1; }
	if ( blockdens2 != NULL ) { blockdens2->remove_from_tree(); delete blockdens2; }
	if ( blockdens3 != NULL ) { blockdens3->remove_from_tree(); delete blockdens3; }
	blockdens1 = blockdens2 = blockdens3 = NULL;
	}

if ( blockdens1 == NULL && type->value() == 1 )
	{
	blockdens1 = new PovTexDensity( app_ref, NULL, NULL, false );
	blockdens2 = new PovTexDensity( app_ref, NULL, NULL, false  );
	blockdens3 = new PovTexDensity( app_ref, NULL, NULL, false  );
	blockdens1->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
	blockdens2->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
	}
if ( blockpattern != NULL  )
	{
	delete blockpattern;
	blockpattern = NULL;
	}
if ( blendmap != NULL  )
	{
	delete blendmap;
	blendmap = NULL;
	}
if ( blendmap_mod != NULL && type->value() != 2 && type->value() != 3 )
	{
	delete blendmap_mod;
	blendmap_mod = NULL;
	}
if ( map_editor != NULL )
	{
	map_editor->remove_from_tree();
	delete map_editor;
	map_editor = NULL;
	}
set_changing_box();

}


void PovTexDensity::set_changing_box()
{
bool tt = true;

// Changing box 1
if ( changing_box != NULL ) gtk_widget_destroy( changing_box );
changing_box  = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(vbox), changing_box, FALSE, FALSE, 0 );

switch ( type->value() )
	{
	case 0:
		if ( blendmap == NULL ) blendmap = new TvWidget_blendmap( _("Pattern"), "BMAP", NULL, app_ref );
		blendmap->get_widget( changing_box, tt );
		break;

	case 1:
		{
		if ( blockpattern == NULL ) blockpattern = new TvWidget_blockpattern( _("Block Pattern"), "BLKPAT", NULL, app_ref );
		blockpattern->get_widget( changing_box, tt );
		blockpattern->connect_signal( sign_density_densblocktype_changed, this );
		set_densblock_items();
		}
		break;

	case 2:
		{
		if ( blendmap == NULL ) blendmap = new TvWidget_blendmap( _("Map definition"), "BMAP", NULL, app_ref, true );
		blendmap->get_widget( changing_box, tt );
		if ( map_editor == NULL ) 
			{
			map_editor = new TvWidget_map_editor( _("Map editor"), "MAPEDIT", NULL, app_ref, mapedit_density_feeder_col, this );
			MapItem_color *col = new MapItem_color( 0.0, 0, app_ref );
			map_editor->add( col );
			col = new  MapItem_color( 1.0, 255, app_ref );
			map_editor->add( col );		
			}
		map_editor->get_widget( changing_box, tt );
		}
		break;

	case 3:
		{
		if ( blendmap == NULL ) blendmap = new TvWidget_blendmap( _("Map definition"), "BMAP", NULL, app_ref );		
		blendmap->get_widget( changing_box, tt );
		if ( map_editor == NULL ) 
			{
			map_editor = new TvWidget_map_editor( _("Map editor"), "MAPEDIT", NULL, app_ref, mapedit_feeder_dens, this );
			MapItem_density *dens = new MapItem_density( 0.0, 0, app_ref );
			map_editor->add( dens );
			dens = new  MapItem_density( 1.0, 255, app_ref );
			map_editor->add( dens );		
			map_editor->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
			}
		map_editor->get_widget( changing_box, 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(vbox), changing_box2, FALSE, FALSE, 0 );
if ( type->value() == 2 || type->value() == 3 )
	{ 
	if ( blendmap_mod == NULL ) blendmap_mod = new TvWidget_blendmap_mod( _("Map modifiers"), "BMAPMOD", NULL, app_ref );
	blendmap_mod->get_widget( changing_box2, tt );
	}
transformation->get_widget_rb( changing_box2, tt );
noise->get_widget_rb( changing_box2, tt );
warp->get_widget_rb( changing_box2, tt );

gtk_widget_show_all( changing_box );
gtk_widget_show_all( changing_box2 );
}


void PovTexDensity::set_densblock_items()
{
if ( blockdens3 == NULL ) return;
blockpattern->flush();
if ( blockpattern->value() == 2 )
		blockdens3->add_to_tree( tree_view, tree_store, tree_selection, &node_iter );
else
		blockdens3->remove_from_tree();

}

void PovTexDensity::destroy_widget()
{
MaterialItem::destroy_widget();
changing_box = NULL;
changing_box2 = NULL;
type->clear_widget();
transformation->clear_widget();
noise->clear_widget();
warp->clear_widget();
if ( blendmap != NULL ) blendmap->clear_widget();
if ( blendmap_mod != NULL )  blendmap_mod->clear_widget();
if ( blockpattern != NULL ) blockpattern->clear_widget();
if ( map_editor != NULL ) map_editor->clear_widget();
}


void PovTexDensity::flush()
{
type->flush();
transformation->flush();
noise->flush();
warp->flush();
if ( blendmap != NULL ) blendmap->flush();
if ( blendmap_mod != NULL )  blendmap_mod->flush();
if ( blockpattern != NULL ) blockpattern->flush();
if ( map_editor != NULL ) map_editor->flush();
}

void PovTexDensity::output_to_povray( ofstream & file, bool inmap  )
{
if ( !inmap ) file << "\n\t\t\tdensity {\n\t\t\t\t";

switch ( type->value() )
	{
	case 0:
		blendmap->output_to_povray( file );
		break;
		
	case 1:
		blockpattern->output_to_povray( file );
		file << "\n\t\t\t\t";
		blockdens1->output_to_povray( file );
		file << ",\n\t\t\t\t";
		blockdens2->output_to_povray( file );		
		if ( blockpattern->value() == 2 )
			{
			file << ",\n\t\t\t\t";
			blockdens3->output_to_povray( file );
			}
		blockpattern->output_to_povray_options( file );				
		break;

	case 2:
		blendmap->output_to_povray( file );
		file << "\n\t\t\tcolor_map {";
		map_editor->output_to_povray( file );
		file << "\n\t\t\t}\n\t\t\t";
		if ( blendmap_mod != NULL ) blendmap_mod->output_to_povray( file );
		break;

	case 3:
		blendmap->output_to_povray( file );
		file << "\n\t\t\tdensity_map {";
		map_editor->output_to_povray( file );
		file << "\n\t\t\t}\n\t\t\t";
		if ( blendmap_mod != NULL ) blendmap_mod->output_to_povray( file );
		break;
	
	}

file << "\n\t\t\t\t";
noise->output_to_povray( file );
warp->output_to_povray( file );
transformation->output_to_povray( file );
if ( !inmap ) file << "\n\t\t\t}";
}


void PovTexDensity::save( ofstream & file )
{
file << "DENSITY{ ";
name->save( file );
expand->save( file );
type->save( file );

switch ( type->value() )
	{
	case 0:
		blendmap->save( file );
		break;
		
	case 1:
		blockpattern->save( file );
		blockdens1->save( file );
		blockdens2->save( file );		
		if ( blockpattern->value() == 2 )
			blockdens3->save( file );
		break;

	case 2:
		blendmap->save( file );
		map_editor->save( file );
		if ( blendmap_mod != NULL ) blendmap_mod->save( file );
		break;

	case 3:
		blendmap->save( file );
		map_editor->save( file );
		if ( blendmap_mod != NULL ) blendmap_mod->save( file );
		break;	
	}

noise->save( file );
warp->save( file );
transformation->save( file );
file << "}\n";
}

//**********************************************
// Load
//**********************************************
bool PovTexDensity::load( ifstream & file, char * ltag )
{
if ( strcmp( "DENSITY", 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 (noise->load( file , tag ) ) continue;
	if (warp->load( file , tag ) ) continue;	
	if (transformation->load( file , tag ) ) continue;	

	if ( !strcmp( tag, "BMAP" ) )
		{
		if ( blendmap == NULL ) blendmap = new TvWidget_blendmap( _("Pattern"), "BMAP", NULL, app_ref );
		blendmap->load( file, tag );
		continue;
		}
		
	if ( !strcmp( tag, "BMAPMOD" ) )
		{
		if ( blendmap_mod == NULL ) blendmap_mod = new TvWidget_blendmap_mod( _("Pattern"), "BMAPMOD", NULL, app_ref );
		blendmap_mod->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, "DENSITY" ) )
		{
		if ( blockdens1 == NULL ) { blockdens1 = new PovTexDensity( app_ref, NULL, NULL, false ); blockdens1->load( file, tag ); continue; }
		if ( blockdens2 == NULL ) { blockdens2 = new PovTexDensity( app_ref, NULL, NULL, false ); blockdens2->load( file, tag ); continue; }
		if ( blockdens3 == NULL ) { blockdens3 = new PovTexDensity( app_ref, NULL, NULL, false ); blockdens3->load( file, tag ); continue; }
		tvio_skip_section( file );
		}

	if ( !strcmp( tag, "MAPEDIT" ) )
		{
		if ( type->value() == 3 )
			map_editor = new TvWidget_map_editor( _("Map editor"), "MAPEDIT", NULL, app_ref, mapedit_feeder_dens, this );
		else
			map_editor = new TvWidget_map_editor( _("Map editor"), "MAPEDIT", NULL, app_ref, mapedit_density_feeder_col, this );
		map_editor->load( file, tag );
		continue;
		}

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


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

PovTexDensity *dens = (PovTexDensity*)item;
if ( blockdens1 != NULL ) { delete blockdens1; blockdens1 = NULL; }
if ( blockdens2 != NULL ) { delete blockdens2; blockdens2 = NULL; }
if ( blockdens3 != NULL ) { delete blockdens3; blockdens3 = NULL; }
if ( blendmap != NULL ) { delete blendmap; blendmap = NULL; }
if ( blendmap_mod != NULL ) { delete blendmap_mod; blendmap_mod = NULL; }
if ( blockpattern != NULL ) { delete blockpattern; blockpattern = NULL; }
if ( map_editor != NULL ) { delete map_editor; map_editor = NULL; }

if ( GTK_IS_WIDGET(widget) )
	{
	gtk_widget_destroy( edit_cont );
	type->clear_widget();
	transformation->clear_widget();
	noise->clear_widget();
	warp->clear_widget();
    }
type->copy( dens->type );
transformation->copy( dens->transformation );
noise->copy( dens->noise );
warp->copy( dens->warp );

if ( dens->blockdens1 != NULL ) blockdens1 = new PovTexDensity( *dens->blockdens1 ); else blockdens1 = NULL;
if ( dens->blockdens2 != NULL ) blockdens2 = new PovTexDensity( *dens->blockdens2 ); else blockdens2 = NULL;
if ( dens->blockdens3 != NULL ) blockdens3 = new PovTexDensity( *dens->blockdens3 ); else blockdens3 = NULL;
if ( dens->blendmap != NULL ) blendmap = new TvWidget_blendmap( *dens->blendmap );
if ( dens->blendmap_mod != NULL ) blendmap_mod = new TvWidget_blendmap_mod( *dens->blendmap_mod );
if ( dens->blockpattern != NULL ) blockpattern = new TvWidget_blockpattern( *dens->blockpattern );
if ( dens->map_editor != NULL ) map_editor = new TvWidget_map_editor( *dens->map_editor );

	GtkTreeIter *old_node = gtk_tree_iter_copy( &(dens->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;
}



//**********************************************
// Map Item Density
//**********************************************
int MapItem_density::obj_count = 1;

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

MapItem_density::MapItem_density( MapItem_density & res ) : MapItem( res )
{
color[0]=color[1]=color[2]=res.color[0]; color[3]=255;
density = new PovTexDensity( *res.density );
//char text[25];
//sprintf( text, "Map density #%u", obj_count++ );
//density->set_name( text );
density->set_name( res.density->get_name() );
}

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

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