/*
 *  $Id: host_dialog.c,v 1.68 2000/12/26 19:30:20 remlali Exp $
 *
 *  GXSNMP -- An snmp management 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.
 */

/*
**  host_dialog.c defines the dialog that is used to enter and edit
**  information about hosts.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gnome.h>
#include "gnome-property-dialog.h"
#include "host_dialog.h"
#include "main.h"			/* Needed for gxsnmp struct */
#include "host_panel.h"
#include "interface_panel.h"
#include "graph_panel.h"
#include "panel_utility.h"
#include "gxsnmp_window.h"
#include "gxsnmp_map.h"
#include "gxsnmp_map_item.h"

#include "dbapi.h"
#include "gxsnmp/gxsnmp_dbapi.h"

#include "rfc1213.h"

#include "debug.h"

extern gxsnmp *app_info;

/******************************************************************************
**
**  Static Data
**
******************************************************************************/

#define INTERFACE_CLIST_COLUMNS 1

static gchar * interface_clist_titles[]		= { "Interface Name" };
static gint    interface_clist_title_widths[1]	= { 14 };

#define GRAPH_CLIST_COLUMNS 2

static gchar * graph_clist_titles[]		= { "Map Name", "Displayed" };
static gint    graph_clist_title_widths[2]	= { 15, 9 };

static gulong  syscontact[] = { SYSCONTACT, 0 };
static gulong  sysdescr[]   = { SYSDESCR, 0 };
static gulong  ifdescr[]    = { IFDESCR };
static gulong  ifastatus[]  = { IFADMINSTATUS };
static gulong  iftype[]     = { IFTYPE };
static gulong  ifindex[]    = { IFINDEX };
static gulong  ipaddr[]     = { IPADENTADDR };
static gulong  ipnetmask[]  = { IPADENTNETMASK };
static gulong  ipifindex[]  = { IPADENTIFINDEX };

#define oidlen(val) (sizeof(val)/sizeof(gulong))

/******************************************************************************
**
**  Forward declarations and callback functions
**
******************************************************************************/

static void host_dialog_class_init 	 (GXsnmp_host_dialogClass * klass);

static void host_dialog_init	   	 (GXsnmp_host_dialog      * dialog);
static void host_dialog_help_cb          (GtkWidget               *widget,
					  gpointer                data);
static void host_panel_changed_cb	 (GtkWidget		  *widget,
					  gpointer	  	  data);

static void host_discover_button_cb	 (GtkWidget     	  * widget,
					  gpointer	  	    data);

static void interface_panel_changed_cb	 (GtkWidget		  * widget,
					  gpointer	  	    data);

static void interface_snmp_changed_cb	 (GtkList   		  * widget,
					  GtkListItem		  * child, 
					  gpointer 		    data);

static void discover_snmp_changed_cb	 (GtkList   		  * widget,
					  GtkListItem		  * child, 
					  gpointer 		    data);

static void interface_select_row_cb      (GtkWidget      	  * widget,
                         		  gint             	    row,
                         		  gint             	    column,
                        		  GdkEventButton	  * event, 
                         		  gpointer        	    data);

static void interface_add_button_cb	 (GtkWidget		  * widget,
					  gpointer	  	    data);

static void interface_delete_button_cb   (GtkWidget		  * widget,
					  gpointer	  	    data);
 
static void graph_changed_cb		 (GtkWidget		  * widget,
					  gpointer	  	    data);

static void graph_select_row_cb 	 (GtkWidget      	  * widget,
                     			  gint             	    row,
                     			  gint             	    column,
                     			  GdkEventButton 	  * event,
                     			  gpointer       	    data);

static void graph_show_button_cb	 (GtkWidget		  * widget,
					  gpointer	 	    data);

static void graph_hide_button_cb	 (GtkWidget		  * widget,
					  gpointer	  	    data);

static void host_apply_cb                (GtkWidget               * widget,
                                          gpointer                  data);

static gint host_close_cb                (GtkWidget     	  * widget,
                                          gpointer        	    data);

static void host_refresh_cb              (G_sqldb_table           * table,
                                          gpointer                  row,
                                          G_sqldb_cb_type           type,
                                          gpointer                  data);

static void interface_insert_row         (GXsnmp_host_dialog      * dialog,
                                          DB_interface            * dbi,
                                          gint                    * unnamed);

static void set_dialog_state 		 (GXsnmp_host_dialog      * dialog);
static void discover_timeout             (host_snmp               * host,
                                          void                    * magic);
static gboolean discover_system_value    (host_snmp               * host,
                                          void                    * magic,
                                          SNMP_PDU                * spdu, 
                                          GSList                  * objs);
static void discover_int_row             (GHashTable              * objs,
                                          guint                     indlen,
                                          void                    * magic);
static void discover_int_finish          (void                    * magic);
static void discover_int_error           (void                    * magic);
static void discover_ip_row              (GHashTable              * objs,
                                          guint                     indlen,
                                          void                    * magic);
static void discover_ip_finish           (void                    * magic);
static void discover_ip_error            (void                    * magic);

/*****************************************************************************
**
**  gxsnmp_host_dialog_get_type()
**
*****************************************************************************/

GtkType
gxsnmp_host_dialog_get_type ()
{
  static GtkType ghd_type = 0;

  if (!ghd_type)
    {
      GtkTypeInfo ghd_info =
      {
        "GXsnmp_host_dialog",
        sizeof (GXsnmp_host_dialog),
        sizeof (GXsnmp_host_dialogClass),
        (GtkClassInitFunc) host_dialog_class_init,
        (GtkObjectInitFunc) host_dialog_init,
        /* reserved 1 */ NULL,
        /* reserved 2 */ NULL,
	(GtkClassInitFunc) NULL,
      };
      ghd_type = gtk_type_unique (gnome_property_dialog_get_type (), &ghd_info);
    }
  return ghd_type;
}

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

static void
host_dialog_class_init (GXsnmp_host_dialogClass *class)
{
}

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

static void
host_dialog_init (GXsnmp_host_dialog *dialog)
{
  GtkWidget * label;
  GtkWidget * separator;
  GtkWidget * button_box;
  GtkWidget * pixmap;
  GtkWidget * combo;
  GtkWidget * vbox;
  GtkWidget * hbox;
  GtkWidget * frame;
  GtkWidget * table;
  gint        i, c_width, c_height;

  D_FUNC_START;
  dialog->transport_names  = g_transport_name_list ();
  dialog->transport_values = g_transport_model_list ();
  dialog->stable  = NULL;
  dialog->if_desc = NULL;
  dialog->host    = NULL;
  dialog->request = NULL;
  dialog->table = gtk_table_new (6, 5, FALSE);
  gtk_table_set_row_spacings (GTK_TABLE (dialog->table), 7);
  gtk_table_set_col_spacings (GTK_TABLE (dialog->table), 7);
  gtk_container_border_width (GTK_CONTAINER (dialog->table), 7);
  gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox),
		      dialog->table, TRUE, TRUE, 0);
  gtk_widget_show (dialog->table);

  c_width  = gdk_string_width (dialog->table->style->font, "xW") / 2;
  c_height = dialog->table->style->font->ascent + 
             dialog->table->style->font->descent;

