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

/* Some <math.h> files do not define M_PI... */
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

//**************************************
// Constructeur
//**************************************
Torus::Torus( app_objs *appref ) : Object3D_with_material( appref )
{
type = TV_OBJ3D_TORUS;
category = TV_OBJ3D_OBJECTS;
set_name( "Torus" );

// Base
location = new ObjParam_point( N_("Location"), "LOC", NULL, app_ref, true );
location->set( 0, 0, 0 );
scale = new ObjParam_scale( N_("Scale"), "SIZE", NULL, app_ref, true );
scale->set( 1, 1, 1 );
minor = new ObjParam_float( N_("Minor radius"), "MINR", NULL, app_ref, true, 0.1 );
minor->set_range( 100, 0, 0.1, 4 );
major = new ObjParam_float( N_("Major radius"), "MAJR", NULL, app_ref, true, 0.4 );
major->set_range( 100, 0, 0.1, 4 );
rotation = new ObjParam_rotation( N_("Rotation"), "ROT", NULL, app_ref, true );
rotation->set( 0, 0, 0 );
sturm = new ObjParam_bool( N_("Sturm"), "STURM", NULL, app_ref, false, false );
}

Torus::Torus( Torus & ref ) : Object3D_with_material( ref )
{
location = new ObjParam_point( *ref.location );
scale = new ObjParam_scale( *ref.scale );
rotation = new ObjParam_rotation( *ref.rotation );
minor = new ObjParam_float( *ref.minor );
major = new ObjParam_float( *ref.major );
sturm = new ObjParam_bool( *ref.sturm );
}

Torus::~Torus()
{
delete location;
delete scale;
delete rotation;
delete minor;
delete major;
delete sturm;
}

//**************************************
// Display
//**************************************
void Torus::display( glview *view, bool set_col )
{
if ( hidden->value() ) return;
if ( location->changed() || scale->changed() || rotation->changed() || minor->changed() || major->changed() )
	list.invalidate();
	
Object3D::display( view );	
if ( set_col ) set_color();
if ( ! list.exec() )
	{
	// mise à zero des parametres
	location->unchange();
	scale->unchange();
	rotation->unchange();
	minor->unchange();
	major->unchange();

	// creation de la liste si necessaire
	list.begin();
	glPushMatrix();

	
	// Position et direction
	gfloat a, b, c;
	location->get( a, b, c );
	glTranslatef( a, b, c );
	scale->get( a, b, c );
	glScalef( a, b, c );
	rotation->gl_rotate();

	PREF_DEF
	int quality = pref->preview_quality->value();

	const int numc = 6 + quality*4;
	const int numt = 20 + quality*6;	
   double twopi = 2 * (double)M_PI;
   for ( int i = 0 ; i < numc ; i++ )
   		{
      	glBegin( GL_QUAD_STRIP );
      	for ( int j = 0 ; j <= numt ;  j++)
      		{
         	for ( int k = 1 ; k >= 0 ; k--)
         		{
           		double s = (i + k) % numc + 0.5;
            	double t = j % numt;
	
				double x = cos(t*twopi/numt) * cos(s*twopi/numc);
				double y = sin(t*twopi/numt) * cos(s*twopi/numc);
				double z = sin(s*twopi/numc);
				glNormal3f(x, y, z);

	          	x = ( major->value() + minor->value() * cos(s*twopi/numc) ) * cos(t*twopi/numt);
            	y =  minor->value() * sin(s * twopi / numc);
            	z = ( major->value()+minor->value() * cos(s*twopi/numc) ) * sin(t*twopi/numt);
            	glVertex3f(x, y, z);
         		}
      		}
   		glEnd();
       }
	glPopMatrix();
	list.end();
	}
}


//***********************************************
// Edit
//***********************************************
void Torus::edit_widget( GtkWidget *wid )
{
PREF_DEF
bool tt = pref->tooltips->value();
// Options communes
Object3D::edit_widget( wid );

// Options de geometrie
new_table( edit_cont, _("General settings"), 6 );
	location->get_widget( table, tt, 1 );
	major->get_widget( table, tt, 2 );
	minor->get_widget( table, tt, 3 );
	scale->get_widget( table, tt, 4 );
	rotation->get_widget( table, tt, 5 );
	sturm->get_widget( table, tt, 6 );

get_texture_widgets( edit_cont, tt );
gtk_widget_show_all( wid );
}

//***********************************************
// Mouse drag
//***********************************************
void Torus::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:
		{ scale->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
//***********************************************
void Torus::pref_changed()
{
Object3D::pref_changed();
location->pref_changed();
scale->pref_changed();
rotation->pref_changed();
minor->pref_changed();
major->pref_changed();
sturm->pref_changed();
}

//***********************************************
// Destroy editor
//***********************************************
void Torus::destroy_editor()
{
Object3D::destroy_editor();
location->clear_widget();
scale->clear_widget();
texture->clear_widget();
rotation->clear_widget();
minor->clear_widget();
major->clear_widget();
sturm->clear_widget();
}

//***********************************************
// Output to povray
//***********************************************
void Torus::output_to_povray_pass1( ofstream & file )
{
file << "\n\n// Torus : " << name->value();
file << "\n#declare "; get_underscore_name( file ); file << " =";

file << "\ntorus {\n\t";
file << major->value() << ", " << minor->value();
Object3D_with_material::output_to_povray_pass1( file );
file << "\t";
rotation->output_to_povray( file );

float x, y, z;
scale->get( x, y, z );
file << "\n\tscale <" << x << ", " << y << ", " << z << ">";

location->get( x, y, z );
file << "\n\ttranslate <" << x << "," << y << "," << -z << "> \n\t";
if ( sturm->value() ) file << "\n\tsturm";
file << "\n}";
}


void Torus::save( ofstream & file )
{
file << "\nTORUS{\n";
save_basics( file );
location->save( file );
scale->save( file );
rotation->save( file );
minor->save( file );
major->save( file );
texture->save( file );
sturm->save( file );
file << "\n}";
}

bool Torus::load( ifstream & file, char *ltag )
{
if ( strcmp( ltag, "TORUS" ) ) 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 ( scale->load( file, tag ) ) continue;
 	if ( rotation->load( file, tag ) ) continue;
 	if ( minor->load( file, tag ) ) continue;
 	if ( major->load( file, tag ) ) continue;
 	if ( sturm->load( file, tag ) ) continue;
		
	tvio_skip_section(file );
	} while ( tag != NULL );

return true;
}
