/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)
  
	Adresse mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea P fr.

	Ce logiciel est un programme informatique servant à visualiser des
	structures atomiques dans un rendu pseudo-3D. 

	Ce logiciel est régi par la licence CeCILL soumise au droit français et
	respectant les principes de diffusion des logiciels libres. Vous pouvez
	utiliser, modifier et/ou redistribuer ce programme sous les conditions
	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
	sur le site "http://www.cecill.info".

	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
	pris connaissance de la licence CeCILL, et que vous en avez accepté les
	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
	Copyright CEA, contributors : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)

	E-mail address:
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.

	This software is a computer program whose purpose is to visualize atomic
	configurations in 3D.

	This software is governed by the CeCILL  license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/

#include <gtk/gtk.h>

#include <math.h>

#include "gtk_orientationChooser.h"

#include <support.h>
#include <visu_tools.h>
#include <visu_data.h>
#include <visu_gtk.h>

#include <coreTools/toolMatrix.h>

/**
 * SECTION:gtk_orientationChooser
 * @short_description: Defines a dialog widget to choose the camera position.
 * @include: coreTools/toolMatrix.h
 * 
 * <para>This widget is a #GtkDialog window that can be used to choose
 * an orientation for the camera, using either the cartesian
 * coordinates, the box coordinates or the spherical coordinates.</para>
 */

enum {
  VALUES_CHANGED,
  LAST_SIGNAL
};

typedef enum
  {
    visu_ui_orientation_chooser_ortho,
    visu_ui_orientation_chooser_box,
    visu_ui_orientation_chooser_angles
  } VisuUiOrientationChooserBasis;

/**
 * VisuUiOrientationChooser:
 *
 * Short form for a #VisuUiOrientationChooser_struct structure.
 */
struct _VisuUiOrientationChooser
{
  GtkDialog dialog;

  /* The kind of chooser in box coordinates. */
  VisuUiOrientationChooserKind kind;

  /* The spins for the ortho selection. */
  GtkWidget *spinsOrtho[3];
  gulong signalsOrtho[3];

  /* The spins for the box selection. */
  GtkWidget *hboxBox;
  GtkWidget *spinsBox[3];
  gulong signalsBox[3];

  /* The spins for the angle selection. */
  GtkWidget *spinsAngles[2];
  gulong signalsAngles[2];

  /* The check box for live update. */
  GtkWidget *checkLiveUpdate;

  /* Associated VisuBox, if any. */
  VisuBoxed *boxed;
  gulong box_sig;
  VisuBox *box;
  gulong size_sig;
  /* Transformation matrix associated to the box. */
  float boxToOrtho[3][3];
  float orthoToBox[3][3];

  /* Memory gestion. */
  gboolean dispose_has_run;
};
/**
 * VisuUiOrientationChooserClass:
 *
 * Opaque structure.
 */
struct _VisuUiOrientationChooserClass
{
  GtkDialogClass parent_class;

  void (*orientationChooser) (VisuUiOrientationChooser *orientation);
};

/**
 * visu_ui_orientation_chooser_get_type
 *
 * #GType are unique numbers to identify objects.
 *
 * Returns: the #GType associated with #VisuUiOrientationChooser objects.
 */
G_DEFINE_TYPE(VisuUiOrientationChooser, visu_ui_orientation_chooser, GTK_TYPE_DIALOG)

/* Local variables. */
static guint visu_ui_orientation_chooser_signals[LAST_SIGNAL] = { 0 };

/* Local methods. */
static void visu_ui_orientation_chooser_class_init(VisuUiOrientationChooserClass *klass);
static void visu_ui_orientation_chooser_init(VisuUiOrientationChooser *orientationChooser);
static void visu_ui_orientation_chooser_dispose(GObject *orientationChooser);
static void visu_ui_orientation_chooser_finalize(GObject *obj);
static void orientationChanged(VisuUiOrientationChooser *orientation,
			       VisuUiOrientationChooserBasis changedBasis);
static void _updateBox(VisuUiOrientationChooser *orientation);

/* Local callbacks. */
static void onBox(VisuBoxed *boxed, VisuBox *box, gpointer data);
static void onBoxSize(VisuBox *box, float extens, gpointer data);
static void onResponse(VisuUiOrientationChooser *chooser, gint response, gpointer data);
static void onOrthoChanged(GtkSpinButton *spin, gpointer data);
static void onBoxChanged(GtkSpinButton *spin, gpointer data);
static void onAnglesChanged(GtkSpinButton *spin, gpointer data);

