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

GdkGC *TvWidget_slopemap::grid_style = NULL;
const float MAX_SLOPE = 6.0;


//*********************************************************
// Slope map point
//*********************************************************
slmap_point::slmap_point()
{
position = height = 0;
slope_up = slope_down = 0;
}

slmap_point::slmap_point( slmap_point & ref )
{
position = ref.position;
height = ref.height;
slope_up = ref.slope_up;
slope_down = ref.slope_down;
}


void slmap_point::output_to_povray( ofstream & file )
{
file << "\n\t\t[ " << position << "\t< " << height << ", " << slope_down << " > ]";
file << "\n\t\t[ " << position << "\t< " << height << ", " << slope_up << " > ]";
}

void slmap_point::save( ofstream & file )
{
file << " PT{ P=" << position << " H=" << height << " U=" << slope_up << " D=" << slope_down << "}";
}

void slmap_point::load( ifstream & file )
{
char * val = NULL;
do
	{
	val = tvio_get_next_val( file );
	if ( val == NULL ) return;

	if ( ! strcmp( val, "P" ) ) { position = tvio_get_value_as_float( file ); continue; }
	if ( ! strcmp( val, "H" ) ) { height = tvio_get_value_as_float( file ); continue; }
	if ( ! strcmp( val, "U" ) ) { slope_up = tvio_get_value_as_float( file ); continue; }
	if ( ! strcmp( val, "D" ) ) { slope_down = tvio_get_value_as_float( file ); continue; }
	}
while ( val != NULL );
}


//**********************************************************
// Slopemap
//**********************************************************


// Constructor
TvWidget_slopemap::TvWidget_slopemap( const char *name, const char *sname, const char *tooltip, app_objs *appref ) : TvWidget( name, sname, tooltip, appref )
{
use = new TvWidget_bool_activator( N_("Use slopemap"), "USED", NULL, app_ref, false );
editor = NULL;
pix_buffer = NULL;
update = false;
edited = 0;

slmap_point *pt = new slmap_point();
pt->set( 0, 0.5 );
Points.push_back( pt );

pt = new slmap_point();
pt->set( 0.25, 0.5 );
pt->slope_up = 2;
pt->slope_down = -1;
Points.push_back( pt );

pt = new slmap_point();
pt->set( 0.75, 0.5 );
pt->slope_up = 1;
pt->slope_down = -1;
Points.push_back( pt );

pt = new slmap_point();
pt->set( 1, 0.5 );
Points.push_back( pt );
}


// Copy  Constructor
TvWidget_slopemap::TvWidget_slopemap( TvWidget_slopemap & ref ) : TvWidget( ref )
{
editor = NULL;
pix_buffer = NULL;
update = false;
use = new TvWidget_bool_activator( *ref.use );
for ( unsigned int i = 0 ; i < ref.Points.size() ; i++ )
	{
	slmap_point *pt = new slmap_point( *ref.Points[i] );
	Points.push_back( pt );
	}
}

// Destructor
TvWidget_slopemap::~TvWidget_slopemap()
{
delete use;
for ( unsigned int i = 0 ; i < Points.size() ; i++ ) delete Points[i];
if ( pix_buffer != NULL ) gdk_pixmap_unref( pix_buffer );
}


// Get widget
void TvWidget_slopemap::get_widget_wnframe( GtkWidget *box, bool tt, bool frame )
{
if ( frame ) widget = dlg_simple_box_frame( _(name), box );
else
	{
	widget = gtk_vbox_new( FALSE, 0 );
	gtk_box_pack_start( GTK_BOX(box), widget, FALSE, TRUE, 2 );
	}
use->get_widget_no_toggle( widget, tt );

activated_box = gtk_vbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(widget), activated_box, FALSE, FALSE, 0 );
use->set_target( activated_box );
	GtkWidget *button = gtk_button_new_with_label( _("Edit") );
	gtk_box_pack_start( GTK_BOX(activated_box), button, FALSE, FALSE, 0 );
	gtk_signal_connect( GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(sign_slopemap_edit), this );
