/* NessusClient
 * Copyright (C) 1998, 2005 Renaud Deraison
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * In addition, as a special exception, Renaud Deraison
 * gives permission to link the code of this program with any
 * version of the OpenSSL library which is distributed under a
 * license identical to that listed in the included COPYING.OpenSSL
 * file, and distribute linked combinations including the two.
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.  If you modify
 * this file, you may extend this exception to your version of the
 * file, but you are not obligated to do so.  If you do not wish to
 * do so, delete this exception statement from your version.
 */

#include <includes.h>

#ifdef USE_GTK
#include <gtk/gtk.h>

#include "../nessus_plugin.h"
#include "../xstuff.h"
#include "../plugin_infos.h"
#include "../families.h"
#include "../preferences.h"
#include "globals.h"
#include "../error_dialog.h"
#include "prefs_help.h"
#include "filter.h"
#include "prefs_plugins.h"
#include "prefs_plugins_tree.h"



static int
set_filter(w, ctrls)
  GtkWidget *w;
  struct arglist *ctrls;
{
  struct plugin_filter filter;
  char *old;
  struct nessus_plugin *plugins = Context->plugins;
  unsigned int filter_count = 0;
  GtkWidget *label = arg_get_value(ctrls, "FILTER_LABEL");

  ask_filter(&filter);
  if(!filter.pattern)
    return 0;


  if((old = arg_get_value(ctrls, "FILTER")))
  {
    arg_set_value(ctrls, "FILTER", sizeof(filter), &filter);
  }
  else
    arg_add_value(ctrls, "FILTER", ARG_STRUCT, sizeof(filter), &filter);


  Filter = filter;
  memcpy(&Filter, &filter, sizeof(filter));

    while(plugins != NULL )
    {
      if(filter_plugin(&filter, plugins))
	 plugins->enabled = 0;
      else
	 filter_count++;
	 
      plugins = plugins->next;
    }

  if (strlen(filter.pattern) == 0)
  {
    gtk_label_set_text(GTK_LABEL(label), _("No filter active"));
  }
  else
  {
    char *text = g_strdup_printf(_("Filter matches %u plugins"), filter_count);
    gtk_label_set_text(GTK_LABEL(label), text);
    g_free(text);
  }

  prefs_plugin_tree_context_changed(Context, ctrls, TRUE);

  return 0;
}

static int
disable_all(w, data)
  GtkWidget *w;
  gpointer data;
{
  prefs_plugin_tree_enable_all(GTK_TREE_VIEW(data), FALSE);

  return 0;
}

static int
enable_all(w, data)
  GtkWidget *w;
  gpointer data;
{
  prefs_plugin_tree_enable_all(GTK_TREE_VIEW(data), TRUE);

  return 0;
}

static void
expand_all(w, data)
  GtkWidget *w;
  gpointer data;
{
  gtk_tree_view_expand_all(GTK_TREE_VIEW(data));

  /* normally the tree should automatically be redrawn.  This doesn't
   * happen in gtk+ 2.0.2, though, so we trigger a redraw explicitly
   */
  gtk_widget_queue_draw(GTK_WIDGET(data));
}

static void
collapse_all(w, data)
  GtkWidget *w;
  gpointer data;
{
  gtk_tree_view_collapse_all(GTK_TREE_VIEW(data));
}

/* Update the statistics label.
 *
 * The user_data argument must be a pointer to the label widget.  This
 * function expects the label widget to have tree widget for which
 * statistics is to be shown to be associated to the label widget under
 * the name "tree" so that it can be retrieved with g_object_get_data.
 *
 * This function is set up as an idle handler by
 * update_plugin_statistics_label_when_idle.  Therefore it sets the
 * label's "idle-update-id" user data value to 0 to indicate that it has
 * run and should be added again.
 */
static gboolean
update_plugin_statistics_label(user_data)
  gpointer user_data;
{
  GtkWidget *label = GTK_WIDGET(user_data);
  GtkTreeView *tree = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(label),
	  "tree"));
  int total, enabled;
  char*text;

  prefs_plugin_tree_statistics(tree, &total, &enabled);

  text = g_strdup_printf(_("%d plugins; %d enabled"), total, enabled);

  gtk_label_set_text(GTK_LABEL(label), text);

  g_free(text);
  
  g_object_set_data(G_OBJECT(label), "idle-update-id", GUINT_TO_POINTER(0));

  return FALSE;
}

/* Schedule the idle update of the statistics label
 *
 * This function is called by the handlers for some of the signals sent
 * by the model behind the plugin tree that indicate that the model has
 * changed and that the statistics may have to be updated.
 *
 * Some user actions -- e.g. enable all -- can lead to many
 * signals being emitted at once, so to avoid unnecessary recomputations
 * of the statistics this function sets up
 * update_plugin_statistics_label to be called when the client becomes
 * idle.  To manage this, the function adds the gtk idle id for the
 * function as the userdata "idle-update-id" to the label widget.
 * update_plugin_statistics_label is only added as idle handler if that
 * attribute is 0 so that it will be set at most once before the client
 * become idle.
 */
static void
update_plugin_statistics_label_when_idle(label)
  GtkWidget *label;
{
  guint idle_id;

  idle_id = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(label),
	  "idle-update-id"));
  if (!idle_id)
  {
    idle_id = g_idle_add(update_plugin_statistics_label, label);

    g_object_set_data(G_OBJECT(label), "idle-update-id",
	GUINT_TO_POINTER(idle_id));
  }
}