static void visu_ui_orientation_chooser_class_init(VisuUiOrientationChooserClass *klass)
{
  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: creating the class of the widget.\n");

  DBG_fprintf(stderr, "                     - adding new signals ;\n");
  /**
   * VisuUiOrientationChooser::values-changed:
   * @chooser: the #VisuUiOrientationChooser that emits the signal.
   *
   * This signal is emitted when the values are changed and when the
   * live update checkbox is active.
   *
   * Since: 3.4
   */
  visu_ui_orientation_chooser_signals[VALUES_CHANGED] =
    g_signal_new ("values-changed",
		  G_TYPE_FROM_CLASS(klass),
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET(VisuUiOrientationChooserClass, orientationChooser),
		  NULL, 
		  NULL,                
		  g_cclosure_marshal_VOID__VOID,
		  G_TYPE_NONE, 0, NULL);

  /* Connect freeing methods. */
  G_OBJECT_CLASS(klass)->dispose = visu_ui_orientation_chooser_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_ui_orientation_chooser_finalize;
}

/* This method can be called several times.
   It should unref all of its reference to
   GObjects. */
static void visu_ui_orientation_chooser_dispose(GObject *orientationChooser)
{
  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: dispose object %p.\n",
	      (gpointer)orientationChooser);

  if (VISU_UI_ORIENTATION_CHOOSER(orientationChooser)->dispose_has_run)
    return;

  VISU_UI_ORIENTATION_CHOOSER(orientationChooser)->dispose_has_run = TRUE;
  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_ui_orientation_chooser_parent_class)->dispose(orientationChooser);
}
/* This method is called once only. */
static void visu_ui_orientation_chooser_finalize(GObject *obj)
{
  VisuUiOrientationChooser *orientationChooser;

  g_return_if_fail(obj);

  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: finalize object %p.\n",
	      (gpointer)obj);

  orientationChooser = VISU_UI_ORIENTATION_CHOOSER(obj);
  if (orientationChooser->box)
    {
      g_signal_handler_disconnect(G_OBJECT(orientationChooser->box), orientationChooser->size_sig);
      g_object_unref(orientationChooser->box);
    }
  if (orientationChooser->boxed)
    {
      g_signal_handler_disconnect(G_OBJECT(orientationChooser->boxed), orientationChooser->box_sig);
      g_object_unref(orientationChooser->boxed);
    }

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_ui_orientation_chooser_parent_class)->finalize(obj);

  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: freeing ... OK.\n");
}


static void visu_ui_orientation_chooser_init(VisuUiOrientationChooser *orientationChooser)
{
  int i;

  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: initializing new object (%p).\n",
	      (gpointer)orientationChooser);

  orientationChooser->dispose_has_run = FALSE;

  orientationChooser->kind = VISU_UI_ORIENTATION_DIRECTION;

  orientationChooser->boxed = (VisuBoxed*)0;
  orientationChooser->box_sig = 0;
  orientationChooser->box = (VisuBox*)0;
  orientationChooser->size_sig = 0;

  for (i = 0; i < 3; i++)
    {
      orientationChooser->spinsOrtho[i] =
	gtk_spin_button_new_with_range(-1000, 1000, 1);
      gtk_spin_button_set_value
	(GTK_SPIN_BUTTON(orientationChooser->spinsOrtho[i]), 0);
      gtk_spin_button_set_digits
	(GTK_SPIN_BUTTON(orientationChooser->spinsOrtho[i]), 5);
      orientationChooser->signalsOrtho[i] = 
	g_signal_connect(G_OBJECT(orientationChooser->spinsOrtho[i]), "value-changed",
			 G_CALLBACK(onOrthoChanged), orientationChooser);
    }
  orientationChooser->hboxBox = gtk_hbox_new(FALSE, 0);
  for (i = 0; i < 3; i++)
    {
      orientationChooser->spinsBox[i] =
	gtk_spin_button_new_with_range(-1000, 1000, 1);
      gtk_spin_button_set_value
	(GTK_SPIN_BUTTON(orientationChooser->spinsBox[i]), 0);
      gtk_spin_button_set_digits
	(GTK_SPIN_BUTTON(orientationChooser->spinsBox[i]), 5);
      orientationChooser->signalsBox[i] = 
	g_signal_connect(G_OBJECT(orientationChooser->spinsBox[i]), "value-changed",
			 G_CALLBACK(onBoxChanged), orientationChooser);
    }
  for (i = 0; i < 2; i++)
    {
      orientationChooser->spinsAngles[i] =
	gtk_spin_button_new_with_range(-180, 180, 1);
      gtk_spin_button_set_value
	(GTK_SPIN_BUTTON(orientationChooser->spinsAngles[i]), 0);
      gtk_spin_button_set_digits
	(GTK_SPIN_BUTTON(orientationChooser->spinsAngles[i]), 5);
      orientationChooser->signalsAngles[i] = 
	g_signal_connect(G_OBJECT(orientationChooser->spinsAngles[i]), "value-changed",
			 G_CALLBACK(onAnglesChanged), orientationChooser);
    }
  orientationChooser->checkLiveUpdate =
    gtk_check_button_new_with_label(_("Update values on the fly."));

  /* Connect a signal when the dialog is closed. */
  g_signal_connect(G_OBJECT(orientationChooser), "response",
		   G_CALLBACK(onResponse), (gpointer)0);
}