/*
**  Column one contains the host editing panel and the Discover button
*/

  label = gtk_label_new (_("Host Information"));
  gtk_table_attach (GTK_TABLE (dialog->table), label,
		    0, 1, 0, 1, 0, 0, 0, 0);

  dialog->host_panel = gxsnmp_host_panel_new ();
  gtk_table_attach (GTK_TABLE (dialog->table), dialog->host_panel,
                    0, 1, 1, 2, 0, 0, 0, 0);
  gtk_widget_show (dialog->host_panel);

  frame = gtk_frame_new(_("Discovery parameters"));
  gtk_table_attach (GTK_TABLE (dialog->table), frame,
                    0, 1, 2, 3, 0, 0, 0, 0);
  
  table = gtk_table_new (2, 2, FALSE);
  gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  gtk_container_add (GTK_CONTAINER (frame), table);
  gtk_container_border_width(GTK_CONTAINER(table), 4);

  label = gtk_label_new (_("SNMP"));
  gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);

  combo = gtk_combo_new ();
  dialog->host_discover_list = GTK_COMBO (combo)->list;
  gtk_entry_set_editable (GTK_ENTRY (GTK_COMBO (combo)->entry), FALSE);
  gtk_table_attach (GTK_TABLE (table), combo, 1, 2, 0, 1, 0, 0, 0, 0);

  label = gtk_label_new (_("Transport"));
  gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);

  dialog->host_discover_combo = gtk_combo_new ();
  gtk_table_attach (GTK_TABLE (table), dialog->host_discover_combo, 
                    1, 2, 1, 2, 0, 0, 0, 0);
  gtk_combo_set_popdown_strings (GTK_COMBO(dialog->host_discover_combo), 
                                 dialog->transport_names);

  button_box = gtk_hbutton_box_new ();
  gtk_hbutton_box_set_spacing_default (4);
  gtk_table_attach (GTK_TABLE (dialog->table), button_box,
                    0, 1, 3, 4, 0, 0, 0, 0);

  pixmap = gnome_stock_new_with_icon (GNOME_STOCK_PIXMAP_SEARCH);
  dialog->host_discover_button = gnome_pixmap_button (pixmap, "Discover Host");
  gtk_box_pack_start (GTK_BOX (button_box), dialog->host_discover_button,
                      TRUE, TRUE, 0);

  separator = gtk_vseparator_new ();
  gtk_table_attach (GTK_TABLE (dialog->table), separator, 1, 2, 0, 4,
                    0, GTK_EXPAND | GTK_FILL, 0, 0);

/*
**  Column two is the interface editing panel and the interface list
*/

  label = gtk_label_new (_("Interface Information"));
  gtk_table_attach (GTK_TABLE (dialog->table), label,
                    2, 3, 0, 1, 0, 0, 0, 0);

  vbox = gtk_vbox_new (FALSE, 4);
  gtk_table_attach (GTK_TABLE (dialog->table), vbox,
                    2, 3, 1, 2, 0, 0, 0, 0);

