//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// objectlayer.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/objectlayer.h"
#include "include/objectlist.h"
#include "include/viewmanager.h"
#include <gdk/gdk.h>
#include "include/interface.h"
#include "include/tvio.h"



//**************************************
// Layers - Calques
//**************************************
// Static members
app_objs * ObjectLayer::app_ref = NULL;
// Pixmaps for visibility, lock, & render buttons in clist
GdkPixmap *ObjectLayer::visibility_pix = NULL;
GdkPixmap *ObjectLayer::visibility_mask = NULL;
GdkPixmap *ObjectLayer::select_pix = NULL;
GdkPixmap *ObjectLayer::select_mask = NULL;
GdkPixmap *ObjectLayer::render_pix = NULL;
GdkPixmap *ObjectLayer::render_mask = NULL;
// pointer to layer list and objects tree
GtkWidget *ObjectLayer::clist = NULL;
GtkWidget *ObjectLayer::obj_ctree = NULL;


//**************************************
// Constructors & destructor
//**************************************
// Basic
ObjectLayer::ObjectLayer( app_objs * appref )
{
app_ref = appref;
name = new TvWidget_entry( N_("Name"), "NAME", NULL, app_ref );
visible = new TvWidget_bool( N_("Visible"), "VISI", NULL, app_ref, true );
selectable = new TvWidget_bool( N_("Selectable"), "SELECT", NULL, app_ref, true );
render  = new TvWidget_bool( N_("Render"), "RENDER", NULL, app_ref, true );
set_name( "Layer #1" );
edit_box = NULL;
}


// Copies contructor
ObjectLayer::ObjectLayer( ObjectLayer & ref )
{
name = new TvWidget_entry( *ref.name );
visible = new TvWidget_bool( *ref.visible );
selectable = new TvWidget_bool( *ref.selectable );
render = new TvWidget_bool( *ref.render );
set_name( (ref.name)->value() );
edit_box = NULL;
OBJLIST_DEF
// objects copy
for ( unsigned int i = 0 ; i < ref.objects.size() ; i++ )
	{
	if ( ref.objects[i]->get_type() == TV_OBJ3D_CAMERA ) continue;
	if ( ref.objects[i]->get_category() == TV_OBJ3D_ATMOS1 ) continue;	
	Object3D *obj = ref.objects[i]->duplicate_yourself();
	obj->set_layer( this );
	objects.push_back( obj );
	objlist->add_object( obj );	
	}
}


// Destructor
ObjectLayer::~ObjectLayer()
{
OBJLIST_DEF
objlist->unselect_object();
if ( objects.size() != 0 )
	for ( unsigned int i = 0 ; i < objects.size() ; i ++ )
		{
		objlist->remove_object( objects[i] );
		delete objects[i]; 	
		}
delete name;
delete visible;
delete selectable;
delete render;
}


//*************************************************
// Layer name management
//
// Set layer name
// Test name existence in object pointer  list managed by ObjectList
// recursive function
//*************************************************
void ObjectLayer::set_name( char *nom )
{
OBJLIST_DEF
if ( objlist->layer_name_exist( nom, this ) )
	{
	int b;
	register int a = b = strlen( nom ) - 1;
	char *tmp = new char[ b + 30 ];
	strcpy( tmp, nom );

	while ( nom[a] > 47 && nom[a] < 58 ) a--;
	if ( a == b )
		strcat( tmp, " #2" );
	else
		{
		int num;
		sscanf( nom + a + 1, "%u", &num );
		tmp[a+1] = '\0';
		sprintf( tmp + a + 1, "%u", num + 1 ); 		
		}
	set_name( tmp );
	delete tmp;
	}
else
	name->set( nom );
}


//********************************************
// Add object
//
// Add an object to layer
// also add it to ObjectList pointer list
// Does NOT display anything, use insert_object
// to add an object to a selected layer
//********************************************
void ObjectLayer::add_object( Object3D *obj )
{
objects.push_back( obj );
obj->set_layer( this );
obj->set_parent( NULL );
OBJLIST_DEF
objlist->add_object( obj );
}
		
