//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// object3d.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/object3d.h"
#include "include/objectlist.h"
#include "include/interface.h"
#include "include/preferences.h"
#include "include/viewmanager.h"
#include "include/scene.h"
#include "include/undo.h"

//**************************************
// Définition
//**************************************
app_objs *Object3D::app_ref = NULL;
GtkTreeStore *Object3D::tree_store= NULL;
GtkWidget *Object3D::tree_view= NULL;
GtkTreeSelection *Object3D::tree_selection= NULL;

//**************************************
// Noms des objets
//**************************************
const int obj_num = 38;
const char *obj_names[ obj_num ] = {
	N_("Camera"),
	N_("Point light"),
	N_("Spot light"),
	N_("Cylindrical Light"),
	N_("Area Light"),
	N_("Box"),
	N_("Sphere"),
	N_("Cylinder"),
	N_("Cone"),
	N_("Background"),
	N_("Union"),
	N_("Merge"),
	N_("Intersection"),
	N_("Difference"),
	N_("Plane"),
	N_("Sky Sphere"),
	N_("Torus"),
	N_("Atmospheric Media"),
	N_("Disc"),
	N_("Heightfield"),
	N_("Superellipsoid"),
	N_("Fog"),
	N_("Light Group"),
	N_("Blob"),
	N_("Blob Sphere"),
	N_("Blob Cylinder"),
	N_("Text"),
	N_("Lathe"),
	N_("Link"),
	N_("Group"),
	N_("Prism"),
	N_("Isosurface"),
	N_("Julia Fractal"),
	N_("Parametric"),
	N_("Pov Script"),
	N_("Sphere sweep"),
	N_("Bicubic patch"),
	N_("Script object"),
};
const char *obj_helpid[ obj_num ] = {
	"sect-camera",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"sect-athmos-bkgd",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	"sect-athmos-sky",
	NULL,
	"sect-athmos-amed",
	NULL,
	NULL,
	NULL,
	"sect-athmos-fog",
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
};
	

//**************************************
// Constructeur
//**************************************
Object3D::Object3D( app_objs *appref )
{
app_ref = appref;
parent = NULL;
layer = NULL;
hidden = new ObjParam_bool( N_("hide"), "HIDDE", NULL, app_ref, true, false );
render = new ObjParam_bool( N_("render"), "RENDER", NULL, app_ref, false, true );
hollow = new ObjParam_bool( N_("hollow"), "HOLLOW", NULL, app_ref, false, false );
no_shadow = new ObjParam_bool(N_("no shadow"), "NOSHAD", NULL, app_ref, false, false );
inverse  = new ObjParam_bool( N_("inverse"), "INV", NULL, app_ref, false, false );
no_image = new ObjParam_bool( N_("no image"), "NOIMG", NULL, app_ref, false, false );
no_reflection = new ObjParam_bool( N_("no reflection"), "NOREFL", NULL, app_ref, false, false );
double_illuminate = new ObjParam_bool( N_("double illuminate"), "DBILL", NULL, app_ref, false, false );

name = new TvWidget_entry( N_("Name"), "NAME", NULL, app_ref );
selected = false;
nm_entry = NULL;
tree_view = NULL;
}

Object3D::Object3D( Object3D & ref )
{
type = ref.type;
parent = ref.parent;
layer = ref.layer;
category = ref.category;
frame = NULL; edit_cont = NULL; widget = NULL;
hidden = new ObjParam_bool( *ref.hidden );
render = new ObjParam_bool( *ref.render );
hollow =  new ObjParam_bool( *ref.hollow );
no_shadow =  new ObjParam_bool( *ref.no_shadow );
inverse =  new ObjParam_bool( *ref.inverse );
no_image = new ObjParam_bool( *ref.no_image );
no_reflection = new ObjParam_bool( *ref.no_reflection );
double_illuminate = new ObjParam_bool( *ref.double_illuminate );
selected = false;
tree_view = NULL;
nm_entry = NULL;

name = new TvWidget_entry( N_("Name"), "NAME", NULL, app_ref );
set_name( ref.name->value() );
}