/*
**  Only one of {dialog->interface_vbox, dialog->no_interface_panel } will
**  be shown at once, so don't gtk_widget_show either of them.
*/

  dialog->interface_vbox = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), dialog->interface_vbox, 0, 0, 0);

  dialog->interface_panel = 
	gxsnmp_interface_panel_new (dialog->transport_names,
                                    dialog->transport_values);
  gtk_box_pack_start (GTK_BOX (dialog->interface_vbox), 
		      dialog->interface_panel, TRUE, TRUE, 0);

  hbox = gtk_hbox_new (FALSE, 4);
  gtk_box_pack_start (GTK_BOX (dialog->interface_vbox), hbox, 0, 0, 4);

  label = gtk_label_new (_("SNMP"));
  gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
  gtk_box_pack_start (GTK_BOX (hbox), label, 0, 0, 0);

  combo = gtk_combo_new ();
  dialog->snmp_combo_list = GTK_COMBO (combo)->list;
  gtk_entry_set_editable (GTK_ENTRY (GTK_COMBO (combo)->entry), FALSE);
  gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);

  dialog->no_interface_panel = gtk_label_new ("Host has no interfaces");
  gtk_box_pack_start (GTK_BOX (vbox), dialog->no_interface_panel, 
		      TRUE, TRUE, 0);

  dialog->interface_scroll = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy 
			(GTK_SCROLLED_WINDOW (dialog->interface_scroll),
                         GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
  gtk_table_attach (GTK_TABLE (dialog->table), dialog->interface_scroll,
                    2, 3, 2, 3, 
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);


  dialog->interface_clist = gtk_clist_new_with_titles (INTERFACE_CLIST_COLUMNS,
						       interface_clist_titles);
  gtk_clist_set_selection_mode (GTK_CLIST (dialog->interface_clist),
                                GTK_SELECTION_BROWSE);
  gtk_clist_column_titles_passive (GTK_CLIST (dialog->interface_clist));
  for (i = 0; i < INTERFACE_CLIST_COLUMNS; i++)
    gtk_clist_set_column_width (GTK_CLIST (dialog->interface_clist), i,
                                c_width * interface_clist_title_widths[i]);
  gtk_container_add (GTK_CONTAINER(dialog->interface_scroll), 
		     dialog->interface_clist);

  button_box = gtk_hbutton_box_new ();
  gtk_hbutton_box_set_spacing_default (4);
  gtk_table_attach (GTK_TABLE (dialog->table), button_box,
                    2, 3, 3, 4, 0, 0, 0, 0);

  pixmap = gnome_stock_pixmap_widget (GTK_WIDGET(dialog),
                                      "gxsnmp/add_host.xpm");
  dialog->interface_add_button = gnome_pixmap_button (pixmap, "Add");
  gtk_box_pack_start (GTK_BOX (button_box), dialog->interface_add_button, 
		      TRUE, TRUE, 0);

  pixmap = gnome_stock_pixmap_widget (GTK_WIDGET(dialog),
                                      "gxsnmp/del_host.xpm");
  dialog->interface_delete_button = gnome_pixmap_button (pixmap, "Delete");
  gtk_box_pack_start (GTK_BOX (button_box), dialog->interface_delete_button, 
		      TRUE, TRUE, 0);

  separator = gtk_vseparator_new ();
  gtk_table_attach (GTK_TABLE (dialog->table), separator, 3, 4, 0, 4,
                    0, GTK_EXPAND | GTK_FILL, 0, 0);

/* 
**  Column three is the display editing panel and display list
*/

  label = gtk_label_new (_("Display Information"));
  gtk_table_attach (GTK_TABLE (dialog->table), label,
                    4, 5, 0, 1, 0, 0, 0, 0);

/*
**  Now create the graph panel.  Don't gtk_widget_show() either of the 
**  graph_panel / no_graph_panel widgets, because only one of them is
**  ever shown at once.
*/

  vbox = gtk_vbox_new (FALSE, 0);
  gtk_table_attach (GTK_TABLE (dialog->table), vbox,
		    4, 5, 1, 2, 0, 0, 0, 0);

  dialog->graph_panel = gxsnmp_graph_panel_new ();
  gtk_box_pack_start (GTK_BOX (vbox), dialog->graph_panel, TRUE, TRUE, 0);
  dialog->no_graph_panel = gtk_label_new ("Host not displayed on this map");
  gtk_box_pack_start (GTK_BOX (vbox), dialog->no_graph_panel, TRUE, TRUE, 0);

  dialog->graph_scroll = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (dialog->graph_scroll),
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
  gtk_table_attach (GTK_TABLE (dialog->table), dialog->graph_scroll,
                    4, 5, 2, 3, 
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
  
  dialog->graph_clist = gtk_clist_new_with_titles (GRAPH_CLIST_COLUMNS,
                                                   graph_clist_titles);
  gtk_clist_set_selection_mode (GTK_CLIST (dialog->graph_clist),
                                GTK_SELECTION_BROWSE);
  gtk_clist_column_titles_passive (GTK_CLIST (dialog->graph_clist));
  for (i = 0; i < GRAPH_CLIST_COLUMNS; i++)
    gtk_clist_set_column_width (GTK_CLIST (dialog->graph_clist), i,
                                c_width * graph_clist_title_widths[i]);
  gtk_container_add (GTK_CONTAINER(dialog->graph_scroll),
                     dialog->graph_clist);

  button_box = gtk_hbutton_box_new ();
  gtk_table_attach (GTK_TABLE (dialog->table), button_box,
		    4, 5, 3, 4, 0, 0, 0, 0);

  pixmap = gnome_stock_pixmap_widget (GTK_WIDGET(dialog),
                                      "gxsnmp/add_host.xpm");
  dialog->graph_show_button = gnome_pixmap_button (pixmap, "Show");
  gtk_box_pack_start (GTK_BOX (button_box), dialog->graph_show_button, 
		      TRUE, TRUE, 0);

  pixmap = gnome_stock_pixmap_widget (GTK_WIDGET(dialog),
                                      "gxsnmp/del_host.xpm");
  dialog->graph_hide_button = gnome_pixmap_button (pixmap, "Hide");
  gtk_box_pack_start (GTK_BOX (button_box), dialog->graph_hide_button, 
		      TRUE, TRUE, 0);

/*
**  Now connect up all our signals
*/
  /* Apply button */
  gtk_signal_connect (GTK_OBJECT (dialog), "apply",
                      GTK_SIGNAL_FUNC (host_apply_cb), dialog);
  /* Help button */
  gtk_signal_connect (GTK_OBJECT (dialog), "help",
		      (GtkSignalFunc) host_dialog_help_cb, dialog);
  /* Close button */
  gtk_signal_connect (GTK_OBJECT (dialog), "close",
		      GTK_SIGNAL_FUNC (host_close_cb), dialog);
  
  gtk_signal_connect (GTK_OBJECT (dialog->host_panel), "changed",
                      GTK_SIGNAL_FUNC (host_panel_changed_cb), dialog);

  gtk_signal_connect (GTK_OBJECT (dialog->host_discover_button), "clicked",
                      GTK_SIGNAL_FUNC (host_discover_button_cb), dialog);

  gtk_signal_connect (GTK_OBJECT (dialog->interface_panel), "changed",
                      GTK_SIGNAL_FUNC (interface_panel_changed_cb), dialog);

  gtk_signal_connect (GTK_OBJECT (dialog->snmp_combo_list), "select_child",
		      GTK_SIGNAL_FUNC (interface_snmp_changed_cb), dialog);

  gtk_signal_connect (GTK_OBJECT (dialog->host_discover_list), "select_child",
		      GTK_SIGNAL_FUNC (discover_snmp_changed_cb), dialog);

  gtk_signal_connect (GTK_OBJECT (dialog->interface_clist), "select_row",
                      GTK_SIGNAL_FUNC (interface_select_row_cb), dialog);

  gtk_signal_connect (GTK_OBJECT (dialog->interface_add_button), "clicked",
                      GTK_SIGNAL_FUNC (interface_add_button_cb), dialog);

  gtk_signal_connect (GTK_OBJECT (dialog->interface_delete_button), "clicked",
                      GTK_SIGNAL_FUNC (interface_delete_button_cb), dialog);

  gtk_signal_connect (GTK_OBJECT(dialog->graph_panel), "changed",
                      GTK_SIGNAL_FUNC (graph_changed_cb), dialog);

  gtk_signal_connect (GTK_OBJECT (dialog->graph_clist), "select_row",
                      GTK_SIGNAL_FUNC (graph_select_row_cb), dialog);

  gtk_signal_connect (GTK_OBJECT (dialog->graph_show_button), "clicked",
                      GTK_SIGNAL_FUNC (graph_show_button_cb), dialog);

  gtk_signal_connect (GTK_OBJECT (dialog->graph_hide_button), "clicked",
                      GTK_SIGNAL_FUNC (graph_hide_button_cb), dialog);
  gtk_widget_show_all (dialog);
  D_FUNC_END;
}
/****************************************************************************
 * Helper function
 ***************************************************************************/
static void
free_hash_element (void *key, void *val, void *data)
{
  if (val)
    g_free (val);
  if (key)
    g_free (key);
}
static void
host_dialog_clean(GXsnmp_host_dialog *dialog)
{
/* Cancel any hanging request */
  D_FUNC_START;
  if (dialog->request)
    g_remove_request(dialog->request);
  dialog->request = NULL;
  if (dialog->stable)
    g_snmp_table_destroy(dialog->stable);
  dialog->stable = NULL;
  if (dialog->if_desc)
    {
      g_hash_table_foreach (dialog->if_desc, free_hash_element, NULL);
      g_hash_table_destroy (dialog->if_desc);
      dialog->if_desc = NULL;
    }
  if (dialog->host)
    g_free(dialog->host);
  D_FUNC_END;
}
/****************************************************************************
 * Callback for the help button. 
 ***************************************************************************/
static void
host_dialog_help_cb (GtkWidget *widget, gpointer data)
{
  gchar *tmp;
  D_FUNC_START;
  tmp = gnome_help_file_find_file ("gxsnmp", "add-hosts.html");
  if (tmp)
    {
      gnome_help_goto (0, tmp);
      g_free (tmp);
    }
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for the "changed" signal from the host panel
**
******************************************************************************/

static void 
host_panel_changed_cb (GtkWidget * widget, gpointer data)
{
  D_FUNC_START;
  GXSNMP_HOST_DIALOG (data)->host_dirty = TRUE;
  set_dialog_state (data);
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for the "clicked" signal from the "Discover host" button
**
******************************************************************************/


static void 
host_discover_button_cb (GtkWidget * widget, gpointer data)
{
  GXsnmp_host_dialog * dialog;
  DB_snmp            * dbs;
  GSList             * objs;

  D_FUNC_START;
  g_return_if_fail (data != NULL);
  dialog = GXSNMP_HOST_DIALOG (data);
  d_print (DEBUG_DUMP, "Looking up snmp config %d\n", dialog->discover_rowid);
  dbs = g_sqldb_row_find (snmp_sqldb, "_rowid", &dialog->discover_rowid);
  if (!dbs) 
    return;

  host_dialog_clean(dialog);

  dialog->host = g_malloc(sizeof(host_snmp));
  dialog->host->rcomm   = dbs->read_c ? g_strdup (dbs->read_c) : "public";
  dialog->host->wcomm   = dbs->write_c ? g_strdup (dbs->write_c) : "private";
  dialog->host->retries = dbs->retries;
/* Setup DNS hostname for initial SNMP queries. This is yet another candidate
   for strange interface hangs */
  dialog->host->name    = gtk_entry_get_text (GTK_ENTRY 
                           (GXSNMP_HOST_PANEL(dialog->host_panel)->dns_name));
  dialog->host->domain  = GPOINTER_TO_INT(
			    g_list_nth_data(dialog->transport_values,
                              g_list_position(dialog->transport_names,
                                g_list_find_custom(dialog->transport_names,
                                  gtk_entry_get_text
                                    (GTK_ENTRY (GTK_COMBO 
                                      (dialog->host_discover_combo)->entry)),
                                        (GCompareFunc)strcmp))));
  dialog->host->status  = 0;
  dialog->host->port    = dbs->port;
  dialog->host->timeout = dbs->timeout;
  dialog->host->version = dbs->version;
  dialog->host->magic   = dialog;

  dialog->host->time_callback  = discover_timeout;
  dialog->host->done_callback  = discover_system_value;
  objs = NULL;
  g_pdu_add_oid (&objs, syscontact, oidlen(syscontact), SNMP_NULL, NULL);
  g_pdu_add_oid (&objs, sysdescr, oidlen(sysdescr), SNMP_NULL, NULL);
/* Maybe add a third field for the location? */
  d_print (DEBUG_DUMP, "Fetching system info, (%s) to %s\n", 
	   dialog->host->rcomm, dialog->host->name);
  dialog->request = g_async_get(dialog->host, objs);
  D_FUNC_END;
}
/*
 * snmp callback
 */
static gboolean
discover_system_value (host_snmp *host, void *magic, SNMP_PDU *spdu, 
                       GSList *objs)
{
  char                  buf[1024];
  struct _SNMP_OBJECT * obj;
  DB_host             * dbh;
  gboolean              changed;
  GXsnmp_host_dialog  * dialog;

  D_FUNC_START;


  dialog = (GXsnmp_host_dialog *) magic;
  dialog->request = NULL;
  if (host->status == 0)
    {
      dbh = dialog->selected_host;

      g_assert(objs); /* Might barf on broken SNMP agents */
      obj = objs->data;
      g_snmp_printf (buf, sizeof(buf), obj);
      changed = FALSE;
      d_print (DEBUG_DUMP, "Contact information returned. %s\n", buf);
      changed |= update_string_field (&dbh->contact, buf);
      objs = objs->next;
      g_assert(objs); /* Might barf on broken SNMP agents */
      obj = objs->data;
      g_snmp_printf (buf, sizeof(buf), obj);
      d_print (DEBUG_DUMP, "Description information returned, %s\n", buf);
      changed |= update_string_field (&dbh->description, buf);
      if (dbh->rowid == 0)
	{
	  d_print (DEBUG_DUMP, "Adding a new host? %s\n", dbh->name);
          g_sqldb_row_add(host_sqldb, dbh);
	}
      else
        if (changed)
	  {
	    d_print (DEBUG_DUMP, "Update an existing host (%d), %s\n",
		     dbh->rowid, dbh->name);
            g_sqldb_row_update(host_sqldb, dbh);
	  }
      dialog->host_dirty = FALSE;
      d_print (DEBUG_DUMP, "Calling update on %s\n", dbh->name);
      host_refresh_cb (host_sqldb, dbh, G_SQLDB_CB_UPDATE, dialog);

/* Good. We got basic info from the host. Now try to load interfaces. We 
   query for the description and for both the admin status and type. Loopback
   interfaces and interfaces configured down will not be included in our
   further processing */
      objs = NULL;
      d_print (DEBUG_DUMP, "Walking the interface list\n");
      g_pdu_add_oid (&objs, ifdescr, oidlen(ifdescr), SNMP_NULL, NULL);
      g_pdu_add_oid (&objs, ifastatus, oidlen(ifastatus), SNMP_NULL, NULL);
      g_pdu_add_oid (&objs, iftype, oidlen(iftype), SNMP_NULL, NULL);
      g_pdu_add_oid (&objs, ifindex, oidlen(ifindex), SNMP_NULL, NULL);
      dialog->stable  = g_snmp_table_new(dialog->host, objs, 
                          discover_int_error, discover_int_row, 
                          discover_int_finish, dialog);
      d_print (DEBUG_DISCOVERY, "Get table (%s) to %s\n", dialog->host->rcomm, 
                  dialog->host->name);
      g_snmp_table_get(dialog->stable);
    }
  return TRUE;
  D_FUNC_END;
}
/*
 * timeout callback
 */
void
discover_timeout (host_snmp *host, void *magic)
{
  GXsnmp_host_dialog  * dialog;
  D_FUNC_START;
  dialog = (GXsnmp_host_dialog *) magic;
  dialog->request = NULL;
  g_free(dialog->host);
  dialog->host = NULL;
  D_FUNC_END;
  return;
}
static void
discover_int_row (GHashTable *objs, guint indlen, void *magic)
{
  char                  intname[1024];
  guint                 type, status, index, *iptr, i;
  struct _SNMP_OBJECT * obj;
  GXsnmp_host_dialog  * dialog;
  D_FUNC_START;
  g_return_if_fail (magic != NULL);
  dialog = (GXsnmp_host_dialog *) magic;
  i = 0;
  obj = g_hash_table_lookup(objs, &i);
  d_print(DEBUG_DISCOVERY, "obj = %x\n", obj);
  if (obj)
    g_snmp_printf (intname, sizeof(intname), obj);
  else
    g_snprintf (intname, sizeof(intname), "(unknown)");
  i = 1;
  obj = g_hash_table_lookup(objs, &i);
  d_print(DEBUG_DISCOVERY, "obj = %x\n", obj);
  if (obj)
    status = obj->syntax.ul[0];
  else
    status = 1; /* No status available. Assume UP */
  i = 2;
  obj = g_hash_table_lookup(objs, &i);
  d_print(DEBUG_DISCOVERY, "obj = %x\n", obj);
  if (obj)
    type = obj->syntax.ul[0];
  else
    type = 1; /* No type known, assume OTHER */
  i = 3;
  obj = g_hash_table_lookup(objs, &i);
  d_print(DEBUG_DISCOVERY, "obj = %x\n", obj);
  if (obj)
    index = obj->syntax.ul[0];
  else
    index = 0; /* No index returned by agent. Weird... */
  iptr = g_malloc(sizeof(gint));
  *iptr = index;
  d_print(DEBUG_DISCOVERY, "Received row: %s (%d)\n", intname, index);

  if ((status == 1) && (type != 24)) /* Only use interfaces that are up
                                        and not loopback */
    {
      if (!dialog->if_desc)
        dialog->if_desc = g_hash_table_new (g_int_hash, g_int_equal);
      g_hash_table_insert (dialog->if_desc, iptr, g_strdup(intname));
    }
  D_FUNC_END;
}
static void discover_int_finish (void * magic)
{
  GSList              * objs;
  GXsnmp_host_dialog  * dialog;
  D_FUNC_START;
  g_return_if_fail (magic != NULL);
  dialog = (GXsnmp_host_dialog *) magic;
  g_snmp_table_destroy(dialog->stable);
  dialog->stable = NULL;
/* Good. We got the full interface table. Now collect the IP adresses and
   add them to our database :) */
  objs = NULL;
  g_pdu_add_oid (&objs, ipaddr, oidlen(ipaddr), SNMP_NULL, NULL);
  g_pdu_add_oid (&objs, ipnetmask, oidlen(ipnetmask), SNMP_NULL, NULL);
  g_pdu_add_oid (&objs, ipifindex, oidlen(ipifindex), SNMP_NULL, NULL);
  dialog->stable  = g_snmp_table_new(dialog->host, objs, discover_ip_error, 
                      discover_ip_row, discover_ip_finish, dialog);
  g_snmp_table_get(dialog->stable);
  D_FUNC_END;
}
static void discover_int_error (void * magic)
{
  GXsnmp_host_dialog  * dialog;
  D_FUNC_START;
  g_return_if_fail (magic != NULL);
  dialog = (GXsnmp_host_dialog *) magic;
  g_snmp_table_destroy(dialog->stable);
  dialog->stable = NULL;
  if (dialog->if_desc)
    {
      g_hash_table_foreach (dialog->if_desc, free_hash_element, NULL);
      g_hash_table_destroy (dialog->if_desc);
      dialog->if_desc = NULL;
    }
  g_free(dialog->host);
  dialog->host = NULL;
  D_FUNC_END;
}
static void
discover_ip_row (GHashTable *objs, guint indlen, void *magic)
{
char                  addr[1024], mask[1024];
char                * descr;
guint                 index, i;
struct _SNMP_OBJECT * obj;
GXsnmp_host_dialog  * dialog;
DB_host             * dbh;
DB_interface        * dbi;

  D_FUNC_START;

  g_return_if_fail (magic != NULL);
  dialog = (GXsnmp_host_dialog *) magic;
  i = 0;
  obj = g_hash_table_lookup(objs, &i);
  if (obj)
    g_snmp_printf (addr, sizeof(addr), obj);
  else
    g_snprintf(addr, sizeof(addr), "(none)");
  i = 1;
  obj = g_hash_table_lookup(objs, &i);
  if (obj)
    g_snmp_printf (mask, sizeof(mask), obj);
  else
    g_snprintf(mask, sizeof(mask), "(none)");
  i = 2;
  obj = g_hash_table_lookup(objs, &i);
  if (obj)
    index = obj->syntax.ul[0];
  else
    index = 0;
  d_print(DEBUG_DISCOVERY, "Received row: %s/%s (%d)\n", addr, mask, index);
  if ((dialog->if_desc) && (descr = (gchar *)g_hash_table_lookup(dialog->if_desc, &index)))
    {
      fprintf(stderr,"CREATE & ADD interface\n");
      d_print(DEBUG_DISCOVERY, "Found: %s : %s (%s)\n", descr, addr, mask);
      dbh            = dialog->selected_host;

      dbi            = interface_create();
      dbi->host      = dbh->rowid;
      dbi->snmp      = dialog->discover_rowid;
      dbi->transport = AF_INET;					/* This MIB only returns IPv4 stuff */
      dbi->name      = g_strdup(descr);
      dbi->address   = g_strdup(addr);
      dbi->netmask   = g_strdup(mask);
      dialog->selected_interface = dbi;
      g_sqldb_row_add(interface_sqldb, dbi);
      host_refresh_cb (host_sqldb, dbh, G_SQLDB_CB_UPDATE, dialog);
    }
  D_FUNC_END;
}
static void discover_ip_finish (void * magic)
{
  GXsnmp_host_dialog  * dialog;
  D_FUNC_START;
  g_return_if_fail (magic != NULL);
  dialog = (GXsnmp_host_dialog *) magic;
  g_snmp_table_destroy(dialog->stable);
  dialog->stable = NULL;
  if (dialog->if_desc)
    {
      g_hash_table_foreach (dialog->if_desc, free_hash_element, NULL);
      g_hash_table_destroy (dialog->if_desc);
      dialog->if_desc = NULL;
    }
  g_free(dialog->host);
  dialog->host = NULL;
  D_FUNC_END;
}
static 
void discover_ip_error (void * magic)
{
  GXsnmp_host_dialog  * dialog;
  D_FUNC_START;
  g_return_if_fail (magic != NULL);
  dialog = (GXsnmp_host_dialog *) magic;
  g_snmp_table_destroy(dialog->stable);
  dialog->stable = NULL;
  if (dialog->if_desc)
    {
      g_hash_table_foreach (dialog->if_desc, free_hash_element, NULL);
      g_hash_table_destroy (dialog->if_desc);
      dialog->if_desc = NULL;
    }
  if (dialog->host)
    g_free(dialog->host);
  dialog->host = NULL;
  D_FUNC_END;
}
/******************************************************************************
**
**  Callback handler for the "changed" signal from the interface panel
**
**  There should probably be a separate signal and access function for the
**  name field, instead of digging into the widget contents like this :-|
**
******************************************************************************/

static void 
interface_panel_changed_cb (GtkWidget * widget, gpointer data)
{
  GXsnmp_host_dialog * dialog;
  GtkCList	     * clist;
  GtkWidget	     * name_entry;

  D_FUNC_START;
  g_return_if_fail (data != NULL);
  dialog = GXSNMP_HOST_DIALOG (data);
  clist  = GTK_CLIST (dialog->interface_clist);
  if (clist)
    {
      name_entry = GXSNMP_INTERFACE_PANEL (dialog->interface_panel)->name;
      if (name_entry)
	gtk_clist_set_text (clist, dialog->interface_row, 0, 
			    gtk_entry_get_text (GTK_ENTRY (name_entry)));
      
      dialog->interface_dirty = TRUE;
      set_dialog_state (dialog);
    }
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for when the user changes the SNMP configuration.
**
******************************************************************************/

static void
interface_snmp_changed_cb (GtkList     * list,
			   GtkListItem * list_item,
			   gpointer      data)
{
  GXsnmp_host_dialog * dialog;
  D_FUNC_START;
  g_return_if_fail (data != NULL);
  dialog = GXSNMP_HOST_DIALOG (data);

  dialog->snmp_rowid = 
	GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (list_item), 
					      "rowid"));
  dialog->interface_dirty = TRUE;
  set_dialog_state (dialog);
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for when the user changes the SNMP configuration.
**
******************************************************************************/

static void
discover_snmp_changed_cb (GtkList     * list,
			  GtkListItem * list_item,
			  gpointer      data)
{
  GXsnmp_host_dialog * dialog;
  D_FUNC_START;
  g_return_if_fail (data != NULL);
  dialog = GXSNMP_HOST_DIALOG (data);

  dialog->discover_rowid = 
	GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (list_item), 
					      "rowid"));
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for the "select_row" signal from the interface clist
**
**  This callback handler is both directly called from the interface clist
**  widget when the user selects a new interface, and also may be called
**  from the interface_add_button_cb callback when a new interface is 
**  created.
**
**  The rowid of the selected interface is pulled from the data field of
**  the selected clist row. If the rowid associated with the clist row 
**  is zero, then this is a new row, and the pointer to it is already 
**  in dialog->selected_interface.
**
**  If an interface is unnamed, then pull the name from the CList and
**  assign it to the interface.  This will give the interface an initial
**  name like, "Unnamed 0" which the user may then change.
**  
******************************************************************************/

static void
interface_select_row_cb (GtkWidget      * widget,
			 gint	          row,
		         gint	          column,
		         GdkEventButton * event,
			 gpointer         data)
{
  GXsnmp_host_dialog * dialog;
  GtkCList	     * clist;
  DB_interface       * dbi;
  GtkList	     * list;
  gchar              * label;
  guint		       rowid;
  GList		     * gl;

  D_FUNC_START;

  g_return_if_fail (data != NULL);
  dialog = GXSNMP_HOST_DIALOG (data);
  clist  = GTK_CLIST (widget);
  d_print (DEBUG_TRACE, "rowid (%d)\n", row);
  dialog->interface_row = row;
  rowid = GPOINTER_TO_INT (gtk_clist_get_row_data (clist, row));

  if (rowid) 	/* if rowid == 0, then dialog->selected_interface is already */
    {           /* correct */
      fprintf(stderr,"FINDING INTERFACE(%u)\n", rowid);
#if 0
      interface_sqldb->filter = &db_find_by_rowid;
      db_filter_modify(&db_find_by_rowid, "_rowid", &rowid);
      glr = db_find_rows(interface_sqldb);    
      if(glr)
        dialog->selected_interface = glr->data;
      else dialog->selected_interface = 0;
#endif
          dialog->selected_interface = g_sqldb_row_find (interface_sqldb, "_rowid", &rowid);

    }
  dbi = 0;
  dbi = dialog->selected_interface;
  if (!dbi){
    fprintf(stderr,"Program is going to abort, because we couldn't match a interface\n");
    fprintf(stderr,"using db_find_rows() with rowid %d in filter.\n", rowid);
    fprintf(stderr,"ie, this rowid was not found in interface table.\n");
    g_assert_not_reached ();    /* Interface is missing! */
  }

  if (!dbi->name)
    {
      gtk_clist_get_text (clist, row, column, &label);
      dbi->name = g_strdup (label);
    }

  gtk_signal_handler_block_by_data (GTK_OBJECT (dialog->interface_panel),
				    dialog);
  gxsnmp_interface_panel_put_data (GXSNMP_INTERFACE_PANEL 
				     (dialog->interface_panel), dbi);
  gtk_signal_handler_unblock_by_data (GTK_OBJECT (dialog->interface_panel),
				      dialog);

  list = GTK_LIST (dialog->snmp_combo_list);
  gl = list->children;
  while (gl)
    {
      GtkListItem * item = GTK_LIST_ITEM (gl->data);

      if (dbi->snmp == GPOINTER_TO_INT (gtk_object_get_data 
					       (GTK_OBJECT (item), "rowid")))
	{
	  gtk_signal_handler_block_by_data (GTK_OBJECT (list), dialog);
	  gtk_list_select_child (list, GTK_WIDGET (item));
	  gtk_signal_handler_unblock_by_data (GTK_OBJECT (list), dialog);
	  break;
	}
      gl = gl->next;
    }
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for the "clicked" signal from the "Add interface" button
**
**  Create a new interface object, with the default settings, but do not
**  link it into the database yet.  Instead, leave the rowid as zero and
**  synthesize a call to host_refresh_cb to select the new interface entry
**
******************************************************************************/

static void 
interface_add_button_cb (GtkWidget * widget, gpointer data)
{
  GXsnmp_host_dialog * dialog;
  DB_host            * dbh;
  DB_interface       * dbi;

  D_FUNC_START;
  dialog = GXSNMP_HOST_DIALOG (data);
  dbh = dialog->selected_host;
fprintf(stderr,"interface_add_button_cb():dbh->name(%s)\n", dbh->name);

  dbi 		 = interface_create ();
  dbi->host      = dbh->rowid;
  dbi->snmp      = 1; 			/* 1 is the default SNMP rowid */
  dbi->transport = AF_INET;
  dbi->name      = g_strdup ("Unnamed");

  dialog->selected_interface = dbi;
  dialog->interface_dirty    = TRUE;

  host_refresh_cb (host_sqldb, dbh, G_SQLDB_CB_UPDATE, dialog);
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for the "clicked" signal from the "Delete interface"
**  button
**
******************************************************************************/

static void 
interface_delete_button_cb (GtkWidget * widget, gpointer data)
{                               
  
  GXsnmp_host_dialog * dialog;
  DB_host            * dbh;
  DB_interface       * dbi;
  DB_snmp	     * dbs;

  D_FUNC_START;
  dialog = GXSNMP_HOST_DIALOG (data);
  dbh    = dialog->selected_host;
  dbi    = dialog->selected_interface;
  dbs	 = dbi->DB_snmp;

fprintf(stderr,"Selected host(%d)(%s)\n", dbh->rowid, dbh->name);
fprintf(stderr,"Removing interface(%d)(%s)\n", dbi->rowid, dbi->name);

  if (dbi->rowid != 0){		/* If interface is in database */
    fprintf(stderr,"interface_delete()\n");
    interface_delete (dbi);	/* then delete it */
  }
  interface_destroy (dbi);	/* Destroy the interface block itself */
  dialog->interfaces--;		/* Decrement the interface count */
  if (dialog->interfaces)	/* If the host has any interfaces left */
    {				/* then select another interface */
      guint rowid;

      if (dialog->interface_row < dialog->interfaces)
	dialog->interface_row++;
      else
	dialog->interface_row--;

      d_print (DEBUG_DUMP, "Selected row (%d)\n", dialog->interface_row);
      rowid = GPOINTER_TO_INT (gtk_clist_get_row_data (
					GTK_CLIST (dialog->interface_clist), 
					dialog->interface_row));
      g_assert (rowid != 0);
      dialog->selected_interface = interface_find_by_rowid (rowid);
    }
  else 
    dialog->selected_interface = NULL;
  dialog->interface_dirty = FALSE;
  host_refresh_cb (host_sqldb, dbh, G_SQLDB_CB_UPDATE, dialog);
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for the "changed" signal from the graph panel
**
******************************************************************************/

static void 
graph_changed_cb (GtkWidget * widget, gpointer data)
{
  D_FUNC_START;
  GXSNMP_HOST_DIALOG (data)->graph_dirty = TRUE;
  set_dialog_state (data);
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for the "select_row" signal from the graph clist
**
******************************************************************************/

static void
graph_select_row_cb (GtkWidget      * widget,
                     gint             row,
                     gint             column,
                     GdkEventButton * event,
                     gpointer         data)
{
  GXsnmp_host_dialog * dialog;
  GtkCList	     * clist;
  guint   	     * row_data;
  guint		       graph_rowid;

  D_FUNC_START;
  dialog = GXSNMP_HOST_DIALOG (data);
  clist  = GTK_CLIST (widget);

  dialog->graph_row = row;
  row_data = gtk_clist_get_row_data (clist, row);
  graph_rowid = row_data[1];

  if (graph_rowid)
    dialog->selected_graph = graph_find_by_rowid (graph_rowid);
  else
    dialog->selected_graph = NULL;

  if (dialog->selected_graph)
    {
      gtk_signal_handler_block_by_data (GTK_OBJECT (dialog->graph_panel), 
				        dialog);
      gxsnmp_graph_panel_put_data (GXSNMP_GRAPH_PANEL (dialog->graph_panel), 
		 		   dialog->selected_graph);
      gtk_signal_handler_unblock_by_data (GTK_OBJECT (dialog->graph_panel), 
					  dialog);
    }
  set_dialog_state (dialog);
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for the "clicked" signal from the map "Show" button
**
******************************************************************************/

static void 
graph_show_button_cb (GtkWidget * widget, gpointer data)
{

  GXsnmp_host_dialog * dialog;
  GtkCList           * clist;
  guint              * row_data;
  guint		       map_rowid;
  guint		       graph_rowid;
  DB_graph           * dbg;
  D_FUNC_START;
  g_return_if_fail (data != NULL);
  dialog = GXSNMP_HOST_DIALOG (data);
  clist  = GTK_CLIST (dialog->graph_clist);

  row_data    = gtk_clist_get_row_data (clist, dialog->graph_row);
  map_rowid   = row_data[0];
  graph_rowid = row_data[1];

  g_assert (graph_rowid == 0);	/*  Shouldn't exist yet */

  dbg            = graph_create ();
  if (!dbg)
    {
      g_warning ("graph_show_button: failed to create a new graph object.");
      D_FUNC_END;
      return;
    }
  dbg->map       = map_rowid;
  dbg->type      = DB_GRAPH_HOST;
  dbg->host      = dialog->selected_host->rowid;
  dbg->x         = dialog->x;       /* Insert saved screen */
  dbg->y         = dialog->y;       /* X and Y locations */
fprintf(stderr,"graph_add() ");
  graph_add (dbg);

  dialog->graph_dirty = TRUE;
  host_refresh_cb (host_sqldb, dialog->selected_host, 
                   G_SQLDB_CB_UPDATE, dialog);
fprintf(stderr,"host_refresh_cb()_done ");
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for the "clicked" signal from the map "Hide" button
**
******************************************************************************/

static void 
graph_hide_button_cb (GtkWidget * widget, gpointer data)
{
  GXsnmp_host_dialog * dialog;
  GtkCList           * clist;
  guint              * row_data;
  guint                graph_rowid;
  DB_graph           * dbg;

  D_FUNC_START;
  dialog = GXSNMP_HOST_DIALOG (data);
  clist  = GTK_CLIST (dialog->graph_clist);

  row_data = gtk_clist_get_row_data (clist, dialog->graph_row);
  graph_rowid = row_data[1];
  g_assert (graph_rowid != 0);

  dbg = graph_find_by_rowid (graph_rowid);
  if (dbg)
    {                      /* this is paranoid as graph_delete can handle
			      nulls */
      graph_delete (dbg);         /* Will trigger the 'destroy' callback */
      graph_destroy (dbg);
    }
  dialog->selected_graph = NULL;
  dialog->graph_dirty = TRUE;
  host_refresh_cb (host_sqldb, dialog->selected_host, 
		   G_SQLDB_CB_UPDATE, dialog);
  
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for the "apply" signal from the gnome_property_dialog
**
**  Rewrites any modified panels, and resets the dialog.
**
******************************************************************************/

static void
host_apply_cb (GtkWidget * widget, gpointer data)
{
  GXsnmp_host_dialog * dialog;
  DB_host            * dbh;
  DB_interface       * dbi;
  DB_graph           * dbg;
  gboolean             changed;
  
  D_FUNC_START;

  g_return_if_fail (widget != NULL);
  dialog = GXSNMP_HOST_DIALOG (widget);

/*
**  Add or update the host entry if necessary
*/

  dbh = dialog->selected_host;

  if (dbh)
    {
      changed = gxsnmp_host_panel_get_data
	(GXSNMP_HOST_PANEL (dialog->host_panel), dbh);
      if (dbh->rowid == 0){
        g_sqldb_row_add(host_sqldb, dbh);     /* Add a new host */
        }
      else
	if (changed){
          g_sqldb_row_update(host_sqldb, dbh);		/* Update an existing host */
        }
      dialog->host_dirty = FALSE;
    }
  else
    {
      g_warning ("host_apply_cb: eek! dialog->selected_host != NULL??"
		 "expect a core dump soon.");
    }
      
/*
**  Add or update the interface entry if necessary
*/

  dbi = dialog->selected_interface;

  if (dbi)
    {
      changed = gxsnmp_interface_panel_get_data (
                        GXSNMP_INTERFACE_PANEL (dialog->interface_panel), dbi);
      if (dbi->snmp != dialog->snmp_rowid)
        changed = TRUE;

      dbi->snmp = dialog->snmp_rowid;	/* SNMP saved from separate widget */
      if (dbi->rowid == 0){
/*added 20000701*/
        dbi->rowid == g_sqldb_highest_rowid(interface_sqldb, "_rowid");
/**/

        g_sqldb_row_add(interface_sqldb, dbi);	/* Add a new interface */
      }
      else
        if (changed)
          g_sqldb_row_update(interface_sqldb, dbi);	/* Update an existing interface */
    }
  dialog->interface_dirty = FALSE;

/*
**  Add or update the graph entry if necessary
*/

  dbg = dialog->selected_graph;

  if (dbg)
    {
      changed = gxsnmp_graph_panel_get_data (
                            GXSNMP_GRAPH_PANEL (dialog->graph_panel), dbg);
      if (dbg->rowid == 0)
        g_sqldb_row_add(graph_sqldb, dbg);	/* Add a new graph entry */
      else
        if (changed){
          dbg->details = "";
          g_sqldb_row_update(graph_sqldb, dbg);	/* Update an existing graph entry */
        }
    }

  dialog->graph_dirty = FALSE;

  host_refresh_cb (host_sqldb, dbh, G_SQLDB_CB_UPDATE, dialog);
  D_FUNC_END;
}

/******************************************************************************
**
**  Callback handler for the "close" signal from the gnome_property_dialog
**
******************************************************************************/

static gint
host_close_cb (GtkWidget * widget, gpointer data)
{
  GXsnmp_host_dialog * dialog;

  D_FUNC_START;
  g_return_val_if_fail (data != NULL, FALSE);
  g_return_val_if_fail (GXSNMP_IS_HOST_DIALOG (data), FALSE);
  dialog = GXSNMP_HOST_DIALOG (data);
  if (dialog->graph_datalets)
    g_free (dialog->graph_datalets);
  host_dialog_clean(dialog);
  D_FUNC_END;
  return FALSE;
}

/******************************************************************************
**
**  Callback function to refresh the entire dialog contents
**
******************************************************************************/

static void
host_refresh_cb (G_sqldb_table   * table, 
	         gpointer 	   row, 
	         G_sqldb_cb_type   type,  
	         gpointer 	   data)
{
  GXsnmp_host_dialog * dialog;
  DB_host	     * dbh;
  DB_map      	     * cur_dbm;
  GXsnmp_map  	     * map;
  GtkAdjustment	     * adjustment;
  GtkCList	     * clist;
  GList		     * gl;
  gint		       unnamed;		/* Counter for unnamed interfaces */
  gint		       datalet;		/* Used to allocate SNMP clist data */
  double	       old_adjustment_value;
  double	       old_adjustment_upper; 
  D_FUNC_START;
  g_return_if_fail (data != NULL);
  dialog      = GXSNMP_HOST_DIALOG (data);
  dbh         = (DB_host *) row;

  g_return_if_fail (dbh != NULL);

  if (dbh != dialog->selected_host)   /* If this refresh is for another row */
    return;			      /* then we aren't interested in it */

/*
**  Fill in the host panel 
*/

  gtk_signal_handler_block_by_data (GTK_OBJECT (dialog->host_panel), dialog);
  gxsnmp_host_panel_put_data (GXSNMP_HOST_PANEL (dialog->host_panel), dbh);
  gtk_signal_handler_unblock_by_data (GTK_OBJECT (dialog->host_panel), dialog);

/*
**  Rebuild the list of available SNMP configurations.
*/

  d_print (DEBUG_TRACE, "Rebuilding the SNMP combo list.");
  gtk_signal_handler_block_by_data (GTK_OBJECT (dialog->snmp_combo_list), 
				    dialog);
  gtk_list_clear_items (GTK_LIST (dialog->snmp_combo_list), 0, -1);
  gtk_list_clear_items (GTK_LIST (dialog->host_discover_list), 0, -1);
  gl = g_sqldb_table_list (snmp_sqldb);
  while (gl)
    {
      DB_snmp   * dbs = (DB_snmp *)gl->data;
      GtkWidget * entry;

      entry = gtk_list_item_new_with_label ((gchar *) dbs->name);
      gtk_object_set_data (GTK_OBJECT (entry), "rowid", 
			   GINT_TO_POINTER (dbs->rowid));
      gtk_widget_show (entry);
      gtk_container_add (GTK_CONTAINER (dialog->snmp_combo_list), entry);
      entry = gtk_list_item_new_with_label ((gchar *) dbs->name);
      gtk_object_set_data (GTK_OBJECT (entry), "rowid", 
			   GINT_TO_POINTER (dbs->rowid));
      gtk_widget_show (entry);
      gtk_container_add (GTK_CONTAINER (dialog->host_discover_list), entry);

      gl = gl->next;
    }
  gtk_signal_handler_unblock_by_data (GTK_OBJECT (dialog->snmp_combo_list),
				      dialog);

/*
**  Fill in the interface list
*/  

  d_print (DEBUG_TRACE, "Rebuilding the interface list.");
  adjustment = gtk_scrolled_window_get_vadjustment (
			GTK_SCROLLED_WINDOW (dialog->interface_scroll));
  old_adjustment_value = adjustment->value;
  old_adjustment_upper = adjustment->upper;

  clist = GTK_CLIST (dialog->interface_clist);
  gtk_signal_handler_block_by_data (GTK_OBJECT (clist), dialog);
  gtk_clist_freeze (clist);
  gtk_clist_clear (clist);
  unnamed = 0;
  dialog->interfaces = 0;
  gl = dbh->DB_interfaces;
  while (gl)
    {
      interface_insert_row (dialog, (DB_interface *)gl->data, &unnamed);
      gl = gl->next;
    }
  if (dialog->selected_interface)
    {
      if (dialog->selected_interface->rowid == 0)
        {
          interface_insert_row (dialog, dialog->selected_interface, &unnamed);
          gtk_adjustment_set_value (adjustment, old_adjustment_upper);
        }
      else
        gtk_adjustment_set_value (adjustment, old_adjustment_value);
    }
  gtk_signal_handler_unblock_by_data (GTK_OBJECT (clist), dialog);
  gtk_clist_thaw (clist);

/*
**  Run down the global map list and create a list item entry for each map,
**  regardless of whether or not we have a DB_graph entry for that map.
**  For each list entry, check our DB_graph table to see if we have an
**  entry with that rowid.  If so, associate the DB_graph entry with the
**  list entry.  If not, store a NULL value with the list item.
*/

  d_print (DEBUG_TRACE, "Rebuilding the map clist.\n");
  map = gxsnmp_window_get_current_map (             /* Find the currently */
                GXSNMP_WINDOW (app_info->window));  /* selected map */
  if (map)
    {
      cur_dbm = map->DB_map;                        /* and map structure */

      clist = GTK_CLIST (dialog->graph_clist);
      gtk_signal_handler_block_by_data (GTK_OBJECT (clist), dialog);
      gtk_clist_freeze (clist);
      gtk_clist_clear (clist);
      
      if (dialog->graph_datalets) 
	g_free (dialog->graph_datalets);
      gl = g_sqldb_table_list (map_sqldb);  /* Loop for each map in the list */
      dialog->graph_datalets = g_new0 (guint, 2 * g_list_length (gl));
      datalet = 0;
      while (gl)
	{
	  DB_map	*dbm = (DB_map *) gl->data;
	  DB_graph      *dbg = NULL;
	  GList	        *gl2 = dbh->DB_graphs;
	  gint          row;
	  guint         *row_data;
	  gchar	        *entry[2];

	  while (gl2)				/* Look for DB_graph entry */
	    {					/* for this host on this map */
	      DB_graph * gldbg = 
		(DB_graph *) gl2->data;
	      if (gldbg->map == dbm->rowid)
		{
		  dbg = gldbg; 		        /* Remember DB_graph pointer */
		  break;
		}
	      gl2 = gl2->next;  /* Now dbg points to the DB_graph entry for this */
	    }		    /* host on this map, or NULL if not displayed */
	  
	  entry[0] = dbm->tab;			/* Name of map */
	  entry[1] = dbg ? "Yes" : "No";		/* Display status */
	  row = gtk_clist_append (clist, entry);
	  
	  row_data = &dialog->graph_datalets[datalet];   /* Annoyingly, you can */
	  row_data[0] = dbm->rowid;			     /* only associate one */
	  row_data[1] = dbg ? dbg->rowid : 0;	     /* gpointer with a */
	  gtk_clist_set_row_data (clist, row, row_data); /* CList row */
	  datalet += 2;                                 
	  
	  if (row == dialog->graph_row)
	    {
	      gtk_signal_handler_unblock_by_data (GTK_OBJECT (clist), dialog);
	      gtk_clist_select_row (clist, row, 0);
	      gtk_signal_handler_block_by_data (GTK_OBJECT (clist), dialog);
	    }
	  gl = gl->next;
	}
      gtk_signal_handler_unblock_by_data (GTK_OBJECT (clist), dialog);
      gtk_clist_thaw (clist);
    }
  set_dialog_state (dialog);
  D_FUNC_END;
}

/******************************************************************************
**
**  Helper function used by host_refresh_cb to process a row in the 
**  interface table.  This function is entered, and exits, with the
**  clist signal-blocked.  Unblocking around the gtk_clist_select_row
**  function causes the "select_row" signal to be emitted only to the
**  currently selected interface, triggering a call to 
**  interface_select_row_cb, which fills in the panel.
**
******************************************************************************/

static void
interface_insert_row (GXsnmp_host_dialog * dialog, 
		      DB_interface       * dbi,
		      gint		  * unnamed)
{
  GtkCList * clist;
  gchar    * entry[1];
  gint       row;

  D_FUNC_START;
  d_print (DEBUG_DUMP, "Rowid (%d)\n", dbi->rowid);
  g_return_if_fail (dialog != NULL);
  clist = GTK_CLIST (dialog->interface_clist);
  if (!clist)
    {
      g_warning ("No interface clist found. Something is broke.");
      return;
    }
  if (dbi->name)
    entry[0] = g_strdup (dbi->name);
  else
    entry[0] = g_strdup_printf ("Unnamed %d", (*unnamed)++);
  row = gtk_clist_append (clist, entry);
  g_free (entry[0]);

  gtk_clist_set_row_data (clist, row, GINT_TO_POINTER (dbi->rowid));
  if (dialog->selected_interface)
    if (dialog->selected_interface == dbi)
      {
        gtk_signal_handler_unblock_by_data (GTK_OBJECT (clist), dialog);
        gtk_clist_select_row (clist, row, 0);
        gtk_signal_handler_block_by_data (GTK_OBJECT (clist), dialog);
      }
  dialog->interfaces++;
  D_FUNC_END;
}

/******************************************************************************
**
**  Subroutine to grey out the correct set of buttons to match the current
**  dialog state.  Called after each state change to the _dirty elements.
**
******************************************************************************/

static void
set_dialog_state (GXsnmp_host_dialog * dialog)
{

  D_FUNC_START;
  g_return_if_fail (dialog != NULL);

/*
**  Display the interface panel.  If we have no interfaces on this host,
**  then display the "no_interface_panel" label instead, and disable the
**  'delete interface' button.
*/

  if (dialog->selected_interface)
    {
      gtk_widget_show (dialog->interface_vbox);
      gtk_widget_hide (dialog->no_interface_panel);
      gtk_widget_set_sensitive (dialog->interface_delete_button, TRUE);
    }
  else
    {
      gtk_widget_hide (dialog->interface_vbox);
      gtk_widget_show (dialog->no_interface_panel);
      gtk_widget_set_sensitive (dialog->interface_delete_button, FALSE);
    }

/*
**  Once the interface panel is changed, you can't switch to another 
**  interface without first applying the change or cancelling.
*/

  gtk_widget_set_sensitive (dialog->interface_clist, 
			    !dialog->interface_dirty);

  gtk_widget_set_sensitive (dialog->interface_add_button, 
			    !dialog->interface_dirty);

/*
**  If the host is displayed on the current map, then enable the graph
**  panel. and the "hide" button.  Otherwise, enable the "no_graph" panel
**  and the "show" button.
*/
  if (dialog->selected_graph)
    {
      gtk_widget_show (dialog->graph_panel);
      gtk_widget_hide (dialog->no_graph_panel);
      gtk_widget_set_sensitive (dialog->graph_show_button, FALSE);
      gtk_widget_set_sensitive (dialog->graph_hide_button, TRUE);
    }
  else
    {
      gtk_widget_hide (dialog->graph_panel);
      gtk_widget_show (dialog->no_graph_panel);
      gtk_widget_set_sensitive (dialog->graph_show_button, TRUE);
      gtk_widget_set_sensitive (dialog->graph_hide_button, FALSE);
    }
/*
**  If the graph entry is dirty, then disable the clist so that the user
**  can't change graph entries without first applying the changes
*/

  if (dialog->graph_dirty)
    gtk_widget_set_sensitive (dialog->graph_clist, FALSE);
  else
    gtk_widget_set_sensitive (dialog->graph_clist, TRUE);

/*
**  If this is a new host that hasn't been added to the database yet,
**  then disable all of the controls except for 'ok' and 'apply'
*/

  if (dialog->selected_host->rowid == 0)
    {
      gtk_widget_set_sensitive (dialog->interface_add_button, FALSE);
      gtk_widget_set_sensitive (dialog->graph_show_button, FALSE);
      gtk_widget_set_sensitive (dialog->interface_clist, FALSE);
      gtk_widget_set_sensitive (dialog->graph_clist, FALSE);
    }

/*
**  Finally, only enable the "Apply" and "Ok" buttons if something has 
**  changed and the change needs to be applied.
*/

  gnome_property_dialog_set_state (GNOME_PROPERTY_DIALOG (dialog), 
				   dialog->host_dirty || 
				   dialog->interface_dirty || 
				   dialog->graph_dirty);
  D_FUNC_END;
}

/*****************************************************************************
**
**  Public function to create a new host dialog widget         
**
**  Parameters:
**
**  dbh -- Either the handle of an existing DB_host entry, or NULL if this
**         a call to create a new host.
**
**  x, y -- The cursor position at the time of the mouse select.
**
*****************************************************************************/

GtkWidget *
gxsnmp_host_dialog_new (DB_host * dbh, gdouble x, gdouble y)
{
  GXsnmp_host_dialog * dialog;
  GList		     * gl;
  GXsnmp_map	     * map;
  DB_map	     * dbm;

  D_FUNC_START;
  dialog = gtk_type_new (gxsnmp_host_dialog_get_type());

  dialog->host_dirty      = FALSE;
  dialog->interface_dirty = FALSE;
  dialog->graph_dirty     = FALSE;
  dialog->request         = NULL;

  dialog->snmp_rowid = 0;	/* No rowid selected yet */

  if (!dbh)
    {
      d_print (DEBUG_TRACE, "Create the host instance.\n");
      dbh = host_create();
      dialog->host_dirty = TRUE;
    }
  if (!dbh)
    {
      g_warning ("gxsnmp_host_dialog_new: Unable to get host instance. "
		 "bailing out.");
      return NULL;
    }
  dialog->selected_host	     = dbh; 	/* Select the desired host */	
  if (dbh->DB_interfaces)		/* Select the first interface */
    dialog->selected_interface = 
      (DB_interface *)dbh->DB_interfaces->data;

/*
**  Bring up the dialog with the current map selected, if possible 
*/

  map = gxsnmp_window_get_current_map (             /* Find the currently */
                GXSNMP_WINDOW (app_info->window));  /* selected map */
  if (map)
    {
      d_print (DEBUG_TRACE, "Valid map..");
      dbm = map->DB_map;                                /* and map structure */
      
      dialog->selected_graph     = NULL;    /* No map selected yet */
      gl = dbh->DB_graphs;
      while (gl)                               /* Look for DB_graph entry */
	{                                       /* for this host on this map */
	  DB_graph * dbg;
	  
	  dbg = (DB_graph *) gl->data;
	  if (dbg->map == dbm->rowid)
            {
              dialog->selected_graph = dbg;     /* Remember DB_graph pointer */
              break;
            }
	  gl = gl->next;
	}
    }
  dialog->x		     = x;	/* Store the mouse */
  dialog->y		     = y;	/* X and Y location */

  host_refresh_cb (host_sqldb, dbh, G_SQLDB_CB_UPDATE, dialog);
  set_dialog_state (dialog);
  D_FUNC_END;
  return GTK_WIDGET (dialog);
}

/* EOF */