//*********************************************
// Insert object
// Add object with add_object and also add it to tree
//*********************************************		
void ObjectLayer::insert_object( Object3D *obj, int position )
{
OBJLIST_DEF
objlist->add_object( obj );
obj->set_parent( NULL );
obj->set_layer( this );
vector<Object3D*>::iterator it;
it = objects.begin() + position;
objects.insert( it, obj );

if ( objlist->get_current_layer() == this )
	{
	obj->add_to_tree( GTK_WIDGET(obj_ctree), NULL , ( (unsigned int)position == objects.size() - 1 ) ? NULL : objects[position+1]->get_node() );
	obj->ctree_node_select();
	}
}


//**********************************************
// Delete object
//**********************************************
void ObjectLayer::delete_object( Object3D *obj )
{
if ( objects.size() == 0 ) return;
if ( obj == NULL ) return;
remove_object( obj );
// Don't delete cause we wanna undo if necessary
//delete obj;
}



//*********************************************
// Layer list management
// Called by ObjectList to manage layers in a clist
//*********************************************

//*********************************************
// Add to list
// Add the layer to the clist managed by ObjectList
// Set pixmaps for visbility / render / lock
//*********************************************
void ObjectLayer::add_to_list( GtkWidget *clistd, gint pos )
{

clist = clistd;
gchar *text[] = { name->value(), "",  "", "" };
gint clist_row = gtk_clist_insert( GTK_CLIST(clist), pos, text );
gtk_clist_set_row_data( GTK_CLIST(clist), clist_row, (gpointer)this );

INTERF_DEF
GdkColor transparent;
if ( visibility_pix == NULL )
	{
	//gchar *pixmap = gnome_pixmap_file( "Truevision/object_visible.xpm" );
	char *pixmap = tv_get_pixmap( "object_visible.xpm" );
	visibility_pix = gdk_pixmap_create_from_xpm( interf->get_gdkwin(), &visibility_mask, &transparent, pixmap );
	delete pixmap;
	}
if ( visible->value()  ) gtk_clist_set_pixmap( GTK_CLIST(clist), clist_row, 1, visibility_pix, visibility_mask );

if ( select_pix == NULL )
	{
	//gchar *pixmap = gnome_pixmap_file( "truevision/lock.xpm" );
	char *pixmap = tv_get_pixmap( "lock.xpm" );		
	select_pix = gdk_pixmap_create_from_xpm( interf->get_gdkwin(), &select_mask, &transparent, pixmap );
	delete pixmap;
	}
if ( ! selectable->value()  ) gtk_clist_set_pixmap( GTK_CLIST(clist), clist_row, 2, select_pix, select_mask );

if ( render_pix == NULL )
	{
	//gchar *pixmap = gnome_pixmap_file( "truevision/render.xpm" );
	char *pixmap = tv_get_pixmap( "render.xpm" );
	render_pix = gdk_pixmap_create_from_xpm( interf->get_gdkwin(), &render_mask, &transparent, pixmap );
	delete pixmap;
	}
if ( render->value()  ) gtk_clist_set_pixmap( GTK_CLIST(clist), clist_row, 3, render_pix, render_mask );
}



//***************************************************
// Move layers : move_up & move_down
// move layer in clist
//***************************************************
void ObjectLayer::move_up_in_clist()
{
OBJLIST_DEF
gint index = objlist->get_layer_index( this );
gtk_clist_row_move( GTK_CLIST(clist), --index, --index );
}

void ObjectLayer::move_down_in_clist()
{
OBJLIST_DEF
gint index = objlist->get_layer_index( this );
gtk_clist_row_move( GTK_CLIST(clist), index-1, index );
}