//**************************************
// Destructeur
//**************************************
Object3D::~Object3D()
{
delete hidden;
delete render;
delete hollow;
delete no_shadow;
delete name;
delete inverse;
delete no_image;
delete no_reflection;
delete double_illuminate;
}


void Object3D::unselect()
{ 
selected = false; 
//if ( ctree != NULL ) gtk_ctree_unselect( ctree, ctree_node );
//list.invalidate(); 
}

char * Object3D::get_type_name()
{
return (char*)obj_names[ type ];
}

//**************************************
// Set Name
//**************************************
void Object3D::name_changed()
{
if ( !name->has_changed() ) return;
set_name( name->get_current_value() );
SCENE_DEF
scene->set_modified();
}

void Object3D::set_name( char *nm )
{
OBJLIST_DEF
char *test = new char[ strlen(nm) + 1 ];
strcpy( test, nm );

char *res = objlist->create_name( test );
if ( res != NULL ) 
	{
	name->set(res);
	delete res;
	}
else name->set(test);
delete test;

name->update_widget();
if ( GTK_IS_WIDGET(tree_view) ) gtk_tree_store_set( tree_store, &node_iter, 4, name->value(), -1 );
objlist->update_ref_list();
}

void Object3D::check_name()
{
char *nm = name->value();	
char *test = new char[ strlen(nm) + 1 ];
strcpy( test, nm );
name->set( NULL );
set_name( test );
delete test;
}

void Object3D::get_underscore_name( ofstream & file )
{
char *nom = name->value();
//file << nom;
int len = strlen( nom );
file << "obj_";
for ( int i = 0 ; i < len ; i++ )
	file << (( nom[i] == ' ' || nom[i] == '#' || nom[i] == '-' || nom[i] == '+' || nom[i] == '=' ) ? '_' : nom[i]);
}

//**************************************
// Set Picking name
//**************************************
void Object3D::display( glview *view, bool set_color )
{
OBJLIST_DEF
pick_name = objlist->get_pick_name();
glLoadName( pick_name );
}


//**************************************
// Set Color
//**************************************
void Object3D::set_color()
{
PREF_DEF 
if ( selected ) pref->objsel_color->gl_set_rgb();
else pref->obj_color->gl_set_rgb();
}

void Object3D_with_material::set_color()
{
PREF_DEF 
if ( ! selected && texture->get_current() == NULL && parent != NULL ) { parent->set_color(); return; }
if ( selected ) pref->objsel_color->gl_set_rgb();
else 
	if ( ! texture->gl_set_material() ) pref->obj_color->gl_set_rgb();
}

//**************************************
// Add to tree
//**************************************
void Object3D::add_to_tree( GtkWidget *view, GtkTreeStore *store, GtkTreeSelection *sel, GtkTreeIter *parent, GtkTreeIter *sibling, const gchar *pixspe )
{
tree_view = view;
tree_store = store;
tree_selection = sel;
parent_node_iter = parent;

GdkPixbuf *obj_pixbuf = NULL;
char *pixmap = NULL;
if ( pixspe != NULL ) pixmap = tv_get_pixmap( (char*)pixspe );
else pixmap =  tv_get_pixmap( "object_default.xpm" );
obj_pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
delete pixmap;

GdkPixbuf *visible_pixbuf = NULL;
if ( ! hidden->value()  ) 
	{
	char *pixmap = tv_get_pixmap( "object_visible.xpm" );
	visible_pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
	delete pixmap;
	}
	
GdkPixbuf *render_pixbuf = NULL;
if ( render->value()  ) 
	{
	char *pixmap = tv_get_pixmap( "render.xpm" );
	render_pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
	delete pixmap;
	}

if ( parent != NULL ) 
	{
	gtk_tree_store_insert_before( tree_store, &node_iter, parent, sibling );
	}
else 
	{
	gtk_tree_store_append( tree_store, &node_iter, NULL );
	}
gtk_tree_store_set( tree_store, &node_iter, 1, visible_pixbuf, 2, render_pixbuf, 0, obj_pixbuf, 3,  (gchar*)obj_names[type], 4, name->value(), 5, this, -1);
}