use->update_widget();
}


void TvWidget_slopemap::clear_widget()
{
widget = NULL;
use->clear_widget();
}


void TvWidget_slopemap::flush()
{
if ( widget == NULL ) return;
use->flush();
}


void TvWidget_slopemap::pref_changed()
{
PREF_DEF
bool tt = pref->tooltips->value();
	
if ( editor != NULL )
	{
	for ( int i = 0 ; i < MAX_SLMAP_BUT ; i ++ )
		if ( tt ) gtk_tooltips_enable( slmap_butt_tt_wid[i] );
		else gtk_tooltips_disable( slmap_butt_tt_wid[i] );
	}
}


//*********************************
// Editor
//*********************************
const char *slmap_buttons[MAX_SLMAP_BUT] = { GNOME_STOCK_PIXMAP_NEW, GNOME_STOCK_PIXMAP_CLOSE, GNOME_STOCK_PIXMAP_BACK, GNOME_STOCK_PIXMAP_FORWARD };
const char *slmap_tooltips[MAX_SLMAP_BUT] = { N_("Add control point"), N_("Delete current point"), N_("Previous point"), N_("Next point") };

void TvWidget_slopemap::open_editor()
{
if ( GTK_IS_DIALOG(editor) )
	{
	gdk_window_raise( editor->window );
	return;
	}
editor =  gtk_dialog_new_with_buttons( _("Slope map editor"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL );
g_signal_connect( G_OBJECT(editor), "response", G_CALLBACK(sign_slopemap_editor_click), this );
g_signal_connect( G_OBJECT(editor), "close", G_CALLBACK(sign_slopemap_editor_destroy), this );
gtk_widget_realize( editor );
GtkWidget *hbox = gtk_hbox_new( FALSE, 2 );
gtk_box_pack_start( GTK_BOX(GTK_DIALOG(editor)->vbox), hbox, FALSE, TRUE, 5 );


// Curve editor
curve  = gtk_drawing_area_new();
if ( grid_style == NULL )
	{
	grid_style = gdk_gc_new( editor->window );
	GdkColor col;
	col.red = col.green = col.blue = 40000;
	gdk_colormap_alloc_color( gtk_widget_get_colormap( curve ), &col, FALSE, TRUE );
	gdk_gc_set_foreground( grid_style,  &col );
	gdk_gc_set_line_attributes( grid_style, 1,  GDK_LINE_ON_OFF_DASH,   GDK_CAP_BUTT, GDK_JOIN_MITER );
	}
gtk_widget_set_events( curve, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK );
gtk_signal_connect( GTK_OBJECT(curve), "realize", GTK_SIGNAL_FUNC(sign_slopecurve_realize), this );
gtk_signal_connect( GTK_OBJECT(curve), "expose_event", GTK_SIGNAL_FUNC(sign_slopecurve_expose), this );
gtk_signal_connect( GTK_OBJECT(curve), "configure_event", GTK_SIGNAL_FUNC(sign_slopecurve_configure), this );
gtk_signal_connect( GTK_OBJECT(curve), "button_press_event", GTK_SIGNAL_FUNC(sign_slopecurve_mouse_click), this );
gtk_signal_connect( GTK_OBJECT(curve), "button_release_event", GTK_SIGNAL_FUNC(sign_slopecurve_mouse_release), this );
gtk_signal_connect( GTK_OBJECT(curve), "motion_notify_event", GTK_SIGNAL_FUNC(sign_slopecurve_mouse_moved), this );
gtk_widget_set_usize( curve, 550, 370 );
gtk_box_pack_start( GTK_BOX(hbox), curve, TRUE, TRUE, 0 );


// Values Editors
GtkWidget *valframe = gtk_frame_new( NULL );
gtk_box_pack_start( GTK_BOX(hbox), valframe, TRUE, TRUE, 0 );
GtkWidget *vbox = gtk_vbox_new( FALSE, 5 );
gtk_container_set_border_width( GTK_CONTAINER(vbox), 6 );
gtk_widget_set_usize( vbox, 180, -1 );
gtk_container_add( GTK_CONTAINER(valframe), vbox );
GtkWidget *frame = gtk_frame_new( NULL );
gtk_box_pack_start( GTK_BOX(vbox), frame, FALSE, TRUE, 2 );
GtkWidget *label = gtk_label_new( N_("Point editor") );
gtk_container_add( GTK_CONTAINER(frame), label );

// Point number label
GtkWidget *valbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(vbox), valbox, FALSE, TRUE, 10 );
	label = gtk_label_new( N_("Point n° : ") );
	gtk_box_pack_start( GTK_BOX(valbox), label, FALSE, TRUE, 0 );
	point_label = gtk_label_new( N_("0") );
	gtk_box_pack_start( GTK_BOX(valbox), point_label, TRUE, TRUE, 5 );
		
// Position
valbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(vbox), valbox, FALSE, TRUE, 4 );
	label = gtk_label_new( N_("Position") );
	gtk_box_pack_start( GTK_BOX(valbox), label, FALSE, TRUE, 0 );
	posadj = gtk_adjustment_new( 0, 0, 1, 0.1, 0.2, 0 );
	position = gtk_spin_button_new( GTK_ADJUSTMENT(posadj), 0, 4 );
	gtk_box_pack_start( GTK_BOX(valbox), position, TRUE, TRUE, 5 );
	gtk_signal_connect( GTK_OBJECT(posadj), "value-changed", GTK_SIGNAL_FUNC(sign_slmap_value_changed), this );

