//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// box.cc
// A simple CSG box object
//
// 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/box.h"
#include "include/viewmanager.h"
#include "include/objectlist.h"
#include "include/tvio.h"
#include "include/preferences.h"

//**************************************
// Constructors
//**************************************
Box::Box( app_objs *appref ) : Object3D_with_material( appref )
{
	type = TV_OBJ3D_BOX;
	category = TV_OBJ3D_OBJECTS;
	set_name( _("Box") );

	// Basic parameters : Geometry and transformation widgets
	location = new ObjParam_point( N_("Translation"), "LOC", _("Translation transformation"), app_ref, true );
	location->set( 0, 0, 0 );
	size = new ObjParam_scale( N_("Scale"), "SIZE", _("Scale transformation"), app_ref, true );
	size->set( 0.2, 0.2, 0.2 );
	rotation = new ObjParam_rotation( N_("Rotation"), "ROT", _("Rotation transformation"), app_ref, true );
	rotation->set( 0, 0, 0 );
	corner1 = new ObjParam_point( N_("Corner 1"), "CORN1", _("First corner of the box coordinates"), app_ref, true );
	corner1->set( 0.5, 0.5, 0.5 );
	corner2 = new ObjParam_point( N_("Corner 2"), "CORN2", _("Second corner of the box coordinates"), app_ref, true );
	corner2->set( -0.5, -0.5, -0.5 );
}


Box::Box( Box & ref ) : Object3D_with_material( ref )
{
	location = new ObjParam_point( *ref.location );
	size = new ObjParam_scale( *ref.size );
	rotation = new ObjParam_rotation( *ref.rotation );
	corner1 = new ObjParam_point( *ref.corner1 );
	corner2 = new ObjParam_point( *ref.corner2 );
}


//**************************************
// Destructor
//**************************************
Box::~Box()
{
	delete location;
	delete size;
	delete rotation;
	delete corner1;
	delete corner2;
}



//**************************************
// Display - virtual function from Object3D
//
// OpenGL calls to draw the box in the preview panels
//**************************************
void Box::display( glview *view, bool set_col )
{
	// If the user doesn't wanna see the box, we don't draw it
	if ( hidden->value() )
		return;
	
	// If one parameter has changed since last call we invalidate the OpenGL display list
	if ( location->changed() || size->changed() || rotation->changed() || corner1->changed() || corner2->changed() )
		list.invalidate();

	// Set General object parameters : color etc...
	Object3D::display( view );	
	if ( set_col ) 
		set_color();
	
	// Call the OpenGL display list or define it if set as invalid
	if ( ! list.exec() ) {
		// Set all parameters as unchanged
		location->unchange();
		size->unchange();
		rotation->unchange();
		corner1->unchange();
		corner2->unchange();

		// Create the display list
		list.begin();
		glPushMatrix();

	
		// retrieve translation, scale and rotation parameters from TvWidgets
		// apply them to OpenGL transformation matrix
		gfloat x, y, z;
		gfloat a, b, c;
		location->get( x, y, z );
		glTranslatef( x, y, z );
		size->get( a, b, c );
		glScalef( a, b, c );
		rotation->gl_rotate();

		// Retrieve box geometry, define vertices and draw quads
		corner1->get( a, b, c );
		corner2->get( x, y, z );
	
		gfloat vertex[24][3] = { {x,y,z}, {x,y,c}, {x,b,c}, {x,b,z},
							 {x,b,z}, {x,b,c}, {a,b,c}, {a,b,z},
							 {a,b,z}, {a,b,c}, {a,y,c}, {a,y,z},
							 {a,y,z}, {a,y,c}, {x,y,c}, {x,y,z},
							 {x,y,z}, {x,b,z}, {a,b,z}, {a,y,z},
							 {a,b,c}, {x,b,c}, {x,y,c}, {a,y,c}	};
		gfloat normal[6][3] = { {-1,0,0}, {0,1,0}, {1,0,0}, {0,-1,0}, {0,0,-1}, {0,0,1} };

		register int i,j;
		glBegin( GL_QUADS );
		for ( i = 0; i < 6 ; i++ ) {
			glNormal3f( normal[i][0], normal[i][1], normal[i][2] );			
			j = i*4;
			glVertex3f( vertex[j][0], vertex[j][1], vertex[j][2] ); j++;
			glVertex3f( vertex[j][0], vertex[j][1], vertex[j][2] ); j++;
			glVertex3f( vertex[j][0], vertex[j][1], vertex[j][2] ); j++;
			glVertex3f( vertex[j][0], vertex[j][1], vertex[j][2] );
		}
		
		// End of OpenGL calls and end of display list
		glEnd();
		glPopMatrix();
		list.end();
	}
}




