/*
     This file is part of GNUnet.
     Copyright (C) 2010, 2011 GNUnet e.V.

     GNUnet 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 3, or (at your
     option) any later version.

     GNUnet 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 GNUnet; see the file COPYING.  If not, write to the
     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     Boston, MA 02110-1301, USA.
*/

/**
 * @file src/statistics/gnunet-statistics-gtk.c
 * @brief Main function of gnunet-statistics-gtk
 * @author Christian Grothoff
 */
#include "gnunet_gtk.h"

#include <gnunet/gnunet_util_lib.h>
#include <gnunet/gnunet_statistics_service.h>
#include <cairo.h>
#include "gtk_statistics.h"

#define MAX_HISTORY 1280


/**
 * Information about how to plot certain values.
 */
struct PlotInfo
{
  /**
   * Subsystem originating the value to plot.
   */
  const char *subsystem;

  /**
   * Name of the value to plot.
   */
  const char *name;

  /**
   * Label to use in legend.
   */
  const char *label;

  /**
   * Name of color to use when plotting.
   */
  const char *color_name;
};


/**
 * Selected statistics for the connection tab.
 */
static const struct PlotInfo connection_data[] =
  {{"fs", "# peers connected", "file-sharing connections", "blue"},
   {"core", "# peers connected", "encrypted connections (core)", "green"},
   {"core",
    "# neighbour entries allocated",
    "transport connections (core)",
    "yellow"},
   {"transport",
    "# peers connected",
    "total connections (transport)",
    "orange"},
   {"transport", "# TCP sessions active", "tcp connections", "red"},
   {"transport", "# UDP sessions active", "udp connections", "gray"},
   {"dht", "# peers connected", "DHT connections", "brown"},
   {"nse",
    "# nodes in the network (estimate)",
    "network size (estimate)",
    "purple"},
   {NULL, NULL, NULL}};


/**
 * Selected statistics for the traffic tab.
 */
static const struct PlotInfo traffic_data[] =
  {{"core", "# bytes encrypted", "bytes encrypted", "blue"},
   {"core", "# bytes decrypted", "bytes decrypted", "green"},
   {"transport", "# bytes received via TCP", "received via tcp", "yellow"},
   {"transport", "# bytes transmitted via TCP", "sent via tcp", "brown"},
   {NULL, NULL, NULL}};


/**
 * Selected statistics for the storage tab.
 */
static const struct PlotInfo storage_data[] =
  {{"datastore", "# quota", "datastore capacity", "blue"},
   {"datastore",
    "# utilization by current datastore",
    "datastore utilization",
    "yellow"},
   {"datastore", "# cache size", "reservation for datastore cache", "green"},
   {NULL, NULL, NULL}};


/**
 * Handle to our main loop.
 */
static struct GNUNET_GTK_MainLoop *ml;

/**
 * Handle to statistics subsystem.
 */
static struct GNUNET_STATISTICS_Handle *statistics;

/**
 * When did this process start?
 */
static struct GNUNET_TIME_Absolute start_time;


/**
 * Callback function to process statistic values.
 *
 * @param cls the 'struct ValueHistory' to update
 * @param subsystem name of subsystem that created the statistic
 * @param name the name of the datum
 * @param value the current value
 * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not
 * @return #GNUNET_OK to continue
 */
static int
process_value_update (void *cls,
                      const char *subsystem,
                      const char *name,
                      uint64_t value,
                      int is_persistent)
{
  GtkStatistics *stats = cls;
  char *id;

  GNUNET_asprintf (&id, "%s: %s", subsystem, name);
  gtk_statistics_update_value (stats,
                               id,
                               GNUNET_TIME_absolute_get_duration (start_time)
                                   .rel_value_us /
                                 1000000LL,
                               value);
  GNUNET_free (id);
  return GNUNET_OK;
}


/**
 * Get an object from the main window.
 *
 * @param name name of the object
 * @return NULL on error
 */
static GObject *
get_object (const char *name)
{
  return GNUNET_GTK_main_loop_get_object (ml, name);
}


/**
 * Actually draw a plot based on collected data.
 *
 * @param box_name name of the widget to update
 * @param info what to draw
 */
static void
create_plot (const char *box_name, const struct PlotInfo *info)
{
  GtkBox *box;
  GtkStatistics *ret;
  char *id;

  ret = GTK_STATISTICS (gtk_statistics_new ());
  box = GTK_BOX (get_object (box_name));

  for (unsigned int i = 0; NULL != info[i].subsystem; i++)
  {
    GNUNET_asprintf (&id, "%s: %s", info[i].subsystem, info[i].name);
    gtk_statistics_add_line (ret, id, info[i].label, info[i].color_name);
    GNUNET_free (id);
    GNUNET_STATISTICS_watch (statistics,
                             info[i].subsystem,
                             info[i].name,
                             &process_value_update,
                             ret);
  }
  gtk_widget_show (GTK_WIDGET (ret));
  gtk_box_pack_start (box, GTK_WIDGET (ret), TRUE, TRUE, 0);
}


/**
 * Task run on shutdown.
 *
 * @param cls unused
 */
static void
shutdown_task (void *cls)
{
  GNUNET_GTK_main_loop_quit (ml);
  ml = NULL;
  GNUNET_STATISTICS_destroy (statistics, GNUNET_NO);
  statistics = NULL;
}


/**
 * Callback invoked if the application is supposed to exit.
 */
void
GNUNET_STATISTICS_GTK_quit_cb (GObject *object, gpointer user_data)
{
  GNUNET_SCHEDULER_shutdown ();
}


/**
 * Actual main function run right after GNUnet's scheduler
 * is initialized.  Initializes up GTK and Glade.
 */
static void
run (void *cls)
{
  GtkWidget *main_window;

  ml = cls;

  if (GNUNET_OK != GNUNET_GTK_main_loop_build_window (ml, NULL))
  {
    return;
  }

  statistics =
    GNUNET_STATISTICS_create ("gnunet-statistics-gtk",
                              GNUNET_GTK_main_loop_get_configuration (ml));
  if (NULL == statistics)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _ ("Failed to initiate connection with statistics service\n"));
    return;
  }
  GNUNET_GTK_set_icon_search_path ();
  GNUNET_GTK_setup_nls ();

  /* setup main window */
  main_window = GTK_WIDGET (get_object ("GNUNET_STATISTICS_GTK_main_window"));
  gtk_window_maximize (GTK_WINDOW (main_window));
  create_plot ("GNUNET_STATISTICS_GTK_connectivity_box", connection_data);
  create_plot ("GNUNET_STATISTICS_GTK_traffic_box", traffic_data);
  create_plot ("GNUNET_STATISTICS_GTK_storage_box", storage_data);

  /* make GUI visible */
  gtk_widget_show (main_window);
  gtk_window_present (GTK_WINDOW (main_window));
  GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
}


int
main (int argc, char *const *argv)
{
  static struct GNUNET_GETOPT_CommandLineOption options[] = {
    GNUNET_GETOPT_OPTION_END};

  start_time = GNUNET_TIME_absolute_get ();
  if (GNUNET_OK !=
      GNUNET_GTK_main_loop_start ("gnunet-statistics-gtk",
                                  "GTK GUI for viewing GNUnet statistics",
                                  argc,
                                  argv,
                                  options,
                                  "gnunet_statistics_gtk_main_window.glade",
                                  &run))
    return 1;
  return 0;
}


/* end of gnunet-statistics-gtk.c */