// Height
valbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(vbox), valbox, FALSE, TRUE, 4 );
	label = gtk_label_new( N_("Height") );
	gtk_box_pack_start( GTK_BOX(valbox), label, FALSE, TRUE, 0 );
	GtkObject *adj = gtk_adjustment_new( 0, 0, 1, 0.1, 0.2, 0 );
	height = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 4 );
	gtk_box_pack_start( GTK_BOX(valbox), height, TRUE, TRUE, 5 );
	gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_slmap_value_changed), this );

// Slope up
valbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(vbox), valbox, FALSE, TRUE, 4 );
	label = gtk_label_new( N_("Slope right") );
	gtk_box_pack_start( GTK_BOX(valbox), label, FALSE, TRUE, 0 );
	adj = gtk_adjustment_new( 0, - MAX_SLOPE, MAX_SLOPE, 0.1, 0.2, 0 );
	slopeup = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 4 );
	gtk_box_pack_start( GTK_BOX(valbox), slopeup, TRUE, TRUE, 5 );
	gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_slmap_value_changed), this );

// Slope down
valbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_start( GTK_BOX(vbox), valbox, FALSE, TRUE, 4 );
	label = gtk_label_new( N_("Slope left") );
	gtk_box_pack_start( GTK_BOX(valbox), label, FALSE, TRUE, 0 );
	adj = gtk_adjustment_new( 0, -MAX_SLOPE, MAX_SLOPE, 0.1, 0.2, 0 );
	slopedown = gtk_spin_button_new( GTK_ADJUSTMENT(adj), 0, 4 );
	gtk_box_pack_start( GTK_BOX(valbox), slopedown, TRUE, TRUE, 5 );
	gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(sign_slmap_value_changed), this );