//***********************************************
// Edit Widget - virtual function from Object3D
//
// Display box parameters in the propertiy panel
// when object is selected
//***********************************************
void Box::edit_widget( GtkWidget *wid )
{
	// Check if we should display tooltips for TvWidgets
	PREF_DEF
	bool tt = pref->tooltips->value();
	
	// General options defined in Object3D.cc
	Object3D::edit_widget( wid );

	// Geometry options
	new_table( edit_cont, N_("Geometry"), 2 );
	corner1->get_widget( table, tt, 1 );
	corner2->get_widget( table, tt, 2 );
	
	// Transformation
	new_table( edit_cont, N_("Transformation"), 3 );
	location->get_widget( table, tt, 1 );
	size->get_widget( table, tt, 2 );	
	rotation->get_widget( table, tt, 3 );

	// Material & photon definitions from Object3D_with_material ( in Object3D.cc )
	get_texture_widgets( edit_cont, tt );

	// Show all this
	gtk_widget_show_all( wid );
}


//***********************************************
// Destroy editor - virtual from Object3D
//
// Destroy the parameter editor from property panel
// when object is deselected
//***********************************************
void Box::destroy_editor()
{
Object3D::destroy_editor();
location->clear_widget();
size->clear_widget();
texture->clear_widget();
rotation->clear_widget();
corner1->clear_widget();
corner2->clear_widget();
}


//***********************************************
// Mouse drag - virtual function from Object3D
//
// Callback for mouse interactions on preview panels
//***********************************************
void Box::mouse_drag( struct drag_info *drag )
{
	VMAN_DEF
	OBJLIST_DEF

	switch( vmanager->get_pointer_mode() ) {
		case TV_PMODE_SELECT:
		case TV_PMODE_TRANSLATE: {
			location->mouse_drag( drag );
			break;
		}

		case TV_PMODE_SCALE: { 
			size->mouse_drag( drag ); 
			break;
		}

		case TV_PMODE_ROTATE: {
			rotation->mouse_drag( drag ); 
			break; 
		}

		case TV_PMODE_CUSTOM: {
			((ObjParam_point*)(objlist->get_current_param()))->mouse_drag( drag );
			break;
		}

		default: 
			break;
	}
}



//***********************************************
// Pref_changed - virtual function from Object3D 
//
// Callback for Preferences change signal
//***********************************************
void Box::pref_changed()
{
	Object3D::pref_changed();
	location->pref_changed();
	size->pref_changed();
	rotation->pref_changed();
	corner1->pref_changed();
	corner2->pref_changed();
}




//***********************************************
// Output to povray - virtual function from Object3D
//
// Output code for povray
// pass1 -> declare object
// pass 2 -> use object
//***********************************************
void Box::output_to_povray_pass1( ofstream & file )
{
	// Header
	file << "\n\n// Box : " << name->value();
	file << "\n#declare "; get_underscore_name( file ); file << " ="; 
	file << "\nbox {\n\t";

	// Geometry
	float a, b, c, x, y, z;
	corner1->get( a, b, c );
	corner2->get( x, y, z );
	file << '<' << a << ',' << b << ',' << -c << ">,\n\t<" << x << ',' << y << ',' << -z << ">\n\t";

	// Material 
	Object3D_with_material::output_to_povray_pass1( file );
	file << "\t";
	
	// Transformation
	rotation->output_to_povray( file );
	size->get( x, y, z );
	file << "\n\tscale <" << x << ',' << y << ',' << z << ">";
	location->get( x, y, z );
	file << "\n\ttranslate <" << x << "," << y << "," << -z << ">";
	file << "\n}  ";
}


//***********************************************
// Save - virtual from Object3D
//
// Save the object to a truevision scene or truevision
// object file
//***********************************************
void Box::save( ofstream & file )
{
	file << "\nBOX{\n";
	save_basics( file );
	corner1->save( file );
	corner2->save( file );
	location->save( file );
	size->save( file );
	rotation->save( file );
	texture->save( file );
	file << "\n}";
}


//***********************************************
// Load - virtual from Object3D
//
// Load the object from a truevision scene or truevision
// object file
//***********************************************
bool Box::load( ifstream & file, char *ltag )
{
	if ( strcmp( ltag, "BOX" ) ) 
		return false;
	set_load_progress( file );

	char * tag = NULL;
	do {
		tag = tvio_get_next_tag( file );
		if ( tag == NULL ) break;
	
		if ( load_basics( file, tag ) ) 
			continue;
 		if ( location->load( file, tag ) ) 
			continue;
 		if ( size->load( file, tag ) ) 
			continue;
 		if ( rotation->load( file, tag ) ) 
			continue;
 		if ( corner1->load( file, tag ) ) 
			continue;
 		if ( corner2->load( file, tag ) ) 
			continue;
	
		tvio_skip_section(file );
		} while 
			( tag != NULL );

	return true;
}