/**
 * visu_ui_orientation_chooser_new:
 * @kind: to set the box coordinates behavior ;
 * @liveUpdate: raise "values-changed" when a value is changed ;
 * @boxed: (allow-none): the associated #VisuData to get the box
 * definition (can be NULL);
 * @parent: (allow-none): give the parent window to set the modal
 * status and the position.
 *
 * Create a dialog box with three choices to choose a direction in space: the
 * classical orthogonal basis set, the spherical one or the basis set linked
 * to the box. If the @data argument is NULL, this last possibility is made
 * unsensitive. If the kind is set to VISU_UI_ORIENTATION_DIRECTION, the orthogonal
 * coordinates correspond exactly to the box coordinates (after transformation) ;
 * whereas @kind is VISU_UI_ORIENTATION_NORMAL, the cartesian coordinates are those
 * which give the right normal plane to the direction given in the box coordinates.
 *
 * Returns: (transfer full): a newly created object.
 */
GtkWidget* visu_ui_orientation_chooser_new(VisuUiOrientationChooserKind kind,
                                           gboolean liveUpdate,
                                           VisuBoxed *boxed, GtkWindow *parent)
{
  VisuUiOrientationChooser *orientationChooser;
  GtkWidget *wd, *hbox, *hbox2, *vbox;
  int i;
  gchar *axesNames[3] = {"x:", "y:", "z:"};
  gchar *anglesNames[2] = {"theta:", "phi:"};

  orientationChooser = VISU_UI_ORIENTATION_CHOOSER(g_object_new(visu_ui_orientation_chooser_get_type(), NULL));

  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: creating new object: %p"
	      " with parent %p.\n", (gpointer)orientationChooser, (gpointer)parent);

  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(orientationChooser->checkLiveUpdate),
			       liveUpdate);

  gtk_dialog_add_buttons(GTK_DIALOG(orientationChooser),
			 TOOL_ICON_CANCEL, GTK_RESPONSE_CANCEL,
			 TOOL_ICON_CLOSE, GTK_RESPONSE_ACCEPT, NULL);
  gtk_dialog_set_default_response(GTK_DIALOG(orientationChooser),
				  GTK_RESPONSE_ACCEPT);