// Bouttons de manipulation des objets
GtkWidget *bbox = gtk_hbox_new( FALSE, 0 );
gtk_box_pack_end( GTK_BOX(vbox), bbox, FALSE, TRUE, 2 );
//INTERF_DEF
PREF_DEF
//GtkWidget *win = interf->get_gtkwin();
GtkWidget *pix;
for ( int i = 0 ; i < MAX_SLMAP_BUT ; i ++ )
	{
	slmap_butt_wid[i] = gtk_button_new();
	gtk_box_pack_start( GTK_BOX(bbox), slmap_butt_wid[i], TRUE, FALSE, 0 );
	gtk_signal_connect( GTK_OBJECT(slmap_butt_wid[i]), "clicked", GTK_SIGNAL_FUNC(sign_slmap_button_clicked), this );	
	pix = gtk_image_new_from_stock( slmap_buttons[i], GTK_ICON_SIZE_BUTTON );
	gtk_container_add( GTK_CONTAINER(slmap_butt_wid[i]), pix );
	slmap_butt_tt_wid[i] = gtk_tooltips_new();
	gtk_tooltips_set_tip( slmap_butt_tt_wid[i], slmap_butt_wid[i], _(slmap_tooltips[i]), NULL );
	if ( !pref->tooltips->value() ) gtk_tooltips_disable( slmap_butt_tt_wid[i] );
	}
set_edited( edited );
gtk_widget_show_all(editor);
}


void TvWidget_slopemap::close_editor()
{
if ( editor == NULL ) return;
if ( GTK_IS_DIALOG(editor) ) gtk_widget_destroy( editor );
editor = NULL;
if ( pix_buffer != NULL ) gdk_pixmap_unref( pix_buffer );
pix_buffer = NULL;
}


void TvWidget_slopemap::editor_clicked( int button )
{
close_editor();
}


// Points manipulation
void TvWidget_slopemap::button_clicked( GtkWidget *wid )
{
int button = 0;
for ( register int i = 0 ; i < MAX_SLMAP_BUT ; i++ )
	if ( wid == slmap_butt_wid[i] ) { button = i; break; }
	
switch ( button )
	{
	// New point
	case 0:
		{
		if ( edited == (int)Points.size() - 1 ) return;
		vector<slmap_point*>::iterator it = Points.begin() + edited + 1;
		slmap_point *pt = new slmap_point();
		float pos = ( Points[edited]->position + Points[edited+1]->position ) / 2.0;
		float hei = ( Points[edited]->height +  Points[edited+1]->height ) / 2.0;
		pt->set( pos, hei );
		pt->slope_down =( Points[edited]->slope_up + Points[edited+1]->slope_down ) / 2.0;
		pt->slope_up = - pt->slope_down;
		Points.insert( it, pt );
		set_edited( edited+1);
		}
		break;
		
	// Delete point
	case 1:
		{
		if ( edited == 0 ) return;
		if ( edited == (int)Points.size() - 1 ) return;
		vector<slmap_point*>::iterator it = Points.begin() + edited;
		slmap_point *target = Points[edited];
		Points.erase( it );
		delete target;
		set_edited( edited - 1 );
		}
		break;
		
	// Previous point
	case 2:
		set_edited( edited -1 );
		break;
		
	// Next point
	case 3:
		set_edited( edited + 1 );
		break;
	}
}


//******************************
// Set edited point
//******************************
void TvWidget_slopemap::set_edited( int num )
{
if ( num < 0 ) return;
if ( num > (int)Points.size() - 1 ) return;
update = true;
edited = num;

char str[10];
sprintf( str, "%u", edited );
gtk_label_set_text( GTK_LABEL(point_label), str );
gtk_spin_button_set_value( GTK_SPIN_BUTTON(position), Points[edited]->position );
GTK_ADJUSTMENT(posadj)->upper = ( edited == (int)Points.size() - 1 ) ? 1.0 : Points[edited+1]->position;
GTK_ADJUSTMENT(posadj)->lower = ( edited == 0 ) ? 0.0 : Points[edited-1]->position;

gtk_spin_button_set_value( GTK_SPIN_BUTTON(position), Points[edited]->position );
gtk_spin_button_set_value( GTK_SPIN_BUTTON(height), Points[edited]->height );
gtk_spin_button_set_value( GTK_SPIN_BUTTON(slopeup), Points[edited]->slope_up );
gtk_spin_button_set_value( GTK_SPIN_BUTTON(slopedown), Points[edited]->slope_down );
redraw_curve();
update = false;
}


