/*
 *  GXSNMP - An snmp managment application
 *  Copyright (C) 1998 Gregory McLean
 *
 *  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.,  59 Temple Place - Suite 330, Cambridge, MA 02139, USA.
 *
 *  The map item widget
 *
 *  Map item widgets are based on GnomeCanvasGroup objects.
 *
 *  This is an abstract class.  There is no function to create a map
 *  item widget.  Instead, other classes, specifically the gxsnmp_host
 *  class and the gxsnmp_network class, are derived from this class.
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gnome.h>
#include "dbapi.h"
#include "gxsnmp/gxsnmp_dbapi.h"
#include "gxsnmp_map.h"
#include "gxsnmp_map_item.h"
#include "gxsnmp_wire.h"

#include "debug.h"

static GnomeCanvasItem   *parent_class;

/* Argument types */

enum {
  MAP_ITEM_ARG_0,
  MAP_ITEM_ARG_X,
  MAP_ITEM_ARG_Y
};

/*
**  Forward references
*/

static void     map_item_class_init     (GXsnmp_map_itemClass 	* klass);

static void     map_item_init       	     (GXsnmp_map_item	* map_item);

static void     map_item_destroy    	     (GtkObject		* object);
static void     map_item_set_arg	     (GtkObject        	* object,
					      GtkArg		* arg,
					      guint		  arg_id);

static void     map_item_get_arg    	     (GtkObject         * object,
				     	      GtkArg		* arg,
				     	      guint               arg_id);

static gboolean map_item_default_select      (GXsnmp_map_item 	* item);

static gboolean map_item_default_deselect    (GXsnmp_map_item 	* item);

static void     map_item_default_move        (GXsnmp_map_item 	* item, 
					      gdouble 		  x, 
					      gdouble 		  y);

static void	map_item_default_add_wire    (GXsnmp_map_item	* item,
					      GXsnmp_wire	* wire);

static void	map_item_default_move_wire   (GXsnmp_map_item   * item,
					      GXsnmp_wire	* wire);

static void	map_item_default_remove_wire (GXsnmp_map_item   * item,
					      GXsnmp_wire	* wire);

static gint     map_item_default_popup       (GdkEventButton    * event,
					      gpointer            data);

static void	map_item_default_changed     (GXsnmp_map_item   * item,
					      gpointer            data);

/******************************************************************************
**
**  Signals
**
******************************************************************************/

enum {
  SELECT,
  DESELECT,
  MOVE,
  ADD_WIRE,
  MOVE_WIRE,
  REMOVE_WIRE,
  CHANGED,
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };


/*****************************************************************************
**
**  gxsnmp_map_item_get_type ()
**
*****************************************************************************/

