/*
**  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 host widget
**
**  Host widgets are based on GXsnmp_map_item objects.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gnome.h>
#include "dbapi.h"
#include "gxsnmp/gxsnmp_dbapi.h"
#include "gxsnmp_host.h"
#include "gxsnmp_wire.h"
#include <math.h>
#include "menus.h"
#include "debug.h"

enum {
  HOST_ARG_0,
  HOST_ARG_PIXMAP_FILE,    /* file name of a pixmap to load */
  HOST_ARG_PIXMAP,         /* preloaded pixmap */
  HOST_ARG_LABEL,
  HOST_ARG_LABEL_GDKCOLOR, /* Preallocated color */
  HOST_ARG_LABEL_COLOR,    /* Allocate and parse this */
  HOST_ARG_HOST_POINTER    /* Fetch the pointer for the host associated */
                           /* with this item */
};

static gchar * default_pixmap = "desktop.xpm";

/******************************************************************************
**
**  Forward references
**
******************************************************************************/

static void   host_class_init  		(GXsnmp_hostClass * class);
static void   host_init        		(GXsnmp_host	  * host);
static void   host_get_arg     		(GtkObject        * object,
                                	 GtkArg           * arg,
                                	 guint              arg_id);
static void   host_set_arg     		(GtkObject        * object,
                                	 GtkArg           * arg,
                                	 guint              arg_id);
static void   host_item_destroy         (GtkObject        * object);
static void   host_move_wire   		(GXsnmp_map_item  * host,
                                	 GXsnmp_wire      * wire);
static gboolean host_item_select	(GXsnmp_map_item  * item);
static gboolean host_item_deselect	(GXsnmp_map_item  * item);
static void   host_load_database_pixmap (GXsnmp_host      * host);
static void   free_imlib_image 	 	(GtkObject        * object,
                                	 gpointer           data);
static void   host_changed              (GXsnmp_map_item  * item,
					 gpointer	    data);
static void   host_object_updated_cb    (G_sqldb_table    * table,
					 gpointer	    row,
					 G_sqldb_cb_type    type,
					 gpointer	    data);
static void   graph_object_added_cb     (G_sqldb_table    * table,
                                         gpointer           row,
                                         G_sqldb_cb_type    type,
                                         gpointer           data);
static void   graph_object_updated_cb   (G_sqldb_table    * table,
                                         gpointer           row,
                                         G_sqldb_cb_type    type,
                                         gpointer           data);
static void   graph_object_deleted_cb   (G_sqldb_table    * table,
                                         gpointer           row,
                                         G_sqldb_cb_type    type,
                                         gpointer           data);

/******************************************************************************
**
**  gxsnmp_host_get_type ()
**
******************************************************************************/
static GXsnmp_map_item *parent_class;
GtkType
gxsnmp_host_get_type()
{
  static GtkType host_type = 0;

  if (!host_type)
    {
      GtkTypeInfo host_info =
      {
        "GXsnmp_host",
        sizeof (GXsnmp_host),
        sizeof (GXsnmp_hostClass),
        (GtkClassInitFunc) host_class_init,
        (GtkObjectInitFunc) host_init,
        /* reserved 1 */ NULL,
        /* reserved 2 */ NULL,
	(GtkClassInitFunc) NULL,
      };
      host_type = 
	    gtk_type_unique (gxsnmp_map_item_get_type (), &host_info);
    }
  return host_type;
}

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