static void
update_statistics(tree_view, user_data)
  GtkWidget *tree_view;
  gpointer user_data;
{
  update_plugin_statistics_label_when_idle(user_data);
}
  
/*
 * plugin_list_setup
 *
 * Draws the main window showing informations
 * about the plugins of the server
 */
struct arglist *
prefs_dialog_plugins(context)
  struct context *context;
{
  struct arglist *ctrls = emalloc(sizeof(struct arglist));
  GtkWidget *frame;
  GtkWidget *w_box;
  GtkWidget *hbox;
  GtkWidget *button;
  GtkWidget *tree_window;
  GtkWidget *tree;
  GtkWidget *label;
  GtkTreeModel *model;

  frame = gtk_frame_new(_("Plugin selection"));
  gtk_container_border_width(GTK_CONTAINER(frame), 10);
  arg_add_value(ctrls, "FRAME", ARG_PTR, -1, frame);

  w_box = gtk_vbox_new(TRUE, 5);
  gtk_container_border_width(GTK_CONTAINER(w_box), 5);
  gtk_container_add(GTK_CONTAINER(frame), w_box);
  gtk_box_set_homogeneous(GTK_BOX(w_box), FALSE);

  /* tree view of the plugins */
  tree_window = gtk_scrolled_window_new(NULL, NULL);
  gtk_container_border_width(GTK_CONTAINER(tree_window), 10);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(tree_window),
      GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tree_window),
      GTK_SHADOW_IN);
  gtk_box_pack_start(GTK_BOX(w_box), tree_window, TRUE, TRUE, 0);
  tree = prefs_create_plugins_tree(context, ctrls);
  gtk_container_add(GTK_CONTAINER(tree_window), tree);
  gtk_widget_show(tree);
  gtk_widget_show(tree_window);

  /* Label showing some statistics */
  label = gtk_label_new(_("No plugins"));
  gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
  gtk_box_pack_start(GTK_BOX(w_box), label, FALSE, FALSE, 0);
  gtk_widget_show(label);
  model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree));
  /* connect to some tree model signals so that we update the label when
   * the model changes.  The way the model is updated currently means
   * that only row-changed and row-deleted are interesting. */
  g_signal_connect(G_OBJECT(tree), "statistics-changed", 
      update_statistics, label);
  g_object_set_data(G_OBJECT(label), "tree", tree);
  g_object_set_data(G_OBJECT(label), "idle-update-id", GUINT_TO_POINTER(0));

  /* Label showing the filter status */
  label = gtk_label_new(_("No filter active"));
  gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
  gtk_box_pack_start(GTK_BOX(w_box), label, FALSE, FALSE, 0);
  gtk_widget_show(label);
  arg_add_value(ctrls, "FILTER_LABEL", ARG_PTR, -1, label);

  /* enable all / disable all buttons */
  hbox = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(w_box), hbox, FALSE, FALSE, 0);
  gtk_widget_show(hbox);

  button = gtk_button_new_with_label(_("Enable all"));
  gtk_signal_connect(GTK_OBJECT(button),
      "clicked", GTK_SIGNAL_FUNC(enable_all), tree);
  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  gtk_widget_show(button);


  button = gtk_button_new_with_label(_("Disable all"));
  gtk_signal_connect(GTK_OBJECT(button),
      "clicked", GTK_SIGNAL_FUNC(disable_all), tree);
  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  gtk_widget_show(button);

  /* expand all / collapse all buttons */
  hbox = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(w_box), hbox, FALSE, FALSE, 0);
  gtk_widget_show(hbox);

  button = gtk_button_new_with_label(_("Expand all"));
  gtk_signal_connect(GTK_OBJECT(button),
      "clicked", GTK_SIGNAL_FUNC(expand_all), tree);
  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  gtk_widget_show(button);

  button = gtk_button_new_with_label(_("Collapse all"));
  gtk_signal_connect(GTK_OBJECT(button),
      "clicked", GTK_SIGNAL_FUNC(collapse_all), tree);
  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  gtk_widget_show(button);


  /* dependency buttons */
  hbox = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(w_box), hbox, FALSE, FALSE, 5);
  gtk_widget_show(hbox);

  button =
      gtk_check_button_new_with_label(_("Enable dependencies at runtime"));
  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  gtk_widget_show(button);
  arg_add_value(ctrls, "ENABLE_DEPS_AT_RUNTIME", ARG_PTR, -1, button);

  button =
      gtk_check_button_new_with_label(_("Silent dependencies"));
  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  gtk_widget_show(button);
  arg_add_value(ctrls, "SILENT_DEPS", ARG_PTR, -1, button);

  button = gtk_button_new_with_mnemonic(_("Fi_lter..."));
  gtk_signal_connect(GTK_OBJECT(button),
      "clicked", GTK_SIGNAL_FUNC(set_filter), ctrls);
  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  gtk_widget_show(button);

  fill_plugins_family(context, ctrls);

  gtk_widget_show(w_box);
  gtk_widget_show(frame);

  return (ctrls);
}


void
fill_plugins_family(context, ctrls)
  struct context *context;
  struct arglist *ctrls;
{
  /* init tree data */
  prefs_plugin_tree_context_changed(context, ctrls, FALSE);
}




#endif