/*   gtk_window_set_type_hint(GTK_WINDOW(orientationChooser), */
/* 			   GDK_WINDOW_TYPE_HINT_UTILITY); */
  gtk_window_set_skip_pager_hint(GTK_WINDOW(orientationChooser), TRUE);
  if (!parent)
    parent = visu_ui_getPanel();
  gtk_window_set_transient_for(GTK_WINDOW(orientationChooser), parent);

  /* Title. */
  wd = gtk_label_new("");
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_label_set_markup(GTK_LABEL(wd),
		       _("<span size=\"larger\">Choose an orientation</span>"));
  gtk_widget_set_name(wd, "label_head_2");
  gtk_box_pack_start
    (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(orientationChooser))),
     wd, FALSE, FALSE, 5);

  /* Ortho orientation. */
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start
    (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(orientationChooser))),
     hbox, FALSE, FALSE, 3);
  wd = create_pixmap((GtkWidget*)0, "axes-ortho.png");
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  vbox = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
  wd = gtk_label_new("");
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_label_set_markup(GTK_LABEL(wd), _("<b>On an orthonormal basis set</b>"));
  gtk_label_set_xalign(GTK_LABEL(wd), 0.1);
  gtk_box_pack_start(GTK_BOX(vbox), wd, TRUE, TRUE, 15);
  hbox2 = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, 0);
  for (i = 0; i < 3; i++)
    {
      wd = gtk_label_new(axesNames[i]);
      gtk_label_set_xalign(GTK_LABEL(wd), 1.);
      gtk_box_pack_start(GTK_BOX(hbox2), wd, TRUE, TRUE, 2);
      gtk_box_pack_start(GTK_BOX(hbox2), orientationChooser->spinsOrtho[i], FALSE, FALSE, 0);
    }

  /* Box orientation. */
  gtk_widget_set_sensitive(orientationChooser->hboxBox, boxed &&
			   visu_box_getBoundary(visu_boxed_getBox(boxed)) != VISU_BOX_FREE);
  gtk_box_pack_start(GTK_BOX
		     (gtk_dialog_get_content_area(GTK_DIALOG(orientationChooser))),
		     orientationChooser->hboxBox, FALSE, FALSE, 3);
  wd = create_pixmap((GtkWidget*)0, "axes-box.png");
  gtk_box_pack_start(GTK_BOX(orientationChooser->hboxBox), wd, FALSE, FALSE, 0);
  vbox = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(orientationChooser->hboxBox), vbox, TRUE, TRUE, 0);
  wd = gtk_label_new("");
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_label_set_markup(GTK_LABEL(wd), _("<b>Following the box basis set</b>"));
  gtk_label_set_xalign(GTK_LABEL(wd), 0.1);
  gtk_box_pack_start(GTK_BOX(vbox), wd, TRUE, TRUE, 7);
  hbox2 = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, 0);
  for (i = 0; i < 3; i++)
    {
      wd = gtk_label_new(axesNames[i]);
      gtk_label_set_xalign(GTK_LABEL(wd), 1.);
      gtk_box_pack_start(GTK_BOX(hbox2), wd, TRUE, TRUE, 2);
      gtk_box_pack_start(GTK_BOX(hbox2), orientationChooser->spinsBox[i], FALSE, FALSE, 0);
    }

  /* Ortho orientation. */
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX
		     (gtk_dialog_get_content_area(GTK_DIALOG(orientationChooser))),
		     hbox, FALSE, FALSE, 3);
  wd = create_pixmap((GtkWidget*)0, "axes-angles.png");
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  vbox = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
  wd = gtk_label_new("");
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_label_set_markup(GTK_LABEL(wd), _("<b>On a spherical basis set</b>"));
  gtk_label_set_xalign(GTK_LABEL(wd), 0.1);
  gtk_box_pack_start(GTK_BOX(vbox), wd, TRUE, TRUE, 15);
  hbox2 = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, 0);
  for (i = 0; i < 2; i++)
    {
      wd = gtk_label_new(anglesNames[i]);
      gtk_label_set_xalign(GTK_LABEL(wd), 1.);
      gtk_box_pack_start(GTK_BOX(hbox2), wd, TRUE, TRUE, 2);
      gtk_box_pack_start(GTK_BOX(hbox2), orientationChooser->spinsAngles[i], FALSE, FALSE, 0);
    }

  /* The live update checkbox. */
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX
		     (gtk_dialog_get_content_area(GTK_DIALOG(orientationChooser))),
		     hbox, FALSE, FALSE, 5);
  gtk_box_pack_start(GTK_BOX(hbox), orientationChooser->checkLiveUpdate,
		     FALSE, FALSE, 90);

  gtk_widget_show_all(GTK_WIDGET(orientationChooser));

  /* Set other internal values. */
  orientationChooser->kind = kind;

  if (boxed)
    {
      orientationChooser->boxed = boxed;
      g_object_ref(G_OBJECT(boxed));
      orientationChooser->box_sig = g_signal_connect
        (G_OBJECT(boxed), "setBox", G_CALLBACK(onBox), (gpointer)orientationChooser);
      onBox(boxed, visu_boxed_getBox(boxed), (gpointer)orientationChooser);
    }

  return GTK_WIDGET(orientationChooser);
}

static void onBox(VisuBoxed *boxed _U_, VisuBox *box, gpointer data)
{
  VisuUiOrientationChooser *chooser = VISU_UI_ORIENTATION_CHOOSER(data);

  if (chooser->box)
    {
      g_signal_handler_disconnect(G_OBJECT(chooser->box), chooser->size_sig);
      g_object_unref(G_OBJECT(chooser->box));
    }
  chooser->box = box;
  if (chooser->box)
    {
      g_object_ref(G_OBJECT(chooser->box));
      chooser->size_sig = g_signal_connect(G_OBJECT(chooser->box), "SizeChanged",
                                           G_CALLBACK(onBoxSize), data);
      _updateBox(chooser);
    }
}