static void
host_class_init (GXsnmp_hostClass *klass)
{
  GtkObjectClass       *object_class;
  GXsnmp_map_itemClass *map_item_class;
  D_FUNC_START;
  object_class   = (GtkObjectClass *)       klass;
  map_item_class = (GXsnmp_map_itemClass *) klass;
  
  parent_class   = gtk_type_class (gxsnmp_map_item_get_type ());

  /* Arg types */
  gtk_object_add_arg_type ("GXsnmp_host::label",
			   GTK_TYPE_STRING,
			   GTK_ARG_READWRITE,
			   HOST_ARG_LABEL);
  gtk_object_add_arg_type ("GXsnmp_host::pixmap_filename",
			   GTK_TYPE_STRING,
			   GTK_ARG_READWRITE,
			   HOST_ARG_PIXMAP_FILE);
  gtk_object_add_arg_type ("GXsnmp_host::label_color",
			   GTK_TYPE_STRING,
			   GTK_ARG_READWRITE,
			   HOST_ARG_LABEL_COLOR);
  /* Object methods */
  object_class->set_arg     = host_set_arg;
  object_class->get_arg     = host_get_arg;
  object_class->destroy     = host_item_destroy;
  /* Class methods */
  map_item_class->select     = host_item_select;
  map_item_class->deselect   = host_item_deselect;
  map_item_class->move_wire  = host_move_wire;
  map_item_class->popup_menu = menu_raise_host_popup;  /* In menus.c */
  map_item_class->changed    = host_changed;

/*
**  Add a hook to the database so that we get a callback whenever any host   
**  object is modified
*/
  /* For the host info. */
  g_sqldb_table_cb_add (host_sqldb, NULL, 
			G_SQLDB_CB_UPDATE | G_SQLDB_CB_AFTER,
			host_object_updated_cb, NULL);

  /* For graph entries being added to a map */
  g_sqldb_table_cb_add (graph_sqldb, NULL,
                        G_SQLDB_CB_ADD | G_SQLDB_CB_AFTER,
                        graph_object_added_cb, NULL);

  /* For changes to the graph information */
  g_sqldb_table_cb_add (graph_sqldb, NULL, 
			G_SQLDB_CB_UPDATE | G_SQLDB_CB_AFTER,
			graph_object_updated_cb, NULL);

  /* For graph entries being deleted from a map */
  g_sqldb_table_cb_add (graph_sqldb, NULL, 
			G_SQLDB_CB_DELETE | G_SQLDB_CB_AFTER,
			graph_object_deleted_cb, NULL);
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback subroutines for accessing arguments
**
******************************************************************************/

static void
host_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
  GXsnmp_host    *host;
  D_FUNC_START;
  g_return_if_fail (GXSNMP_IS_HOST (object));
  host = GXSNMP_HOST (object);
  switch (arg_id)
    {
    case HOST_ARG_PIXMAP_FILE:
      gxsnmp_host_set_pixmap (host, GTK_VALUE_STRING (*arg) ?
			            GTK_VALUE_STRING (*arg) : default_pixmap);
      break;
    case HOST_ARG_LABEL:
      gxsnmp_host_set_display_name (host, GTK_VALUE_STRING (*arg) ?
				          GTK_VALUE_STRING (*arg) : "");
      break;
    case HOST_ARG_LABEL_COLOR:
      gxsnmp_host_set_text_color (host, GTK_VALUE_STRING (*arg) ?
				  GTK_VALUE_STRING (*arg) : "black");
      break;
    default:
      break;
    }
  D_FUNC_END;
}

static void
host_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
  GXsnmp_host     * host;
  GXsnmp_map_item * host_map_item;
  GtkArg          * my_arg;
  D_FUNC_START;
  g_return_if_fail (GXSNMP_IS_HOST (object));
  host          = GXSNMP_HOST     (object);
  host_map_item = GXSNMP_MAP_ITEM (object);

  switch (arg_id)
    {
    case HOST_ARG_PIXMAP_FILE:
      if (host_map_item->DB_graph->pixmap)
	GTK_VALUE_STRING (*arg) = g_strdup (host_map_item->DB_graph->pixmap);
      else
	GTK_VALUE_STRING (*arg) = NULL;
      break;
    case HOST_ARG_LABEL:
      if (host->text)
	{
	  my_arg = g_new0 (GtkArg, 1);
	  my_arg[0].name = "text";
	  gtk_object_getv (GTK_OBJECT (host->text), 1, my_arg);
	  GTK_VALUE_STRING (*arg) = g_strdup (GTK_VALUE_STRING (my_arg[0]));
	  g_free (my_arg);
	}
      else
	GTK_VALUE_STRING (*arg) = NULL;
      break;
    case HOST_ARG_LABEL_COLOR:
      if (host->text_color)
	GTK_VALUE_STRING (*arg) = g_strdup (host->text_color);
      else
	GTK_VALUE_STRING (*arg) = g_strdup ("black");
      break;
    default:
      arg->type = GTK_TYPE_INVALID;
      break;
    }
  D_FUNC_END;
}
/****************************************************************************
 * Destructor
 ***************************************************************************/