void Object3D::destroy()
{
gtk_tree_store_remove( tree_store, &node_iter );
delete this;
}


//**************************************
// Editeur
//**************************************
void Object3D::edit_widget_base( GtkWidget *wid  )
{
OBJLIST_DEF
PREF_DEF
bool tt = pref->tooltips->value();
objlist->clear_current_param();
widget = wid;

// Type de l'objet
GtkWidget *frame = gtk_frame_new(NULL);
gtk_box_pack_start( GTK_BOX(wid), frame, FALSE, TRUE, 0 );
GtkWidget *label = gtk_label_new( obj_names[type] );
gtk_container_add( GTK_CONTAINER(frame), label );

// Nom
name->get_widget( wid, tt );
name->connect_signal( GTK_SIGNAL_FUNC(sign_entry), this );
name->connect_signal2( GTK_SIGNAL_FUNC(sign_entry2), this );

// Container
GtkWidget *scrolled = gtk_scrolled_window_new( NULL, NULL );
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
gtk_box_pack_start( GTK_BOX(wid), scrolled, TRUE, TRUE, 4 );
GtkWidget *viewp = gtk_viewport_new( NULL, NULL );
gtk_viewport_set_shadow_type( GTK_VIEWPORT(viewp), GTK_SHADOW_ETCHED_IN /*GTK_SHADOW_NONE*/ );
gtk_container_add( GTK_CONTAINER(scrolled), viewp );
edit_cont = gtk_vbox_new( FALSE, 0 );
gtk_container_set_border_width( GTK_CONTAINER(edit_cont), 1 );
gtk_container_add( GTK_CONTAINER(viewp), edit_cont );
}

void Object3D::edit_widget( GtkWidget *wid , bool solid_object  )
{
PREF_DEF
bool tt = pref->tooltips->value();
edit_widget_base( wid );
hidden->get_widget( edit_cont, tt );
hidden->connect_signal( GTK_SIGNAL_FUNC(sign_hidden_changed), this );
render->get_widget( edit_cont, tt );
render->connect_signal( GTK_SIGNAL_FUNC(sign_render_changed), this );

if ( !solid_object ) return;
hollow->get_widget( edit_cont, tt );
no_shadow->get_widget( edit_cont, tt );
no_image->get_widget( edit_cont, tt );
no_reflection->get_widget( edit_cont, tt );
inverse->get_widget( edit_cont, tt );
double_illuminate->get_widget( edit_cont, tt );
}

//**************************************
// Frame & Table
//**************************************
void Object3D::new_frame( GtkWidget *wid, char *nom )
{
GtkWidget *Frame = gtk_frame_new( nom );
gtk_box_pack_start( GTK_BOX(wid), Frame, FALSE, TRUE, 6 );
frame = gtk_vbox_new( FALSE, 0 );
gtk_container_add( GTK_CONTAINER(Frame), frame );
gtk_container_set_border_width( GTK_CONTAINER(frame), 3 );
}


void Object3D::new_table( GtkWidget *wid, char *nom, int rows )
{
GtkWidget *Frame = gtk_frame_new( nom );
gtk_box_pack_start( GTK_BOX(wid), Frame, FALSE, TRUE, 6 );
table = gtk_table_new( rows, 4, FALSE );
gtk_container_add( GTK_CONTAINER(Frame), table );
gtk_container_set_border_width( GTK_CONTAINER(table), 3 );
}

void Object3D::new_table_no_frame( GtkWidget *box, int rows )
{
table = gtk_table_new( rows, 4, FALSE );
//gtk_container_add( GTK_CONTAINER(box), table );
gtk_box_pack_start( GTK_BOX(box), table, FALSE, TRUE, 6 );
gtk_container_set_border_width( GTK_CONTAINER(table), 3 );
}