static void onBoxSize(VisuBox *box _U_, float extens _U_, gpointer data)
{
  /* We recompute the transformation matrix. */
  _updateBox(VISU_UI_ORIENTATION_CHOOSER(data));
}

static void _updateBox(VisuUiOrientationChooser *orientation)
{
  float tmpCoord[3], norm;
  float boxMatrix[3][3];
  int i, j;

  g_return_if_fail(VISU_IS_UI_ORIENTATION_CHOOSER(orientation));

  /* Set or unset the box coordinate choice. */
  gtk_widget_set_sensitive(orientation->hboxBox, orientation->box &&
			   visu_box_getBoundary(orientation->box) != VISU_BOX_FREE);

  if (!orientation->box)
    return;

  /* Change the transformation matrix. */
  for (j = 0; j < 3; j++)
    {
      tmpCoord[0] = (j == 0)?1.:0.;
      tmpCoord[1] = (j == 1)?1.:0.;
      tmpCoord[2] = (j == 2)?1.:0.;
      visu_box_convertBoxCoordinatestoXYZ(orientation->box, boxMatrix[j], tmpCoord);
    }
  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: get the box matrix.\n");
  DBG_fprintf(stderr, " | %8.4f %8.4f %8.4f\n",
	      boxMatrix[0][0], boxMatrix[1][0], boxMatrix[2][0]);
  DBG_fprintf(stderr, " | %8.4f %8.4f %8.4f\n",
	      boxMatrix[0][1], boxMatrix[1][1], boxMatrix[2][1]);
  DBG_fprintf(stderr, " | %8.4f %8.4f %8.4f\n",
	      boxMatrix[0][2], boxMatrix[1][2], boxMatrix[2][2]);
  /* We create a matrix to transform the box coordinates to
     cartesian values keeping the orthogonality. */
  for (i = 0; i < 3; i++)
    {
      norm = 0.;
      for (j = 0; j < 3; j++)
	{
	  orientation->boxToOrtho[j][i] =
	    boxMatrix[(i + 1)%3][(j + 1)%3] *
	    boxMatrix[(i + 2)%3][(j + 2)%3] -
	    boxMatrix[(i + 1)%3][(j + 2)%3] *
	    boxMatrix[(i + 2)%3][(j + 1)%3];
	  norm += orientation->boxToOrtho[j][i] * orientation->boxToOrtho[j][i];
	}
      /* We normalise the tranformation matrix. */
      norm = sqrt(norm);
      for (j = 0; j < 3; j++)
	orientation->boxToOrtho[j][i] /= norm;
    }
  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: normal case, boxToOrtho matrix.\n");
  DBG_fprintf(stderr, " | %8.4f %8.4f %8.4f\n", orientation->boxToOrtho[0][0],
	      orientation->boxToOrtho[0][1], orientation->boxToOrtho[0][2]);
  DBG_fprintf(stderr, " | %8.4f %8.4f %8.4f\n", orientation->boxToOrtho[1][0],
	      orientation->boxToOrtho[1][1], orientation->boxToOrtho[1][2]);
  DBG_fprintf(stderr, " | %8.4f %8.4f %8.4f\n", orientation->boxToOrtho[2][0],
	      orientation->boxToOrtho[2][1], orientation->boxToOrtho[2][2]);
  /* We inverse using the trick that the matrix is down (upper side
     is zeros. */
  orientation->orthoToBox[0][0] = 1. / orientation->boxToOrtho[0][0];
  orientation->orthoToBox[1][0] = - orientation->boxToOrtho[1][0] / 
    orientation->boxToOrtho[1][1] / orientation->boxToOrtho[0][0];
  orientation->orthoToBox[2][0] = 
    (orientation->boxToOrtho[1][0] * orientation->boxToOrtho[2][1] -
     orientation->boxToOrtho[1][1] * orientation->boxToOrtho[2][0]) /
    orientation->boxToOrtho[0][0] /
    orientation->boxToOrtho[1][1] /
    orientation->boxToOrtho[2][2];
  orientation->orthoToBox[0][1] = 0.;
  orientation->orthoToBox[1][1] = 1. / orientation->boxToOrtho[1][1];
  orientation->orthoToBox[2][1] =  - orientation->boxToOrtho[2][1] / 
    orientation->boxToOrtho[1][1] / orientation->boxToOrtho[2][2];
  orientation->orthoToBox[0][2] = 0.;
  orientation->orthoToBox[1][2] = 0.;
  orientation->orthoToBox[2][2] = 1. / orientation->boxToOrtho[2][2];
  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: normal case, orthoToBox matrix.\n");
  DBG_fprintf(stderr, " | %8.4f %8.4f %8.4f\n", orientation->orthoToBox[0][0],
	      orientation->orthoToBox[0][1], orientation->orthoToBox[0][2]);
  DBG_fprintf(stderr, " | %8.4f %8.4f %8.4f\n", orientation->orthoToBox[1][0],
	      orientation->orthoToBox[1][1], orientation->orthoToBox[1][2]);
  DBG_fprintf(stderr, " | %8.4f %8.4f %8.4f\n", orientation->orthoToBox[2][0],
	      orientation->orthoToBox[2][1], orientation->orthoToBox[2][2]);
}