static void
host_item_destroy (GtkObject *object)
{
  GXsnmp_host    *host;
  GXsnmp_map_itemClass *parent_class;

  D_FUNC_START;
  d_print (DEBUG_OBJECTS, "Destroy host object.\n");
  parent_class = gtk_type_class (gxsnmp_map_item_get_type());
  g_return_if_fail (object != NULL);
  g_return_if_fail (GXSNMP_IS_HOST (object));
  host = GXSNMP_HOST (object);
  if (host->text_color)
    g_free (host->text_color);
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
  D_FUNC_END;
}
/******************************************************************************
**
**  The widget initialization callback
**
******************************************************************************/
static void
host_init (GXsnmp_host * host)
{
  host->text_color   = NULL;
  host->image        = NULL;
  host->text         = NULL;
}
/****************************************************************************
**
**  Callback function to free up the image when the object is destroyed
**
****************************************************************************/

static void
free_imlib_image (GtkObject *object, gpointer data)
{
  g_return_if_fail (data != NULL);

  gdk_imlib_destroy_image (data);
}

/*****************************************************************************
**
**  The move_wire method for hosts first puts the wire directly in the
**  center of the bounding box of the bitmap.  It then moves the line 
**  endpoint to just outside the bounding box of the entire host item.
**
*****************************************************************************/

static void
host_move_wire (GXsnmp_map_item * item, GXsnmp_wire * wire)
{
  GXsnmp_host * host;

  gdouble dx, dy;		/* Differences between endpoint/nextpoint */
  gdouble x1, y1, x2, y2;	/* Left, Top, Right, Bottom of bounding box */
  gdouble xb, yb;		/* Lengths of bounding box sides */
  gdouble xc, yc;               /* Center point of the bounding box */
  gdouble dxaXyb, dyaXxb;	/* Used to compare line slopes */
  gdouble xx, yy;		/* Final correct location of line endpoint */
  D_FUNC_START;
  g_return_if_fail (item != NULL);
  g_return_if_fail (GXSNMP_IS_HOST (item));
  host = (GXsnmp_host *)item;
  g_return_if_fail (wire != NULL);
  g_return_if_fail (GXSNMP_IS_WIRE (wire));

  gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (GXSNMP_HOST(host)->image),
                                &x1, &y1, &x2, &y2);
  gnome_canvas_item_i2w (GNOME_CANVAS_ITEM (host), &x1, &y1);
  gnome_canvas_item_i2w (GNOME_CANVAS_ITEM (host), &x2, &y2);

  dx = wire->nextpoint[0] -	/* Width of the box formed by the */
       wire->endpoint[0];	/* nextpoint and the endpoint */
  dy = wire->nextpoint[1] -	/* Height of the box formed by the */
       wire->endpoint[1];	/* nextpoint and the endpoint */
  xb = x2 - x1;			/* Lengths of the sides */
  yb = y2 - y1;			/* of the bounding box */
  xc = x1 + xb / 2.0;		/* Centerpoint of */
  yc = y1 + yb / 2.0;		/* the bounding box */
  dxaXyb = fabs (dx) * yb;	/* Intermediate values used to compare the */
  dyaXxb = fabs (dy) * xb;	/* line slope to the bounding diagonal slope */

  if (wire->adjuststate == WIRE_INCORRECT) /* If the other side of the wire */
    {					   /* is incorrectly placed, then */
      wire->endpoint[0] = xc;              /* move our end of the wire to */
      wire->endpoint[1] = yc;              /* the center of our bounding box */
      wire->adjuststate = WIRE_CENTERED;   /* Indicate wire is centered */
      d_print (DEBUG_TRACE, "Allowing other side of wire to adjust.\n");
      D_FUNC_END;
      return;                   	   /* Let the other side adjust */
    }