//*********************************
// Value changed
//*********************************
void TvWidget_slopemap::value_changed()
{
if ( update ) return;

Points[edited]->position = gtk_spin_button_get_value_as_float(  GTK_SPIN_BUTTON(position) );
Points[edited]->height = gtk_spin_button_get_value_as_float(  GTK_SPIN_BUTTON(height) );
Points[edited]->slope_up = gtk_spin_button_get_value_as_float(  GTK_SPIN_BUTTON(slopeup) );
Points[edited]->slope_down = gtk_spin_button_get_value_as_float(  GTK_SPIN_BUTTON(slopedown) );
redraw_curve();
}


//*********************************
// Resdraw curve
//*********************************
void TvWidget_slopemap::redraw_curve()
{
GdkRectangle rect;
rect.x = 0; rect.y = 0; rect.width = curve->allocation.width; rect.height = curve->allocation.height;
gtk_widget_draw( curve, &rect );
}


//******************************
// Expose
//******************************
const int margin = 40;
const int step = 10;
const int point_size = 3;
const double slope_lon = 70;

void TvWidget_slopemap::expose( GdkEventExpose *ev )
{
// Geometry
int x = ev->area.x;
int y = ev->area.y;
int width = curve->allocation.width; //ev->area.width;
int height = curve->allocation.height; //ev->area.height;
int area_width = width - margin*2;
int area_height = height - margin * 2;
int left = x + margin;
int right = width - margin;
int top = y + margin;
int bottom = height - margin;

// Double buffer
if ( pix_buffer == NULL )
	pix_buffer = gdk_pixmap_new( curve->window,  width, height, -1 );

// Clean & draw basics
gdk_draw_rectangle( pix_buffer, curve->style->bg_gc[curve->state], TRUE, x, y, width, height );
gdk_draw_rectangle( pix_buffer, curve->style->fg_gc[curve->state], FALSE, left, top, right - margin, bottom - margin );

// Labels
gdk_draw_text( pix_buffer, gtk_style_get_font(curve->style), curve->style->fg_gc[curve->state], left - 10, y + margin, "1", 1 );
gdk_draw_text( pix_buffer, gtk_style_get_font(curve->style), curve->style->fg_gc[curve->state], x + width - margin, y + height - margin + 15, "1", 1 );
gdk_draw_text( pix_buffer, gtk_style_get_font(curve->style), curve->style->fg_gc[curve->state], left, y + height - margin + 15, "0", 1 );
gdk_draw_text( pix_buffer, gtk_style_get_font(curve->style), curve->style->fg_gc[curve->state], left - 10, y + height - margin, "0", 1 );

// Grids
int xinc = area_width / step;
int yinc = area_height / step;
for ( register int i = 1 ; i < step ; i++ )
	{
	int xpos = left + xinc * i;
	int ypos = top + yinc *i;
	gdk_draw_line( pix_buffer, grid_style, xpos, top, xpos , bottom );
	gdk_draw_line( pix_buffer, grid_style, left, ypos, right , ypos );
	}
	
// Points
int size = point_size*2;
for ( unsigned int i = 0 ; i < Points.size() ; i ++ )
	{
	int xpos = left + (int)((float)area_width * Points[i]->position);
	int ypos = top + (int)((float)area_height * ( 1.0 - Points[i]->height ));
	gdk_draw_rectangle( pix_buffer, curve->style->fg_gc[curve->state], TRUE, xpos-point_size, ypos-point_size, size, size );
	
	if ( i == (unsigned int)edited )
		{
		double slopeu = atan(  Points[i]->slope_up );
		double sloped = atan( Points[i]->slope_down );
		
		if ( edited != (int)Points.size() - 1 )
			{
			gdk_draw_rectangle( pix_buffer, curve->style->fg_gc[curve->state], FALSE, xpos - point_size + (int)(slope_lon*cos( slopeu ) ),
					ypos - point_size + (int)( slope_lon * sin( slopeu ) ), size, size );		
			gdk_draw_line( pix_buffer,  curve->style->fg_gc[curve->state],  xpos, ypos,
				xpos + (int)(slope_lon * cos( slopeu )),      ypos + (int)(slope_lon * sin( slopeu )) );			
			}
			
		if ( edited != 0 )
			{			
			gdk_draw_line( pix_buffer,  curve->style->fg_gc[curve->state], xpos, ypos,
				xpos - (int)(slope_lon * cos( sloped )),  ypos + (int)(slope_lon * sin( sloped )) );		
			gdk_draw_rectangle( pix_buffer, curve->style->fg_gc[curve->state], FALSE, xpos - point_size - (int)( slope_lon * cos( sloped) ),
				ypos - point_size + (int)( slope_lon * sin( sloped)), size, size );		
			}
		}
	
	// Bezier curves
	if ( i == Points.size() -1 ) break;
	int xpos2 = left + (int)((float)area_width * Points[i+1]->position);
	int ypos2 = top + (int)((float)area_height * ( 1.0 - Points[i+1]->height ));
	int segment = xpos2 - xpos;
	int xslope1= xpos + segment/2;
	int yslope1= ypos + (int)((float)(segment/2)*Points[i]->slope_up);
	int xslope2= xpos2- segment/2;
	int yslope2= ypos2 + (int)((float)(segment/2)*Points[i+1]->slope_down);
	int old_xcurve = xpos;
	int old_ycurve = ypos;
	for ( register int i = 0 ; i < segment ; i++ )
		{
		float t = (float)i / segment;
		
		int abx = xpos + (int)((float)( xslope1 - xpos ) * t);
		int aby = ypos + (int)((float)( yslope1 - ypos ) * t);
		int bcx = xslope1 + (int)((float)( xslope2 - xslope1 ) * t);
		int bcy = yslope1 + (int)((float)( yslope2 - yslope1 ) * t);
		int cdx = xslope2 + (int)((float)( xpos2 - xslope2 ) * t);
		int cdy = yslope2 + (int)((float)( ypos2 - yslope2 ) * t);
		int abbcx = abx + (int)((float)( bcx - abx ) * t);
		int abbcy = aby + (int)((float)( bcy - aby ) * t);
		int bccdx = bcx + (int)((float)( cdx - bcx ) * t);
		int bccdy = bcy + (int)((float)( cdy - bcy ) * t);
		
		int xcurve = abbcx + (int)((float)( bccdx - abbcx ) * t);
		int ycurve = abbcy + (int)((float)( bccdy - abbcy ) * t);		

		gdk_draw_line( pix_buffer,  curve->style->fg_gc[curve->state], old_xcurve, old_ycurve, xcurve, ycurve );
		old_xcurve = xcurve;
		old_ycurve = ycurve;
		}
	gdk_draw_line( pix_buffer,  curve->style->fg_gc[curve->state], old_xcurve, old_ycurve, xpos2, ypos2 );		
	}

gdk_gc_set_clip_rectangle( curve->style->fg_gc[curve->state], &ev->area );
gdk_draw_pixmap( curve->window, curve->style->fg_gc[curve->state], pix_buffer, ev->area.x, ev->area.y, ev->area.x, ev->area.y, ev->area.width, ev->area.height );
gdk_gc_set_clip_rectangle( curve->style->fg_gc[curve->state], NULL );		
}