static void orientationChanged(VisuUiOrientationChooser *orientation,
			       VisuUiOrientationChooserBasis changedBasis)
{
  int i;
  float xyz[3], boxXYZ[3], angles[3];

  g_return_if_fail(changedBasis != visu_ui_orientation_chooser_box || orientation->box);

  /* Get the changed coordinates and update the others. */
  switch (changedBasis)
    {
    case visu_ui_orientation_chooser_ortho:
      for (i = 0; i < 3; i++)
	xyz[i] = (float)gtk_spin_button_get_value
	  (GTK_SPIN_BUTTON(orientation->spinsOrtho[i]));
      if (orientation->box)
	{
	  if (orientation->kind == VISU_UI_ORIENTATION_DIRECTION)
	    visu_box_convertXYZtoBoxCoordinates(orientation->box, boxXYZ, xyz);
	  else if (orientation->kind == VISU_UI_ORIENTATION_NORMAL)
	    tool_matrix_productVector(boxXYZ, orientation->orthoToBox, xyz);
	}
      tool_matrix_cartesianToSpherical(angles, xyz);
      if (angles[2] > 180.) angles[2] -= 360.;
      break;
    case visu_ui_orientation_chooser_box:
      for (i = 0; i < 3; i++)
	boxXYZ[i] = (float)gtk_spin_button_get_value
	  (GTK_SPIN_BUTTON(orientation->spinsBox[i]));
      /* The conversion to cartesian coordinates depends on the
	 attribute kind. */
      if (orientation->kind == VISU_UI_ORIENTATION_DIRECTION)
	visu_box_convertBoxCoordinatestoXYZ(orientation->box, xyz, boxXYZ);
      else if (orientation->kind == VISU_UI_ORIENTATION_NORMAL)
	tool_matrix_productVector(xyz, orientation->boxToOrtho, boxXYZ);
      tool_matrix_cartesianToSpherical(angles, xyz);
      if (angles[2] > 180.) angles[2] -= 360.;
      break;
    case visu_ui_orientation_chooser_angles:
      for (i = 0; i < 3; i++)
	xyz[i] = (float)gtk_spin_button_get_value
	  (GTK_SPIN_BUTTON(orientation->spinsOrtho[i]));
      angles[0] = sqrt(xyz[0] * xyz[0] + xyz[1] * xyz[1] + xyz[2] * xyz[2]);
      if (angles[0] == 0.)
	angles[0] = 1.;
      for (i = 1; i < 3; i++)
	angles[i] = (float)gtk_spin_button_get_value
	  (GTK_SPIN_BUTTON(orientation->spinsAngles[i - 1]));
      tool_matrix_sphericalToCartesian(xyz, angles);
      if (orientation->box)
	{
	  if (orientation->kind == VISU_UI_ORIENTATION_DIRECTION)
	    visu_box_convertXYZtoBoxCoordinates(orientation->box, boxXYZ, xyz);
	  else if (orientation->kind == VISU_UI_ORIENTATION_NORMAL)
	    tool_matrix_productVector(boxXYZ, orientation->orthoToBox, xyz);
	}
      break;
    }
  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: set coordinates.\n");
  DBG_fprintf(stderr, " | ortho:  %8.4g %8.4g %8.4g\n", xyz[0], xyz[1], xyz[2]);
  DBG_fprintf(stderr, " | box:    %8.4g %8.4g %8.4g\n", boxXYZ[0], boxXYZ[1], boxXYZ[2]);
  DBG_fprintf(stderr, " | angles: %8.4g %8.4g\n", angles[1], angles[2]);
  /* Stop the internal spin signals, change the values and reset the signals. */
  for (i = 0; i < 3; i++)
    {
/*       DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: change the ortho values\n"); */
      g_signal_handler_block(G_OBJECT(orientation->spinsOrtho[i]),
			     orientation->signalsOrtho[i]);
      gtk_spin_button_set_value(GTK_SPIN_BUTTON(orientation->spinsOrtho[i]), xyz[i]);
      g_signal_handler_unblock(G_OBJECT(orientation->spinsOrtho[i]),
			       orientation->signalsOrtho[i]);
    }
  if (orientation->box)
    for (i = 0; i < 3; i++)
      {
/* 	DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: change the box values\n"); */
	g_signal_handler_block(G_OBJECT(orientation->spinsBox[i]),
			       orientation->signalsBox[i]);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(orientation->spinsBox[i]),
				  boxXYZ[i]);
	g_signal_handler_unblock(G_OBJECT(orientation->spinsBox[i]),
				 orientation->signalsBox[i]);
      }
  for (i = 0; i < 2; i++)
    {
/*       DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: change the angles values\n"); */
      g_signal_handler_block(G_OBJECT(orientation->spinsAngles[i]),
			     orientation->signalsAngles[i]);
      gtk_spin_button_set_value(GTK_SPIN_BUTTON(orientation->spinsAngles[i]),
				angles[i + 1]);
      g_signal_handler_unblock(G_OBJECT(orientation->spinsAngles[i]),
			       orientation->signalsAngles[i]);
    }

  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(orientation->checkLiveUpdate)))
    {
      DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: emit value changed (%d).\n",
		  changedBasis);
      g_signal_emit(G_OBJECT(orientation), visu_ui_orientation_chooser_signals[VALUES_CHANGED],
		    0 , NULL);
    }
}
static void onOrthoChanged(GtkSpinButton *spin _U_, gpointer data)
{
  orientationChanged(VISU_UI_ORIENTATION_CHOOSER(data), visu_ui_orientation_chooser_ortho);
}
static void onBoxChanged(GtkSpinButton *spin _U_, gpointer data)
{
  orientationChanged(VISU_UI_ORIENTATION_CHOOSER(data), visu_ui_orientation_chooser_box);
}
static void onAnglesChanged(GtkSpinButton *spin _U_, gpointer data)
{
  orientationChanged(VISU_UI_ORIENTATION_CHOOSER(data), visu_ui_orientation_chooser_angles);
}
static void onResponse(VisuUiOrientationChooser *chooser, gint response, gpointer data _U_)
{
  if (response == GTK_RESPONSE_ACCEPT &&
      !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chooser->checkLiveUpdate)))
    {
      DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: emit value changed on out.\n");
      g_signal_emit(G_OBJECT(chooser), visu_ui_orientation_chooser_signals[VALUES_CHANGED],
		    0 , NULL);
    }
}