/*
**  Adjust the wire position, based on 	   \2/
**  which of the four possible quadrants   3X4
**  of the bounding box the line is in:    /1\
*/

  if      ((dy >= 0) && (dxaXyb <= dyaXxb))	/* Quadrant 1 */
    {
      yy = y2;
      xx = xc + (dx / dy) * (yb / 2.0);
    }
  else if ((dy <= 0) && (dxaXyb <= dyaXxb))	/* Quadrant 2 */
    {
      yy = y1;
      xx = xc - (dx / dy) * (yb / 2.0);
    }
  else if ((dx <= 0) && (dxaXyb >= dyaXxb))	/* Quadrant 3 */
    {
      xx = x1;
      yy = yc - (dy / dx) * (xb / 2.0);
    }
  else /* if ((dx >= 0) && (dxaXyb >= dyaXxb))	   Quadrant 4 */
    {
      xx = x2;
      yy = yc + (dy / dx) * (xb / 2.0);
    }

  wire->endpoint[0] = xx;		/* Now move the line to the */
  wire->endpoint[1] = yy;		/* correct location and return */
  wire->adjuststate = WIRE_ADJUSTED;
  D_FUNC_END;
  return;

}


/******************************************************************************
**
**  The select method for a host draws a rectangle just outside of the
**  object's bounding box.
**
******************************************************************************/

static gboolean
host_item_select (GXsnmp_map_item * item)
{
  gdouble x, y, xx, yy;
  GnomeCanvasItem   *floo;
  D_FUNC_START;
  g_return_val_if_fail (item != NULL, FALSE);
  g_return_val_if_fail (GXSNMP_IS_MAP_ITEM (item), FALSE);

  x = y = xx = yy = 0.0;

  gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (GXSNMP_HOST (item)->image),
                                &x, &y, &xx, &yy);
  if (item->select_box)
    gnome_canvas_item_show (item->select_box);
  else
    {
      item->select_box = gnome_canvas_item_new
                                             (GNOME_CANVAS_GROUP(item),
                                              gnome_canvas_group_get_type (),
                                              NULL);
      floo = gnome_canvas_item_new (GNOME_CANVAS_GROUP (item->select_box),
                                                gnome_canvas_rect_get_type (),
                                                "x1", x,  "y1", y,
                                                "x2", xx, "y2", yy,
                                                "outline_color", "black",
                                                "width_units", 0.0,
                                                NULL);
      /* Here are the handles around the item, connect to the items to
       * implement the item resize code.
       */
      /* Left top cube */
      floo = gnome_canvas_item_new (GNOME_CANVAS_GROUP (item->select_box),
                                    gnome_canvas_rect_get_type (),
                                    "x1", x - 3.0, "y1", y - 3.0,
                                    "x2", x + 2.0, "y2", y + 2.0,
                                    "outline_color", "black",
                                    "fill_color", "black",
                                    "width_units", 1.0,
                                    NULL);
      /* Right top cube */
      floo = gnome_canvas_item_new (GNOME_CANVAS_GROUP (item->select_box),
                                    gnome_canvas_rect_get_type (),
                                    "x1", xx - 3.0, "y1", y - 3.0,
                                    "x2", xx + 2.0, "y2", y + 2.0,
                                    "outline_color", "black",
                                    "fill_color", "black",
                                    "width_units", 1.0,
                                    NULL);
      /* Left bottom cube */
      floo = gnome_canvas_item_new (GNOME_CANVAS_GROUP (item->select_box),
                                    gnome_canvas_rect_get_type (),
                                    "x1", x - 3.0, "y1", yy - 3.0,
                                    "x2", x + 2.0, "y2", yy + 2.0,
                                    "outline_color", "black",
                                    "fill_color", "black",
                                    "width_units", 1.0,
                                    NULL);
      /* Right bottom cube */
      floo = gnome_canvas_item_new (GNOME_CANVAS_GROUP (item->select_box),
                                    gnome_canvas_rect_get_type (),
                                    "x1", xx - 2.0, "y1", yy - 2.0,
                                    "x2", xx + 3.0, "y2", yy + 3.0,
                                    "outline_color", "black",
                                    "fill_color", "black",
                                    "width_units", 1.0,
                                    NULL);
    }
  d_print (DEBUG_DUMP, "Item selected.");
  D_FUNC_END;
  return TRUE;		/* Item should be selected */
}

/*****************************************************************************
**
**  The default deselect method is to hide the rectangle, if it exists.
**
*****************************************************************************/