GtkType
gxsnmp_map_item_get_type()
{
  static GtkType map_item_type = 0;

  if (!map_item_type)
    {
      GtkTypeInfo map_item_info =
      {
        "GXsnmp_map_item",
        sizeof (GXsnmp_map_item),
        sizeof (GXsnmp_map_itemClass),
        (GtkClassInitFunc) map_item_class_init,
        (GtkObjectInitFunc) map_item_init,
        /* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
	(GtkClassInitFunc) NULL,
      };
      map_item_type = 
	    gtk_type_unique (gnome_canvas_group_get_type (), &map_item_info);
    }
  return map_item_type;
}


/******************************************************************************
**
**  Private signal marshaller
**
******************************************************************************/

typedef void (*GtkSignal_NONE__DOUBLE_DOUBLE) (GtkObject    * object,
					       gdouble        arg1,
					       gdouble        arg2,
					       gpointer       user_data);

void gtk_marshal_NONE__DOUBLE_DOUBLE 	      (GtkObject    * object,
                            		       GtkSignalFunc  func,
                            		       gpointer       func_data,
                            		       GtkArg       * args)
{
  GtkSignal_NONE__DOUBLE_DOUBLE  rfunc;

  rfunc = (GtkSignal_NONE__DOUBLE_DOUBLE) func;
  (* rfunc) (object,
            GTK_VALUE_DOUBLE(args[0]),
            GTK_VALUE_DOUBLE(args[1]),
            func_data);
}

/*****************************************************************************
**
**  The class initialization subroutine
**
*****************************************************************************/

static void
map_item_class_init (GXsnmp_map_itemClass *klass)
{
  GtkObjectClass         *object_class;
  GnomeCanvasGroupClass  *group_class;
  GnomeCanvasItemClass   *item_class;
  D_FUNC_START;
  parent_class = gtk_type_class (gnome_canvas_group_get_type());
   
  object_class = (GtkObjectClass *) klass;
  group_class  = (GnomeCanvasGroupClass *) klass;
  item_class   = (GnomeCanvasItemClass *) klass;

  /* Add the arguments here */

  /* Object class method overrides */
  object_class->destroy = map_item_destroy;
  object_class->set_arg = map_item_set_arg;
  object_class->get_arg = map_item_get_arg;

  /* GnomeCanvasGroup method overrides */

  /* Our signals go here */

  signals[SELECT] =
    gtk_signal_new ("select",
                    GTK_RUN_LAST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GXsnmp_map_itemClass, select),
                    gtk_marshal_BOOL__NONE,
		    GTK_TYPE_BOOL, 0);
  signals[DESELECT] =
    gtk_signal_new ("deselect",
                    GTK_RUN_LAST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GXsnmp_map_itemClass, deselect),
                    gtk_marshal_BOOL__NONE,
                    GTK_TYPE_BOOL, 0);
  signals[MOVE] =
    gtk_signal_new ("move",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GXsnmp_map_itemClass, move),
                    gtk_marshal_NONE__DOUBLE_DOUBLE,
                    GTK_TYPE_NONE, 2,  
		    GTK_TYPE_DOUBLE,
                    GTK_TYPE_DOUBLE);
  signals[ADD_WIRE] =
    gtk_signal_new ("add_wire",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GXsnmp_map_itemClass, add_wire),
                    gtk_marshal_NONE__POINTER,
                    GTK_TYPE_NONE, 1,
                    GTK_TYPE_POINTER);
  signals[MOVE_WIRE] =
    gtk_signal_new ("move_wire",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GXsnmp_map_itemClass, move_wire),
                    gtk_marshal_NONE__POINTER,
                    GTK_TYPE_NONE, 1,
                    GTK_TYPE_POINTER);
  signals[REMOVE_WIRE] =
    gtk_signal_new ("remove_wire",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GXsnmp_map_itemClass, remove_wire),
                    gtk_marshal_NONE__POINTER,
                    GTK_TYPE_NONE, 1,
		    GTK_TYPE_POINTER);
  signals[CHANGED] =
    gtk_signal_new ("changed",
                    GTK_RUN_FIRST,
                    object_class->type,
                    GTK_SIGNAL_OFFSET (GXsnmp_map_itemClass, changed),
                    gtk_marshal_NONE__POINTER,
                    GTK_TYPE_NONE, 1,
                    GTK_TYPE_POINTER);

/*
**  Methods for this class
*/

  klass->select      = map_item_default_select;
  klass->deselect    = map_item_default_deselect;
  klass->move	     = map_item_default_move;
  klass->add_wire    = map_item_default_add_wire;
  klass->move_wire   = map_item_default_move_wire;
  klass->remove_wire = map_item_default_remove_wire;
  klass->popup_menu  = map_item_default_popup;
  klass->changed     = map_item_default_changed;
  D_FUNC_END;
}

/*****************************************************************************
**
**  The widget initialization subroutine
**
*****************************************************************************/

static void
map_item_init (GXsnmp_map_item *item)
{
  D_FUNC_START;
  d_print (DEBUG_OBJECTS, "Init map_item object.\n");
  item->select_box = NULL;
  item->DB_graph   = NULL;
  item->selected   = NULL;
  item->wires      = NULL;
/*
**  Connect the GnomeCanvasItem "event" signal to the mouse selection handler
*/
  GTK_WIDGET_SET_FLAGS (item, GTK_CAN_FOCUS);
  gtk_signal_connect (GTK_OBJECT (item), "event",
                      (GtkSignalFunc) gxsnmp_map_mouse_select, NULL);
  D_FUNC_END;
}