//**************************************
// Pref_changed
//**************************************
void Object3D::pref_changed()
{
hidden->pref_changed();
render->pref_changed();
hollow->pref_changed();
no_shadow->pref_changed();
inverse->pref_changed();
no_image->pref_changed();
no_reflection->pref_changed();
double_illuminate->pref_changed();
list.invalidate();
}

//**************************************
// Destroy editor
//**************************************
void Object3D::destroy_editor()
{
flush();
gtk_widget_destroy( widget );
hidden->clear_widget();
render->clear_widget();
hollow->clear_widget();
no_shadow->clear_widget();
name->clear_widget();
inverse->clear_widget();
no_image->clear_widget();
no_reflection->clear_widget();
double_illuminate->clear_widget();
}

//**************************************
// Set visibility pix
//**************************************
void Object3D::set_visibility_pix()
{
GdkPixbuf *visible_pixbuf = NULL;
if ( ! hidden->value()  ) 
	{
	char *pixmap = tv_get_pixmap( "object_visible.xpm" );
	visible_pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
	delete pixmap;
	}
gtk_tree_store_set( tree_store, &node_iter, 1, visible_pixbuf,  -1);
}

void Object3D::set_render_pix()
{
GdkPixbuf *render_pixbuf = NULL;
if ( render->value()  ) 
	{
	char *pixmap = tv_get_pixmap( "render.xpm" );
	render_pixbuf = gdk_pixbuf_new_from_file( pixmap, NULL );
	delete pixmap;
	}
gtk_tree_store_set( tree_store, &node_iter, 2, render_pixbuf,  -1);
}

//**************************************
// MOUSE
//**************************************
/*void Object3D::mouse_translate( float dest[3], float normal[3], float ox, float oy, float x, float y )
{
//float xoffset = x - ox;
//float yoffset = y - oy;

}*/


//**************************************
// Output to povray
//**************************************
void Object3D::output_to_povray_pass1( ofstream & file )
{
if ( hollow->value() ) file << "\n\thollow ";
if ( no_shadow->value() ) file << "\n\tno_shadow";
if ( inverse->value() ) file << "\n\tinverse";
if ( no_image->value() ) 	file << "\n\tno_image";
if ( no_reflection->value() ) 	file << "\n\tno_reflection";
if ( double_illuminate->value() ) 	file << "\n\tdouble_illuminate";
//file << "\n\t";
}


void Object3D::save_basics( ofstream & file )
{
SCENE_DEF
scene->set_save_progress();
name->save( file );
hidden->save( file );
hollow->save( file );
render->save( file );
no_shadow->save( file );
inverse->save( file );
no_image->save( file );
no_reflection->save( file );
double_illuminate->save( file );
}

bool Object3D::load_basics( ifstream & file, char *tag )
{
if ( name->load( file, tag ) ) { check_name(); return true; }
if ( hidden->load( file, tag ) ) return true;
if ( hollow->load( file, tag ) ) return true;
if ( render->load( file, tag ) ) return true;
if ( no_shadow->load( file, tag ) ) return true;
if ( inverse->load( file, tag ) ) return true;
if ( no_image->load( file, tag ) ) return true;
if ( no_reflection->load( file, tag ) ) return true;
if ( double_illuminate->load( file, tag ) ) return true;
return false;
}

void Object3D::set_load_progress( ifstream & file )
{
SCENE_DEF
scene->set_load_progress( file );
}

//-------------------------------------------------
//  Object 3D with material 
//-------------------------------------------------
Object3D_with_material::Object3D_with_material( app_objs *appref ) : Object3D( appref )
{ 
texture = new ObjParam_texref( N_("Material"), "MAT", NULL, app_ref, false ); 
target = new ObjParam_bool( N_("Target"), "PTARG", NULL, app_ref, false, false );
spacing = new ObjParam_float( N_("Spacing"), "PSPACE", NULL, app_ref, false, 1 );
spacing->set_range( 20, 0, 0.1, 6 );
refraction = new ObjParam_bool( N_("Refraction"), "PREFRAC", NULL, app_ref, false, false );
reflection = new ObjParam_bool( N_("Reflection"), "PREFLEC", NULL, app_ref, false, false );
collect = new ObjParam_bool( N_("Collect"), "PCOL", NULL, app_ref, false, true );
pass_through = new ObjParam_bool( N_("Pass trough"), "PPT", NULL, app_ref, false, false );
}