static gboolean
host_item_deselect (GXsnmp_map_item * item)
{
  D_FUNC_START;
  g_return_val_if_fail (item != NULL, FALSE);
  g_return_val_if_fail (GXSNMP_IS_MAP_ITEM (item), FALSE);
  if (item->select_box)
    gnome_canvas_item_hide (item->select_box);

  d_print (DEBUG_DUMP, "Item unselected.");
  D_FUNC_END;
  return TRUE;		/* Item should be deselected */
}


/*****************************************************************************
**
**  Function to create a default host object
**
*****************************************************************************/

static GXsnmp_map_item *
gxsnmp_host_default_new (DB_graph * graph)
{
  GXsnmp_host      * host;
  GXsnmp_map_item  * host_map_item;
  GnomeCanvasGroup * root_group;
  D_FUNC_START;
  g_return_val_if_fail (graph          != NULL, NULL);
  g_return_val_if_fail (graph->DB_map  != NULL, NULL);
  g_return_val_if_fail (graph->DB_host != NULL, NULL);

  root_group = gnome_canvas_root (GNOME_CANVAS (graph->DB_map->application));

  host = GXSNMP_HOST (gnome_canvas_item_new (root_group,
                                     	     gxsnmp_host_get_type (),
					     "x", (double)graph->x,
					     "y", (double)graph->y,
					     NULL));
  host_map_item = GXSNMP_MAP_ITEM (host);

/*
**  Point the DB_graph object and the gxsnmp_host object at each other
*/

  host_map_item->DB_graph = graph;    /* Save pointer to graph structure */
  graph->application = host;	      /* Save pointer to gxsnmp_host object */

/*
**  Invoke the "changed" method to finish constructing the object
*/

  gtk_signal_emit_by_name (GTK_OBJECT (host), "changed", NULL);
  D_FUNC_END;
  return GXSNMP_MAP_ITEM (host);
}

/******************************************************************************
**
**  This g_hook callback function is invoked every time a host object is 
**  changed.  It runs through the graph list for the host, and emits the 
**  changed signal to all of the GXsnmp_host items.
**
******************************************************************************/

static void 
host_object_updated_cb (G_sqldb_table * table, gpointer	row,
			G_sqldb_cb_type type, gpointer data)
{
  DB_host         * dbh;
  DB_graph        * dbg;
  GXsnmp_map_item * map_item;
  GList           * gl;

  D_FUNC_START;
  g_return_if_fail (row != NULL);
  dbh = (DB_host *) row;
  gl = dbh->DB_graphs;
  while (gl)
    {
      dbg = (DB_graph *) gl->data;
      if (dbg->type != DB_GRAPH_HOST)
	{
	  d_print (DEBUG_DUMP, "Invoked with a non host object.\n");
	  D_FUNC_END;
	  return;
	}
      if (dbg->application == NULL)
	{
	  g_warning ("Orphaned graph object in host_object_updated_cb "
		     "watch for flying cores.");
	  D_FUNC_END;
	  return;
	}
      map_item = GXSNMP_MAP_ITEM (dbg->application);
      gtk_signal_emit_by_name (GTK_OBJECT(map_item), "changed", NULL);
      gl = gl->next;
    }
  D_FUNC_END;
}

/******************************************************************************
**
**  This g_hook callback function is invoked every time a graph object is
**  added.
**
******************************************************************************/

static void   
graph_object_added_cb (G_sqldb_table    * table,
                       gpointer           row,
                       G_sqldb_cb_type    type,
                       gpointer           data)
{
  DB_graph        * dbg;
  D_FUNC_START;
  g_return_if_fail (row != NULL);
  dbg = (DB_graph *) row;
  if (!dbg)
    {
      g_warning ("Aieee! NULL pointer passed to me. I'll go cry now.\n");
      return;
    }
  if (dbg->type != DB_GRAPH_HOST)
    {
      d_print (DEBUG_TRACE, "Invoked with a non host object.\n");
      D_FUNC_END;
      return;
    }
  gxsnmp_host_new (dbg);
  D_FUNC_END;
}

/******************************************************************************
**
**  This g_hook callback function is invoked every time a graph object is
**  changed.  It emits the changed signal to that particular graph
**
******************************************************************************/