static void
map_item_destroy (GtkObject *object)
{
  GXsnmp_map_item    *map_item;
  GXsnmp_map_item    *wire;
  GXsnmp_map         *map;
  GList              *gl;
  DB_graph           *dbg;
  DB_graph           *wdbg;
  D_FUNC_START;
  d_print (DEBUG_OBJECTS, "Destroy map_item object.\n");
  g_return_if_fail (object != NULL);
  g_return_if_fail (GXSNMP_IS_MAP_ITEM (object));
  map_item = GXSNMP_MAP_ITEM (object);
  dbg = map_item->DB_graph;
  while (map_item->selected)
    {
      map = GXSNMP_MAP (map_item->selected->data);
      g_return_if_fail (GXSNMP_IS_MAP (map));
      map->selected = g_list_remove (map->selected, map_item);
      map_item->selected = g_list_remove (map_item->selected, map);
    }
  if (map_item->selected)
    g_list_free (map_item->selected);
  /* Clean up the wire mess. */
  gl = map_item->wires;
  while (gl)
    {
      d_print (DEBUG_TRACE, "Delete wires from map_item.\n");
      wire = (GXsnmp_map_item *)gl->data;
      wdbg = (DB_graph *)wire->DB_graph;
      gtk_signal_emit_by_name (GTK_OBJECT (map_item), "remove_wire", wire);
      if (wdbg)
	{
	  d_print (DEBUG_TRACE, "Delete wires from graph db.\n");
	  graph_delete (wdbg);
	  graph_destroy (wdbg);
	  wdbg = NULL;
	}

      gl = g_list_remove (map_item->wires, wire);
    }
  if (map_item->wires)
    g_list_free (map_item->wires);
  map_item->wires = NULL;
  if (map_item->select_box)
    {
      d_print (DEBUG_TRACE, "Freeing the select_box\n");
      gtk_object_destroy (GTK_OBJECT (map_item->select_box));
      map_item->select_box = NULL;
    }
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
  D_FUNC_END;
//  gtk_object_destroy (GTK_OBJECT (map_item));
}

static void
map_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
  /* Add the X and Y arguments here */
  D_FUNC_START;
  g_return_if_fail (object != NULL);
  switch (arg_id) 
    {
    
    default:
      g_warning ("map_item got an unknown arg type.");
      break;
    }
  D_FUNC_END;
}

static void
map_item_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
  D_FUNC_START;
  g_return_if_fail (object != NULL);
  switch (arg_id)
    {
      
    default:
      arg->type = GTK_TYPE_INVALID;
      break;
    }
  D_FUNC_END;
}

/******************************************************************************
**
**  A class select method must be provided for each item type
**
******************************************************************************/

static gboolean
map_item_default_select (GXsnmp_map_item * item)
{
  g_print ("map_item_default_select: Override me please!\n");
  return FALSE;
}

/******************************************************************************
**
**  A class deselect method must be provided for each item type
**
******************************************************************************/

static gboolean
map_item_default_deselect (GXsnmp_map_item * item)
{
  g_print ("map_item_default_deselect:  Override me please!\n");
  return FALSE;
}

/*****************************************************************************
**
**  The default move method uses gnome_canvas_item_move.
**
*****************************************************************************/

static void
map_item_default_move (GXsnmp_map_item * item, gdouble dx, gdouble dy)
{
  GList * gl;
  D_FUNC_START;
  g_return_if_fail (item != NULL);
  g_return_if_fail (GXSNMP_IS_MAP_ITEM (item));

  gnome_canvas_item_move (GNOME_CANVAS_ITEM (item), dx, dy);

  if (item->wires)
    {
      gl = item->wires;
      do {
	gxsnmp_wire_move (item, gl->data);
      } while ((gl = gl->next));
    }
  D_FUNC_END;
}

/******************************************************************************
**
**  The default add_wire method stores a pointer to the wire object in a glist
**
*****************************************************************************/

static void
map_item_default_add_wire (GXsnmp_map_item * item,
			   GXsnmp_wire     * wire)
{
  D_FUNC_START;
  g_return_if_fail (item != NULL);
  g_return_if_fail (GXSNMP_IS_MAP_ITEM (item));
  g_return_if_fail (wire != NULL);
  g_return_if_fail (GXSNMP_IS_WIRE (wire));

  item->wires = g_list_append (item->wires, wire);
  gxsnmp_wire_move (item, wire);			/* Position the wire */
  D_FUNC_END;
}

/*****************************************************************************
**
**  The default move_wire method attaches the wire to the point where the  
**  GXsnmp_map_item attaches to the canvas.
**
*****************************************************************************/

static void
map_item_default_move_wire (GXsnmp_map_item * item, 
			    GXsnmp_wire     * wire)
{
  D_FUNC_START;
  g_return_if_fail (item != NULL);
  g_return_if_fail (GXSNMP_IS_MAP_ITEM (item));

  g_return_if_fail (wire != NULL);
  g_return_if_fail (GXSNMP_IS_WIRE (wire));

  /* Adjust the wires and mark them as adjusted. */
  gnome_canvas_item_i2w (GNOME_CANVAS_ITEM (item), 
			 &wire->endpoint[0], &wire->endpoint[1]);
  wire->adjuststate = WIRE_ADJUSTED;
  D_FUNC_END;
  return;
}

/******************************************************************************
**
**  The default remove_wire method removes the wire from the wires GList.
**
*****************************************************************************/