//*****************************************************
// Toggle properties
//
// Callback used when pixmaps in list clicked
// set visbility / render & lock values
//******************************************************
void ObjectLayer::toggle_property( GtkWidget *clist, gint row, gint col )
{
switch ( col )
	{
	case 1:
		{
		visible->toggle();
		if ( visible->value() ) gtk_clist_set_pixmap( GTK_CLIST(clist), row, 1, visibility_pix, visibility_mask );
		else gtk_clist_set_text( GTK_CLIST(clist), row, 1, NULL );
		VMAN_DEF
		vmanager->refresh();
		}
		break;
		
	case	2:
		{
		selectable->toggle();
		if ( ! selectable->value()  ) gtk_clist_set_pixmap( GTK_CLIST(clist), row, 2, select_pix, select_mask );
		else gtk_clist_set_text( GTK_CLIST(clist), row, 2, NULL );
		}
		break;
	
	case 3:
		{
		render->toggle();
		if ( render->value()  ) gtk_clist_set_pixmap( GTK_CLIST(clist), row, 3, render_pix, render_mask );
		else gtk_clist_set_text( GTK_CLIST(clist), row, 3, NULL );
		}
		break;
	}
}


//**********************************************************
// Can be destroyed
// Check whether we can destroy this layer
// ie if it doesn't contain undeletable objets : camera...
//**********************************************************
bool ObjectLayer::can_be_destroyed()
{
for ( unsigned int i = 0 ; i < objects.size() ; i ++ )
	if ( objects[i]->get_type() == TV_OBJ3D_CAMERA ) return false;
return true;
}



//***********************************************************
// Edit box
//
// used to edit layer name
//***********************************************************
void ObjectLayer::raise_edit_box()
{
if ( edit_box != NULL )
	{
	gdk_window_raise( edit_box->window );
	return;
	}

edit_box = gtk_window_new( GTK_WINDOW_POPUP );
gtk_window_set_title( GTK_WINDOW(edit_box), _("Layer properties") );
gtk_window_set_policy( GTK_WINDOW(edit_box), FALSE, FALSE, TRUE );
gtk_signal_connect( GTK_OBJECT(edit_box), "delete_event", GTK_SIGNAL_FUNC(sign_objlayer_edit_box_destroy), this );

GtkWidget *vbox = gtk_vbox_new( FALSE, 0 );
gtk_container_set_border_width( GTK_CONTAINER(vbox), 7 );
gtk_container_add( GTK_CONTAINER(edit_box), vbox );

GtkWidget *hbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(vbox), hbox, TRUE, TRUE, 5 );

GtkWidget *lab = gtk_label_new( _("Layer name : ") );
gtk_box_pack_start( GTK_BOX(hbox), lab, TRUE, TRUE, 5 );

edit_name_entry = gtk_entry_new();
gtk_entry_set_text( GTK_ENTRY(edit_name_entry), name->value() );
gtk_box_pack_start( GTK_BOX(hbox), edit_name_entry, TRUE, TRUE, 5 );
gtk_signal_connect( GTK_OBJECT(edit_name_entry), "activate", GTK_SIGNAL_FUNC(sign_objlayer_edit_ok_clicked), this );

GtkWidget *sep = gtk_hseparator_new();
gtk_box_pack_start( GTK_BOX(vbox), sep, TRUE, TRUE, 8 );

GtkWidget *button_box = gtk_hbutton_box_new();
gtk_button_box_set_layout( GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_SPREAD );
gtk_button_box_set_spacing( GTK_BUTTON_BOX(button_box), 10 );
gtk_box_pack_start( GTK_BOX(vbox), button_box, FALSE, FALSE, 0 );

	GtkWidget *button = gtk_button_new_with_label( _("OK") );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_objlayer_edit_ok_clicked), this );
	gtk_container_add( GTK_CONTAINER(button_box), button );

	button = gtk_button_new_with_label( _("Cancel") );
	gtk_container_add( GTK_CONTAINER(button_box), button );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_objlayer_edit_cancel_clicked), this );
	
gtk_widget_show_all( edit_box );
}