void TvWidget_slopemap::configure()  {}
void TvWidget_slopemap::realize()  {}


//**************************************
// Mouse
//**************************************
void TvWidget_slopemap::mouse_moved( GdkEventMotion *ev )
{
int xpos, ypos;
GdkModifierType state;

if ( ev->is_hint ) gdk_window_get_pointer( ev->window, &xpos, &ypos, &state );
else {
	xpos = (int)ev->x;
	ypos = (int)ev->y;
	state = (GdkModifierType)ev->state;
	}
int width = curve->allocation.width; //ev->area.width;
int height = curve->allocation.height; //ev->area.height;
int area_width = width - margin*2;
int area_height = height - margin * 2;
int left = margin;
//int right = width - margin;
int top = margin;
//int bottom = height - margin;


switch ( drag_type )
	{
	default:
	case -1:
		return;
		break;
		
	// Move
	case 1:
		{
		float position  = (float)( xpos - left ) / (float)area_width;
		if ( edited == 0 ) position = 0;
		else   if ( position < Points[edited-1]->position ) position =  Points[edited-1]->position;
		if ( edited == (int)Points.size() - 1 ) position = 1.0;
		else if ( position > Points[edited+1]->position ) position =  Points[edited+1]->position;	
		float height = 1.0 - (float)( ypos - top ) / (float)area_height;
		if ( height < 0.0 ) height = 0.0;
		if ( height > 1.0 ) height = 1.0;
		Points[edited]->set( position, height );
		set_edited( edited );
		}
		break;
		
	// Edit slope up
	case 2:
		{
		int xori = left + (int)((float)area_width * Points[edited]->position);
		int yori = top + (int)((float)area_height * ( 1.0 - Points[edited]->height ));
		float xslope = xpos - xori;
		if ( xslope < 0 ) break;
		float yslope = ypos - yori;
		Points[edited]->slope_up = yslope / xslope;
		if ( Points[edited]->slope_up > MAX_SLOPE )    Points[edited]->slope_up =  MAX_SLOPE;
		if ( Points[edited]->slope_up < - MAX_SLOPE )  Points[edited]->slope_up =  - MAX_SLOPE;
		set_edited( edited );
		}
		break;
	
	// Edit slope down
	case 3:
		{
		int xori = left + (int)((float)area_width * Points[edited]->position);
		int yori = top + (int)((float)area_height * ( 1.0 - Points[edited]->height ));
		float xslope = - xpos + xori;
		if ( xslope < 0 ) break;
		float yslope = ypos - yori;
		Points[edited]->slope_down = yslope / xslope;
		if ( Points[edited]->slope_down > MAX_SLOPE )    Points[edited]->slope_down =  MAX_SLOPE;
		if ( Points[edited]->slope_down < - MAX_SLOPE )  Points[edited]->slope_down =  - MAX_SLOPE;
		set_edited( edited );
		}
		break;
	}
}