static void
map_item_default_remove_wire (GXsnmp_map_item * item,
                              GXsnmp_wire     * wire)
{
  D_FUNC_START;
  g_return_if_fail (item != NULL);
  g_return_if_fail (GXSNMP_IS_MAP_ITEM (item));

  g_return_if_fail (wire != NULL);
  g_return_if_fail (GXSNMP_IS_WIRE (wire));

  item->wires = g_list_remove (item->wires, wire);
  D_FUNC_END;
}

/******************************************************************************
**
**  The default changed method needs to be overridden
**
******************************************************************************/

static void
map_item_default_changed (GXsnmp_map_item * item, gpointer data)
{
  g_print ("map_item_default_changed:  Override me please!\n");
}

/******************************************************************************
**
** The default menu popup method needs to be overridden
**
******************************************************************************/

static gint 
map_item_default_popup (GdkEventButton *event, gpointer data)
{
  g_print ("map_item_default_popup: Override me please!\n");
  return FALSE;
}
/******************************************************************************
**
**  Public function to select a map item
**
**  Return value is TRUE if the item authorized the selection,
**  FALSE if the item doesn't want to be selected.
**
******************************************************************************/

gboolean
gxsnmp_map_item_select (GXsnmp_map_item * item)
{
  gint return_val;
  D_FUNC_START;
  g_return_val_if_fail (item != NULL, FALSE);
  g_return_val_if_fail (GXSNMP_IS_MAP_ITEM (item), FALSE);

  return_val = FALSE;
  gtk_signal_emit (GTK_OBJECT (item), signals[SELECT], &return_val);
  D_FUNC_END;
  return return_val;
}

/******************************************************************************
**
**  Public function to deselect an item.
**
**  If the item returned FALSE, then the item does not want to be deselected.
**  In this case, return FALSE.
**
**  If we deselected an item, then check to see if it moved.  If so, then
**  make the position change in the database.
**
******************************************************************************/

gboolean
gxsnmp_map_item_deselect (GXsnmp_map_item * item)
{
  gint return_val;
  D_FUNC_START;
  g_return_val_if_fail (item != NULL, FALSE);
  g_return_val_if_fail (GXSNMP_IS_MAP_ITEM (item), FALSE);

  return_val = FALSE;
  gtk_signal_emit (GTK_OBJECT (item), signals[DESELECT], &return_val);
  if (return_val)
    {
      gdouble x, y;
      x = y = 0.0;
      gnome_canvas_item_i2w (GNOME_CANVAS_ITEM (item), &x, &y);
      if ((item->DB_graph->x != x) || (item->DB_graph->y != y))
        {
          item->DB_graph->x = x;
          item->DB_graph->y = y;
          g_sqldb_row_update (graph_sqldb, item->DB_graph);
        }
    }
  D_FUNC_END;
  return return_val;
}

/*****************************************************************************
**
**  Public functions to set and get the location of a map item
**  These coordinates are relative to the map the item is assigned to.
**  This provides a common interface for moving any object on any map.
**
*****************************************************************************/

void    
gxsnmp_map_item_set_location (GXsnmp_map_item * item, gdouble x, gdouble y)
{
  gdouble dx, dy;
  D_FUNC_START;
  g_return_if_fail (item != NULL);
  g_return_if_fail (GXSNMP_IS_MAP_ITEM (item));

  dx = 0.0;
  dy = 0.0;
  gnome_canvas_item_i2w (GNOME_CANVAS_ITEM (item), &dx, &dy);  
  dx = x - dx;
  dy = y - dy;

  gtk_signal_emit (GTK_OBJECT (item), signals[MOVE], dx, dy);
  D_FUNC_END;
}
/****************************************************************************
 * gxsnmp_map_item_get_location ()
 ***************************************************************************/
gboolean
gxsnmp_map_item_get_location (GXsnmp_map_item *item, gdouble * x, gdouble * y)
{
  GtkArg    *my_arg;
  D_FUNC_START;
  g_return_val_if_fail (item != NULL, FALSE);
  g_return_val_if_fail (GXSNMP_IS_MAP_ITEM (item), FALSE);

  my_arg = g_new0(GtkArg, 2);
  if (!my_arg)
    {
      g_warning ("gxsnmp_map_item_get_location: memory alloc failed....");
      return;
    }

  my_arg[0].name = "x";
  my_arg[1].name = "y";
  gtk_object_getv (GTK_OBJECT (item), 2, my_arg);

  if (x)
    * x = GTK_VALUE_DOUBLE (my_arg[0]);
  if (y)
    * y = GTK_VALUE_DOUBLE (my_arg[1]);
  g_free (my_arg);
  D_FUNC_END;
  return TRUE;
}

/* EOF */