/**
 * visu_ui_orientation_chooser_setOrthoValues:
 * @orientation: a #VisuUiOrientationChooser widget ;
 * @values: (array fixed-size=3): three floating point values.
 *
 * Change the direction using the one given in an orthogonal basis
 * set. Update all other values accordingly.
 */
void visu_ui_orientation_chooser_setOrthoValues(VisuUiOrientationChooser *orientation,
                                                float values[3])
{
  int i;
  gboolean update;
  GtkSpinButton *spin;

  g_return_if_fail(VISU_IS_UI_ORIENTATION_CHOOSER(orientation));

  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: change ortho.\n");
  /* Block the internal spin signals and raise manually the signal for one spin. */
  update = FALSE;
  for (i = 0; i < 3; i++)
    {
      spin = GTK_SPIN_BUTTON(orientation->spinsOrtho[i]);
      g_signal_handler_block(G_OBJECT(spin), orientation->signalsOrtho[i]);
      update = update || ((float)gtk_spin_button_get_value(spin) != values[i]);
      gtk_spin_button_set_value(spin, values[i]);
      g_signal_handler_unblock(G_OBJECT(spin), orientation->signalsOrtho[i]);
    }
  if (update)
    orientationChanged(orientation, visu_ui_orientation_chooser_ortho);
}
/**
 * visu_ui_orientation_chooser_setBoxValues:
 * @orientation: a #VisuUiOrientationChooser widget ;
 * @values: (array fixed-size=3): three floating point values.
 *
 * Change the direction using the one given in the box basis
 * set. Update all other values accordingly.
 */