// Click
void TvWidget_slopemap::mouse_click( GdkEventButton *ev )
{
drag_type = -1;
if ( ev->button != 1 ) return;
GdkModifierType state;
int drag_x, drag_y;
gdk_window_get_pointer( ev->window, &drag_x, &drag_y, &state );
int width = curve->allocation.width; //ev->area.width;
int height = curve->allocation.height; //ev->area.height;
int area_width = width - margin*2;
int area_height = height - margin * 2;
int left = margin;
//int right = width - margin;
int top = margin;
//int bottom = height - margin;

// Position test ( for selection + move )
for ( unsigned int i = 0 ; i < Points.size() ; i++ )
	{
	int xpos = left + (int)((float)area_width * Points[i]->position);
	if ( drag_x > xpos + point_size ) continue;
	if ( drag_x < xpos  - point_size ) continue;
	int ypos = top + (int)((float)area_height * ( 1.0 - Points[i]->height ));
	if ( drag_y > ypos + point_size ) continue;
	if ( drag_y < ypos  - point_size ) continue;
	drag_type = 1;
	set_edited( i );
	return;
	}

// Slope cursor position test
double slopeu = atan(  Points[edited]->slope_up );
double sloped = atan( Points[edited]->slope_down );
if ( edited != (int)Points.size() - 1 )
	{
	bool test = true;
	int xpos = left + (int)((float)area_width * Points[edited]->position) + (int)(slope_lon * cos( slopeu ));
	if ( drag_x > xpos + point_size ) test = false;
	if ( drag_x < xpos  - point_size ) test = false;
	int ypos = 	 top + (int)((float)area_height * ( 1.0 - Points[edited]->height )) + (int)(slope_lon * sin( slopeu ));
	if ( drag_y > ypos + point_size ) test = false;
	if ( drag_y < ypos  - point_size ) test = false;
	if ( test ) { drag_type = 2;	return; }
	}
if ( edited != 0 )
	{
	bool test = true;
	int xpos = left + (int)((float)area_width * Points[edited]->position) - (int)(slope_lon * cos( sloped ));
	if ( drag_x > xpos + point_size ) test = false;
	if ( drag_x < xpos  - point_size ) test = false;
	int ypos = 	 top + (int)((float)area_height * ( 1.0 - Points[edited]->height )) + (int)(slope_lon * sin( sloped ));
	if ( drag_y > ypos + point_size ) test = false;
	if ( drag_y < ypos  - point_size ) test = false;
	if ( test ) { drag_type = 3;	return; }
	}		
	
// Else create a new point
float pos  = (float)( drag_x - left ) / (float)area_width;
float hei = 1.0 - (float)( drag_y - top ) / (float)area_height;
if ( hei < 0.0 ) hei = 0.0;
if ( hei > 1.0 ) hei = 1.0;
slmap_point *pt = new slmap_point();
pt->set( pos, hei );		
unsigned int i = 0;
for ( i = 0 ; i < Points.size() ; i++ )
	if ( Points[i]->position > pos ) break;
vector<slmap_point*>::iterator it = Points.begin() + i;	
Points.insert( it, pt );
set_edited(i);
drag_type = 1;
}