// Close dialog box
void ObjectLayer::destroy_edit_box( int val )
{
OBJLIST_DEF
if ( val == 1 )
	{
	set_name( (char*)gtk_entry_get_text( GTK_ENTRY(edit_name_entry) ) );
	gtk_clist_set_text( GTK_CLIST(clist), objlist->get_layer_index( this )-1, 0, (gchar*)name->value() );
	}
if ( edit_box != NULL )
	{
	gtk_widget_destroy( edit_box );
	edit_box = NULL;
	}
}



//***************************************************
// Display
//***************************************************

//***************************************************
// Display contents
//
// Display objects owned by this layer in a ctree
//***************************************************
void ObjectLayer::display_content( GtkWidget *tree )
{
obj_ctree = tree;
gtk_clist_freeze( GTK_CLIST(tree) );
for ( unsigned int i = 0 ; i < objects.size() ; i++ )
	objects[i]->add_to_tree( obj_ctree, NULL, NULL );	
if ( objects.size() > 0 ) objects[0]->ctree_node_select();
gtk_clist_thaw( GTK_CLIST(tree) );
}


//***************************************************
// Display

// Display objects owned by this layer in OpenGL views
//***************************************************
void ObjectLayer::display( glview *view )
{
if ( ! visible->value() ) return;
if ( ! selectable->value() )
	{
	GLint val = 0;
	glGetIntegerv( GL_RENDER_MODE, &val );
	if ( val == GL_SELECT ) return;
	}
for ( unsigned int i = 0 ; i < objects.size() ; i++ )
	{
	objects[i]->display( view );
	}
}



//************************************************************
// Output & Input
//************************************************************

//************************************************************
// Output to povray
//************************************************************
void ObjectLayer::output_to_povray( ofstream & file )
{
if ( ! render->value() ) return;
file << "\n\n//----------------------\n// Layer " << name->value()<< "\n//----------------------";
for ( unsigned int i = 0 ; i < objects.size() ; i++ )
	objects[i]->output_to_povray( file );
}


//***********************************************************
// Save
//***********************************************************
void ObjectLayer::save( ofstream & file )
{
file << "\nLAYER{\n";
name->save( file );
visible->save( file );
selectable->save( file );
render->save( file );
for ( unsigned int i = 0 ; i < objects.size() ; i++ )
	{
	objects[i]->save( file );
	}
file << "\n}";
}


//************************************************************
// Load
//************************************************************
bool ObjectLayer::load( ifstream & file, char *ltag )
{
if ( strcmp( "LAYER", ltag ) ) return false;

OBJLIST_DEF
char * tag = NULL;
do {
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) break;
	if ( name->load( file, tag ) ) { continue; }
	if ( visible->load( file, tag ) ) continue;
	if ( selectable->load( file, tag ) ) continue;
	if ( render->load( file, tag ) ) continue;
	
	Object3D *obj = objlist->create_object( tag );
	if ( obj != NULL )
		{
		if ( obj->load( file, tag ) )
			{
			add_object( obj );
			if ( obj->get_type() == TV_OBJ3D_CAMERA ) objlist->set_camera( obj );
			continue;
			}
		else delete obj;
		}
		
	tvio_skip_section(file );
	} while ( tag != NULL );

return true;
}



//***********************************************
// Objects manipulation
//***********************************************