static void
graph_object_updated_cb (G_sqldb_table * table,
                         gpointer        row,
                         G_sqldb_cb_type type,
                         gpointer        data)
{
  DB_graph        * dbg;
  GXsnmp_map_item * map_item;
  D_FUNC_START;
  g_return_if_fail (row != NULL);
  dbg = (DB_graph *) row;
  if (dbg->type != DB_GRAPH_HOST)
    {
      d_print (DEBUG_TRACE, "Invoked with a non host object.\n");
      D_FUNC_END;
      return;
    }
  g_assert (dbg->application != NULL);
  map_item = GXSNMP_MAP_ITEM (dbg->application);
  gtk_signal_emit_by_name (GTK_OBJECT(map_item), "changed", NULL);
  D_FUNC_END;
}

/******************************************************************************
**
**  This g_hook callback function is invoked every time a graph object is
**  deleted from the database. It destroys the corresponding map_item.
**
**  If we are deleting bad records from the database, then there won't
**  be an associated map item.
**
******************************************************************************/

static void
graph_object_deleted_cb (G_sqldb_table * table,
                         gpointer        row,
                         G_sqldb_cb_type type,
                         gpointer        data)
{
  DB_graph        * dbg;
  GXsnmp_map_item * map_item;
  D_FUNC_START;
  g_return_if_fail (row != NULL);
  dbg = (DB_graph *) row;
  if (dbg->type != DB_GRAPH_HOST)
    {
      d_print (DEBUG_TRACE, "Invoked with a non host object.\n");
      D_FUNC_END;
      return;
    }
  if (dbg->application)
    {
      map_item = GXSNMP_MAP_ITEM (dbg->application);
      gtk_signal_emit_by_name (GTK_OBJECT(map_item), "destroy", NULL);
    }
  D_FUNC_END;
}


/*****************************************************************************
**
**  Signal handler for the "changed" signal
** 
**  This signal handler is invoked whenever something elsewhere in the gxsnmp
**  system changes the underlying data structure owned by this map_item.
**  Our response is to rebuild and redisplay the host.
**
*****************************************************************************/

static void
host_changed (GXsnmp_map_item * item, gpointer data)
{
  GXsnmp_host	* host;
  DB_graph	* graph;
  D_FUNC_START;
  g_return_if_fail (item != NULL);
  g_return_if_fail (GXSNMP_IS_MAP_ITEM (item));
  host = GXSNMP_HOST (item);
  g_return_if_fail (item->DB_graph != NULL);
  graph = item->DB_graph;

/*
**  Attempt to load whatever pixmap is in the DB_graph structure, or a default
**  pixmap if none is specified or the specified pixmap is invalid.
*/

  host_load_database_pixmap (host);

/*
**  Set the label.
*/
  if (!host->text_color)
    gxsnmp_host_set_text_color (host, "black");
  
  if (graph->DB_host->name)
    gxsnmp_host_set_display_name (host, graph->DB_host->name);
  else
    gxsnmp_host_set_display_name (host, "(unnamed)");
  D_FUNC_END;
}

/*******************************************************************************
**
**  Public function to create a new widget
**
**  This is the function to call to add a host to the map.  The returned
**  widget may be a gxsnmp_host object, or may be an object derived from
**  type gxsnmp_host.
**
**  The general strategy here is to use plugins.  For instance, we might
**  have a plugin that implements a widget to represent a Cisco router.
**
**  Each plugin will register two items ... the type of the item, and
**  a subroutine to decide whether or not to handle the item.
**
**  The plugin's decision as to whether or not to represent the host may     
**  be based on any of the contents of the hosts structure, but the default
**  method will be to examine the system description mib returned from the
**  host.  This usually contains the vendor name and machine model.
**
**  Since a gxsnmp_host widget contains map-specific information, it will
**  be necessary to create a separate widget for each map.  The widget
**  will contain a pointer to the hosts structure, and the hosts structure
**  will contain a glist of all widgets that refer to it.
**
**  Thus, when a change is made to a host through the hosts structure, the
**  change can be easily propagated to all of the map widgets.
**
**  It is the responsibility of the calling routine to add this widget to
**  the correct map, and to gtk_widget_show() it.
**
*******************************************************************************/