void TvWidget_slopemap::mouse_release()
{
drag_type = -1;
}


//*******************
// output to povray
//*******************
void TvWidget_slopemap::output_to_povray(  ofstream & file )
{
if ( use->value() == false ) return;
file << "\n\tslope_map  {";
for ( unsigned int i = 0 ; i < Points.size() ; i++ )
	Points[i]->output_to_povray( file );
file << "\n\t}";
}


//*******************
// Save
//*******************
void TvWidget_slopemap::save(  ofstream & file )
{
TvWidget::save( file );
use->save( file );
for ( unsigned int i = 0 ; i < Points.size() ; i ++ )
	Points[i]->save( file );
file << "} ";
}


bool TvWidget_slopemap::load( ifstream & file, char * ltag )
{
if ( strcmp( sname, ltag ) ) return false;
for ( unsigned int i = 0 ; i < Points.size() ; i ++ ) delete Points[i];
Points.clear();
edited = 0;
char * tag = NULL;
do
	{
	tag = tvio_get_next_tag( file );
	if ( tag == NULL ) return true;
	if ( use->load( file, tag ) ) continue;
	
	if ( ! strcmp( tag, "PT" ) )
		{
		slmap_point *pt = new slmap_point();
		pt->load( file );
		Points.push_back( pt );
		continue;
		}
		
	tvio_skip_section( file );
	}
while( tag != NULL );
return true;
}

//************************************
// COPY
//************************************
void TvWidget_slopemap::copy( TvWidget *smap )
{
TvWidget_slopemap *slope = (TvWidget_slopemap*)smap;
editor = NULL;
pix_buffer = NULL;
edited = slope->edited;
use->copy( slope->use );
for ( unsigned int i = 0 ; i < Points.size() ; i++ ) delete Points[i];
Points.clear();
for ( unsigned int i = 0 ; i < slope->Points.size() ; i++ )
	{
	slmap_point *pt = new slmap_point( *slope->Points[i] );
	Points.push_back( pt );
	}
}