//***********************************************
// Remove object
// remove an object from the layer and from the ctree
// don't delete the object ( need to be stored for undo )
//***********************************************
void ObjectLayer::remove_object( Object3D *obj )
{
// Object belong to CSG ?
if ( obj->get_parent() != NULL )
	{
	 obj->get_parent()->remove_object( obj );
	 return;
	 }

// Finding the object
vector<Object3D*>::iterator object;
unsigned int i = 0;
for ( object = objects.begin() ; object != objects.end() ; object++ )
	{
	i++;
	if ( *object != obj ) continue;
	Object3D *target = *object;
	if ( target->get_type() == TV_OBJ3D_CAMERA )
		{
		app_warning( _("You shoudn't delete the camera ;-)") );
		return;
		}

	OBJLIST_DEF

	// Managing the tree
	if ( i < 0 ) i = 0;
	if ( i > objects.size() - 1 ) i = objects.size() - 2;
	VMAN_DEF
	vmanager->freeze();
	if ( objlist->get_current_layer() == this )
		{
		gtk_clist_freeze( GTK_CLIST(obj_ctree) );
		target->remove_from_tree();	
		//objlist->clear_current_object();
		if ( objects.size() > 1 ) objects[i]->ctree_node_select();		
		gtk_clist_thaw( GTK_CLIST(obj_ctree) );
		}
	vmanager->thaw();

	// Modifying lists
	objlist->remove_object( target );
	objects.erase( object );
	break;
	}
}


//****************************************************
// Duplicate object
// duplicate the current object and insert the copy under the
// current object
//****************************************************
void ObjectLayer::duplicate_object( Object3D *obj )
{
if ( obj->get_parent() != NULL )
	obj->get_parent()->duplicate_object( obj );
else
	{
	vector<Object3D*>::iterator object;
	for ( object = objects.begin() ; object != objects.end() ; object++ )
		{
		if ( *object != obj ) continue;
		Object3D *target = *object;
		if ( target->get_type() == TV_OBJ3D_CAMERA || target->get_category() == TV_OBJ3D_ATMOS1 )
			{
			app_warning( _("You can't duplicate this object") );
			return;
			}
		OBJLIST_DEF
		Object3D *newobj = target->duplicate_yourself();
	    objlist->add_object( newobj );
	    Object3D *parent =    target->get_parent();
	    target = *(++object);
	    newobj->add_to_tree( obj_ctree, (parent == NULL ) ? NULL : parent->get_node() , ( object != objects.end() ) ? target->get_node() : NULL );
		objects.insert( object, newobj );
		newobj->ctree_node_select();				
		break;
		}
	}
}


//***************************************************
// Paste object
// paste object previously copied and stored in ObjectList
//***************************************************
void ObjectLayer::paste_object( Object3D *obj, Object3D* selected )
{
if ( selected != NULL )
	if ( selected->is_group() )
		{
		selected->paste_object( obj );
		return;
		}

append_object( obj );
obj->ctree_node_select();	
obj->set_parent( NULL );				
obj->set_layer( this );
}



//***************************************************
// Move objects up and down
//***************************************************
void ObjectLayer::move_object_up( Object3D *obj )
{
if ( obj->get_parent() == NULL )
	{
	vector<Object3D*>::iterator object;
	int i = 0;
	for ( object = objects.begin() ; object != objects.end() ; object++ )
		{
		i++;
		if ( *object != obj ) continue;
		if ( i ==  1 ) return;
		Object3D *target = *object;
		vector<Object3D*>::iterator object2 = object;
		object2--;
		gtk_ctree_move(  GTK_CTREE(obj_ctree), target->get_node(), NULL, (*object2)->get_node() );
		objects.erase( object );
		objects.insert( object2, target );
		break;
		}
	}
else    // Child of another object, let him care about this...
	obj->get_parent()->move_child_up( obj );
}

void ObjectLayer::move_object_down( Object3D *obj )
{
if ( obj->get_parent() == NULL )
	{
	vector<Object3D*>::iterator object;
	unsigned int i = 0;
	for ( object = objects.begin() ; object != objects.end() ; object++ )
		{
		i++;
		if ( *object != obj ) continue;
		if ( i ==  objects.size() ) return;
		Object3D *target = *object;
		vector<Object3D*>::iterator object2 = object;
		object2 += 1;
		gtk_ctree_move(  GTK_CTREE(obj_ctree), (*object2)->get_node(), NULL, target->get_node() );
		objects.erase( object );
		objects.insert( object2, target );
		break;
		}
	}
else    // Child of another object, let him care about this...
	obj->get_parent()->move_child_down( obj );
}