GXsnmp_map_item *
gxsnmp_host_new (DB_graph * graph)
{
  GXsnmp_host * host;
  D_FUNC_START;
  d_print (DEBUG_DUMP, "New host object.\n");
  g_return_val_if_fail (graph != NULL, NULL);

/*
**  Here, we will run through the plugin list, asking each plugin if it
**  wants to represent this host. If someone answers up, call the new()
**  method in that plugin, which should return a map_item that we can return.
**
*/

/* FIXME:  Write me */

/*
**  None of the plugins replied, so we'll just use the default.
**  Actually, there is no reason why this shouldn't be a plugin also.
*/

  host = GXSNMP_HOST (gxsnmp_host_default_new (graph));
  D_FUNC_END;
  return GXSNMP_MAP_ITEM (host);
}

/******************************************************************************
**
**  Set the display name 
**
******************************************************************************/

void
gxsnmp_host_set_display_name (GXsnmp_host *host, gchar *name)
{
  D_FUNC_START;
  g_return_if_fail (host != NULL);
  g_return_if_fail (GXSNMP_IS_HOST (host));
  g_return_if_fail (name != NULL);

  /* If we already have the item just change the text 
   * We also set the color in case that changed also. 
   */
  if (host->text)
    {
      gnome_canvas_item_set (host->text,
			     "text", name,
			     "fill_color", host->text_color,
			     NULL);
    }
  else 
    {
      host->text = gnome_canvas_item_new (GNOME_CANVAS_GROUP (host),
					  gnome_canvas_text_get_type (),
					  "text", name,
					  "font", "fixed",
					  "x", 0.0,
					  "y", 0.0,
					  "fill_color", host->text_color,
					  "anchor", GTK_ANCHOR_NORTH,
					  NULL);
    }
  D_FUNC_END;
}

/******************************************************************************
**
**  Set the text color, both text and gdkcolor versions.
**
******************************************************************************/

void
gxsnmp_host_set_text_color (GXsnmp_host *host, gchar *color)
{
  GXsnmp_map_item     *item;
  D_FUNC_START;
  g_return_if_fail (host != NULL);
  g_return_if_fail (GXSNMP_IS_HOST (host));
  g_return_if_fail (color != NULL);
  
  item = GXSNMP_MAP_ITEM (host);
  if (host->text_color)
    g_free (host->text_color);
  host->text_color = g_strdup (color);

  if (host->text)                    /* If we have a text item set the color */
    gnome_canvas_item_set (GNOME_CANVAS_ITEM (host->text),
			   "fill_color", host->text_color,
			   NULL);
  D_FUNC_END;
}

void
gxsnmp_host_set_text_gdkcolor (GXsnmp_host *host, GdkColor *color)
{
  D_FUNC_START;
  /* FIXME: write this function */
  D_FUNC_END;
}

/******************************************************************************
**
**  Private routine to load and display the pixmap specified in the graph
**  database -- that is, the default pixmap for this host.
**
******************************************************************************/

static void
host_load_database_pixmap (GXsnmp_host *host)
{
  GXsnmp_map_item * host_map_item;  /* For access to DB_graph element */
  GdkImlibImage   * im;
  gchar		  * pixmap;
  gchar 	  * pixmap_relative;
  gchar 	  * pixmap_absolute;
  D_FUNC_START;
  g_return_if_fail (host != NULL);
  g_return_if_fail (GXSNMP_IS_HOST (host));

  host_map_item = GXSNMP_MAP_ITEM (host);

  g_return_if_fail (host_map_item->DB_graph != NULL);

/*
**  If no pixmap was specified, use the default pixmap.
*/

  pixmap = host_map_item->DB_graph->pixmap;
  if (!pixmap)
    pixmap = default_pixmap;
  pixmap = strdup (pixmap);	/* Makes cleaning up easier later */

/* 
**  If the pixmap name is absolute, use it as specified. Otherwise, resolve
**  the pixmap path using the default GNOME pixmap directory.
*/

  if (pixmap[0] != '/')
    {
      pixmap_relative = g_strdup_printf ("gxsnmp/%s", pixmap);
      pixmap_absolute = gnome_pixmap_file (pixmap_relative);
      g_free (pixmap_relative);
      g_free (pixmap);
      pixmap = pixmap_absolute;
    }

/*
**  If pixmap == NULL, then the pixmap file did not exist.  In this case,
**  try again, using the default pixmap instead.
*/

  if (pixmap == NULL)
    {
      pixmap_relative = g_strdup_printf ("gxsnmp/%s", default_pixmap);
      pixmap = gnome_pixmap_file (pixmap_relative);
      g_free (pixmap_relative);
    } 

  if (pixmap == NULL)
    g_warning ("gxsnmp_host: Unable to load default host pixmap!\n");
  else
    {
      im = gdk_imlib_load_image (pixmap);
      g_free (pixmap);
      gxsnmp_host_set_im_pixmap (host, im);
    }
  D_FUNC_END;
}