void visu_ui_orientation_chooser_setBoxValues(VisuUiOrientationChooser *orientation,
                                              float values[3])
{
  int i;
  gboolean update;
  GtkSpinButton *spin;

  g_return_if_fail(VISU_IS_UI_ORIENTATION_CHOOSER(orientation));

  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: change box.\n");
  /* Block the internal spin signals and raise manually the signal for one spin. */
  update = FALSE;
  for (i = 0; i < 3; i++)
    {
      spin = GTK_SPIN_BUTTON(orientation->spinsBox[i]);
      g_signal_handler_block(G_OBJECT(spin), orientation->signalsBox[i]);
      update = update || ((float)gtk_spin_button_get_value(spin) != values[i]);
      gtk_spin_button_set_value(spin, values[i]);
      g_signal_handler_unblock(G_OBJECT(spin), orientation->signalsBox[i]);
    }
  if (update)
    orientationChanged(orientation, visu_ui_orientation_chooser_box);
}
/**
 * visu_ui_orientation_chooser_setAnglesValues:
 * @orientation: a #VisuUiOrientationChooser widget ;
 * @values: (array fixed-size=2): two floating point values.
 *
 * Change the direction using the one given in a spherical basis
 * set. Update all other values accordingly.
 */
void visu_ui_orientation_chooser_setAnglesValues(VisuUiOrientationChooser *orientation,
                                                 float values[2])
{
  int i;
  gboolean update;
  GtkSpinButton *spin;

  g_return_if_fail(VISU_IS_UI_ORIENTATION_CHOOSER(orientation));

  DBG_fprintf(stderr, "Gtk VisuUiOrientationChooser: change angles to %gx%g.\n",
              values[0], values[1]);
  update = FALSE;
  for (i = 0; i < 2; i++)
    {
      spin = GTK_SPIN_BUTTON(orientation->spinsAngles[i]);
      g_signal_handler_block(G_OBJECT(spin), orientation->signalsAngles[i]);
      update = update || ((float)gtk_spin_button_get_value(spin) != values[i]);
      gtk_spin_button_set_value(spin, values[i]);
      g_signal_handler_unblock(G_OBJECT(spin), orientation->signalsAngles[i]);
    }
  if (update)
    orientationChanged(orientation, visu_ui_orientation_chooser_angles);
}

/**
 * visu_ui_orientation_chooser_getOrthoValues:
 * @orientation: a #VisuUiOrientationChooser widget ;
 * @values: (array fixed-size=3) (out): a location for three floating point values.
 *
 * Get the current orientation in the orthogonal basis set.
 */
void visu_ui_orientation_chooser_getOrthoValues(VisuUiOrientationChooser *orientation,
                                                float values[3])
{
  int i;

  g_return_if_fail(VISU_IS_UI_ORIENTATION_CHOOSER(orientation));

  for (i = 0; i < 3; i++)
    values[i] = (float)gtk_spin_button_get_value
      (GTK_SPIN_BUTTON(orientation->spinsOrtho[i]));
}
/**
 * visu_ui_orientation_chooser_getBoxValues:
 * @orientation: a #VisuUiOrientationChooser widget ;
 * @values: (array fixed-size=3) (out): a location for three floating point values.
 *
 * Get the current orientation in the box basis set.
 */
void visu_ui_orientation_chooser_getBoxValues(VisuUiOrientationChooser *orientation,
                                              float values[3])
{
  int i;

  g_return_if_fail(VISU_IS_UI_ORIENTATION_CHOOSER(orientation));

  for (i = 0; i < 3; i++)
    values[i] = (float)gtk_spin_button_get_value
      (GTK_SPIN_BUTTON(orientation->spinsBox[i]));
}
/**
 * visu_ui_orientation_chooser_getAnglesValues:
 * @orientation: a #VisuUiOrientationChooser widget ;
 * @values: (array fixed-size=2) (out): a location for two floating point values.
 *
 * Get the current orientation in the spherical basis set.
 */
void visu_ui_orientation_chooser_getAnglesValues(VisuUiOrientationChooser *orientation,
                                                 float values[2])
{
  int i;

  g_return_if_fail(VISU_IS_UI_ORIENTATION_CHOOSER(orientation));

  for (i = 0; i < 2; i++)
    values[i] = (float)gtk_spin_button_get_value
      (GTK_SPIN_BUTTON(orientation->spinsAngles[i]));
}
