//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// objectlayer.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/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;
// pointer to layer list and objects tree
GtkListStore *ObjectLayer::list_store = NULL;
GtkWidget *ObjectLayer::obj_tree_view = NULL;
GtkTreeStore *ObjectLayer::obj_tree_store = NULL;
GtkTreeSelection *ObjectLayer::obj_tree_selection = 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;
obj_tree_store = 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;
obj_tree_store = ref.obj_tree_store;
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()
{
//cout << "\ndelete layer " << name->value(); cout.flush();
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;
//cout << "\nDone"; cout.flush();
}


//*************************************************
// 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,  Object3D *selected)
{
if ( selected != NULL ) 
	{
	if ( selected->is_group() == true ) { selected->paste_object( obj ); return; }
	if ( selected->get_parent() != NULL ) if ( selected->get_parent()->is_group() == true ) { selected->get_parent()->paste_object( obj ); return; }
	}
objects.push_back( obj );
obj->set_layer( this );
obj->set_parent( NULL );
OBJLIST_DEF
objlist->add_object( obj );
if ( objlist->get_current_layer() == this  ) 
	{ 
	obj->add_to_tree( obj_tree_view, obj_tree_store, obj_tree_selection, NULL, NULL, NULL );
	obj->tree_node_select(); 
	}
}
		
//*********************************************
// 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( obj_tree_view, obj_tree_store, obj_tree_selection, NULL, 	( (unsigned int)position == objects.size() - 1 ) ? NULL : objects[position+1]->get_node() );
	obj->tree_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( GtkListStore *store, int position )
{
list_store = store;
GdkPixbuf *visible_pixbuf = NULL;
if ( visible->value()  == true ) 
	{
	char *pixmap = tv_get_pixmap( "object_visible.xpm" );
	visible_pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
	delete pixmap;
	}
	
GdkPixbuf *lock_pixbuf = NULL;
if ( selectable->value()  == false  ) 
	{
	char *pixmap = pixmap = tv_get_pixmap( "lock.xpm" );
	lock_pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
	delete pixmap;
	}
	
GdkPixbuf *render_pixbuf = NULL;
if ( render->value()  == true  ) 
	{
	char *pixmap = pixmap = tv_get_pixmap( "render.xpm" );
	render_pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
	delete pixmap;
	}

if ( position != -1 ) gtk_list_store_insert( list_store, &node_iter, position );
else gtk_list_store_append( list_store, &node_iter );
gtk_list_store_set( list_store, &node_iter, 3, name->value(), 0, visible_pixbuf, 1, lock_pixbuf, 2, render_pixbuf, 4,  this, -1 );
}



//***************************************************
// Move layers : move_up & move_down
// move layer in clist
//***************************************************
void ObjectLayer::move_before_in_list( GtkTreeIter *sibling )
{
gtk_list_store_move_before( list_store, &node_iter, sibling );
}

void ObjectLayer::move_after_in_list( GtkTreeIter *sibling )
{
gtk_list_store_move_after( list_store, &node_iter, sibling );

}



//*****************************************************
// Toggle properties
//
// Callback used when pixmaps in list clicked
// set visbility / render & lock values
//******************************************************
void ObjectLayer::toggle_property( int col )
{
switch ( col )
	{
	case 1:
		{
		visible->toggle();
		GdkPixbuf *pixbuf = NULL;
		if ( visible->value()  ) 
			{
			char *pixmap = tv_get_pixmap( "object_visible.xpm" );
			pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
			delete pixmap;
			}
		gtk_list_store_set( list_store, &node_iter, 0, pixbuf, -1 );
		VMAN_DEF
		vmanager->refresh();
		}
		break;
		
	case	2:
		{
		selectable->toggle();
		GdkPixbuf *pixbuf = NULL;
		if ( ! selectable->value()  ) 
			{
			char *pixmap = tv_get_pixmap( "lock.xpm" );
			pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
			delete pixmap;
			}
		gtk_list_store_set( list_store, &node_iter, 1, pixbuf, -1 );
		}
		break;
	
	case 3:
		{
		render->toggle();
		GdkPixbuf *pixbuf = NULL;
		if ( render->value()  ) 
			{
			char *pixmap = tv_get_pixmap( "render.xpm" );
			pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
			delete pixmap;
			}
		gtk_list_store_set( list_store, &node_iter, 2, pixbuf, -1 );
		}
		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_dialog_new_with_buttons( _("Layer properties"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL );
g_signal_connect( G_OBJECT(edit_box), "response", G_CALLBACK(sign_objlayer_edit_clicked), this );
g_signal_connect( G_OBJECT(edit_box), "close", G_CALLBACK(sign_objlayer_edit_box_destroy), this );


GtkWidget *hbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(GTK_DIALOG(edit_box)->vbox), hbox, FALSE, 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 );

gtk_widget_show_all( edit_box );
}


// Close dialog box
void ObjectLayer::destroy_edit_box( int val )
{
//OBJLIST_DEF
if ( val == GTK_RESPONSE_OK )
	{
	set_name( (char*)gtk_entry_get_text( GTK_ENTRY(edit_name_entry) ) );
	gtk_list_store_set( list_store, &node_iter, 3, (gchar*)name->value(), -1 );
	}
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 *view, GtkTreeStore *store, GtkTreeSelection *selection )
{
obj_tree_view = view;
obj_tree_store = store;
obj_tree_selection = selection;
for ( unsigned int i = 0 ; i < objects.size() ; i++ )
	objects[i]->add_to_tree( view, store, selection, NULL, NULL );	

if ( objects.size() > 0 ) objects[0]->tree_node_select();
}


//***************************************************
// 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_pass1( 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_pass1( file );
}

void ObjectLayer::output_to_povray_pass2( 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_pass2( 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, NULL );
			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 )
		{
		target->remove_from_tree();	
		if ( objects.size() > 1 ) 
			objects[i]->tree_node_select();		
		else objlist->clear_current_object();
		}
	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;
		if ( obj->get_type() == TV_OBJ3D_CAMERA || obj->get_category() == TV_OBJ3D_ATMOS1 )
			{
			app_warning( _("You can't duplicate this object") );
			return;
			}
		OBJLIST_DEF
		Object3D *newobj = obj->duplicate_yourself();
	    objlist->add_object( newobj );
	    Object3D *parent =    obj->get_parent();
	    Object3D *target = *(++object);
   		 newobj->add_to_tree( obj_tree_view, obj_tree_store, obj_tree_selection, (parent == NULL ) ? NULL : parent->get_node() , ( object != objects.end() ) ? target->get_node() : NULL );
		objects.insert( object, newobj );
		newobj->tree_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_tree_store_move_before( obj_tree_store, target->get_node(), (*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() );
		gtk_tree_store_move_after( obj_tree_store, target->get_node(), (*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_down( obj );
}

// *************************************************************************
// Unparent object
//
// Remove  object from a group and move it to group's parent / root
//**************************************************************************
void ObjectLayer::unparent_object( Object3D *obj )
{
	Object3D *parent  = obj->get_parent();
	if ( parent == NULL )
		return;
	parent->remove_object( obj );
	Object3D *grand_parent = parent->get_parent();
	if ( grand_parent == NULL ) {
		int pos = get_object_pos( parent );
		insert_object( obj, pos );
	}
	else {
		int pos = grand_parent->get_child_pos( parent );
		grand_parent->insert_child( obj, pos );
	}
}