/******************************************************************************
**
**  Set the host pixmap (filename version)
**
**  This subroutine causes the DB_graph table entry to be updated.
**
******************************************************************************/

void
gxsnmp_host_set_pixmap (GXsnmp_host *host, gchar * pixmap)
{
  GXsnmp_map_item * host_map_item;  /* For access to DB_graph element */
  D_FUNC_START;
  g_return_if_fail (host != NULL);
  g_return_if_fail (GXSNMP_IS_HOST (host));


  host_map_item = GXSNMP_MAP_ITEM (host);

  g_return_if_fail (host_map_item->DB_graph != NULL);

  if (host_map_item->DB_graph->pixmap)          /* Release the old */
    g_free (host_map_item->DB_graph->pixmap);   /* pixmap file name */
  host_map_item->DB_graph->pixmap = pixmap;     /* Save the new file name */

  g_sqldb_row_update (graph_sqldb, host_map_item->DB_graph);

  host_load_database_pixmap (host);	/* Load the new pixmap */
  D_FUNC_END;
}

/******************************************************************************
**
**  Set the host pixmap, (preloaded image version)
**
**  This subroutine does NOT cause the DB_graph table to to be updated.  It
**  may be used by plugins that want to change the pixmap to indicate some 
**  special condition.
**
******************************************************************************/

void 
gxsnmp_host_set_im_pixmap (GXsnmp_host *host, GdkImlibImage *im)
{
  GXsnmp_map_item        *item;
  GXsnmp_map_itemClass   *klass;
  GnomeCanvasItem        *citem;
  D_FUNC_START;
  g_return_if_fail (host != NULL);
  g_return_if_fail (GXSNMP_IS_HOST (host));
  g_return_if_fail (im != NULL);

  if (host->image)
    {
      d_print (DEBUG_TRACE, "Destroy old pixmap.");
      gtk_object_destroy(GTK_OBJECT (host->image));
      host->image = NULL;
    }

  host->image = gnome_canvas_item_new (GNOME_CANVAS_GROUP (host),
                                       gnome_canvas_image_get_type (),
                                       "image", im,
                                       "x", (double) 0.0,
                                       "y", (double) 0.0,
                                       "width", (double) im->rgb_width,
                                       "height",(double) im->rgb_height,
                                       "anchor", GTK_ANCHOR_SOUTH,
                                       NULL);
  /* Only execute the following block if we acually got an image loaded.
   * FIXME: We should keep the old image around till we know we can load
   *        the new image, then kill the old image and swap in the new one.
   */
  if (host->image)
    {
      gtk_signal_connect (GTK_OBJECT (host->image), "destroy",
			  (GtkSignalFunc) free_imlib_image, im);
      /*
       * If there was a select box we destroy it so it can be remade 
       * If the select box wasn't visible then just kill it and null the
       * pointer, else we kill it and call the deselect/select methods again
       * to get a new box.
       */
      item  = GXSNMP_MAP_ITEM (host);
      klass = GXSNMP_MAP_ITEM_CLASS (GTK_OBJECT (item)->klass);
      if (item->select_box)
	{
	  d_print (DEBUG_DUMP, "Triggering select box resize.\n");
	  citem = GNOME_CANVAS_ITEM (item->select_box);
	  if (!(citem->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
	    {
	      gtk_object_destroy (GTK_OBJECT (item->select_box));
	      item->select_box = NULL;
	      D_FUNC_END;
	      return;
	    }
	  if (klass->deselect)
	    klass->deselect (item);
	  gtk_object_destroy (GTK_OBJECT (citem));
	  item->select_box = NULL;
	  if (klass->select)
	    klass->select (item);
	}
    }
  D_FUNC_END;
}

/* EOF */