Object3D_with_material::Object3D_with_material( Object3D_with_material & ref ) : Object3D( ref ) 
{ 
texture = new ObjParam_texref( *ref.texture );  
target = new ObjParam_bool( *ref.target );
spacing = new ObjParam_float( *ref.spacing );
refraction = new ObjParam_bool( *ref.refraction );
reflection = new ObjParam_bool( *ref.reflection );
collect = new ObjParam_bool( *ref.collect );
pass_through = new ObjParam_bool( *ref.pass_through );
}

Object3D_with_material::~Object3D_with_material()
{
delete texture;
delete target;
delete spacing;
delete refraction;
delete reflection;
delete collect;
delete pass_through;
}

void Object3D_with_material::get_texture_widgets( GtkWidget *wid, bool tt )
{
new_table_no_frame( wid, 1 );
texture->get_widget( table, tt, 1 );

//GtkWidget *box = dlg_simple_box_frame( N_("Photons"), wid );
new_table( wid, N_("Photons"),6 );
	target->get_widget( table, tt, 1 );
	spacing->get_widget( table, tt ,2 );
	refraction->get_widget( table, tt, 3 );
	reflection->get_widget( table, tt, 4 );
	collect->get_widget( table, tt, 5 );
	pass_through->get_widget( table, tt, 6 );
}


void Object3D_with_material::destroy_editor() 
{ 
flush(); 
Object3D::destroy_editor(); 
target->clear_widget();
spacing->clear_widget();
refraction->clear_widget();
reflection->clear_widget();
collect->clear_widget();
pass_through->clear_widget();
}

void Object3D_with_material::save_basics( ofstream & file ) 
{ 
Object3D::save_basics( file ); 
texture->save( file ); 
target->save( file ); 
spacing->save( file ); 
refraction->save( file ); 
reflection->save( file ); 
collect->save( file ); 
pass_through->save( file ); 
}

bool  Object3D_with_material::load_basics( ifstream & file, char *tag )
 { 
if ( Object3D::load_basics( file, tag ) ) return true; 
if ( texture->load( file , tag ) )  return true; 
if ( target->load( file , tag ) )  return true; 
if ( spacing->load( file , tag ) )  return true; 
if ( refraction->load( file , tag ) )  return true; 
if ( reflection->load( file , tag ) )  return true; 
if ( collect->load( file , tag ) )  return true; 
if ( pass_through->load( file , tag ) )  return true; 
return false;
}

void Object3D_with_material::output_to_povray_pass1( ofstream & file )
{
Object3D::output_to_povray_pass1( file );

file << "\n\tphotons {";
if ( target->value() ) file << "\n\t\ttarget " << spacing->value();
if ( refraction->value() ) file << "\n\t\trefraction on";
if ( reflection->value() ) file << "\n\t\treflection on";
if ( ! collect->value() ) 	file << "\n\t\tcollect off";
if ( pass_through->value() ) 	file << "\n\t\tpass_through";
file << "\n\t}\n";
texture->output_to_povray( file );
}


void Object3D::push_undo_item()
{
Object3D *copy = duplicate_yourself();
UNDO_DEF
undoman->push( TV_UNDO_OBJ3D_CHANGED, this, copy );
}

void Object3D::help_on_object()
{
char *id = (char*)obj_helpid[ type ];
if ( id == NULL ) 
	{
	app_warning( N_("Sorry no help available for "), obj_names[ type ] );
	return;
	}
truevision_help_display( "truevision.xml",  id, NULL );
}
