#include <string.h>

#include <gtk/gtk.h>
#include <gdk/gdk.h>

#include "torturer.h"
#include "paint.h"
#include "gtkwidgetprofiler.h"

typedef struct {
  GtkWidgetProfilerReport	type;
  double			elapsed;
} StoredReport;

GType     *types;
gchar    **function_name;
GtkWidget *crash_test_label;
int        torture_level = 7;
int        current_widget_type;
gdouble    total_create = 0.;
gdouble    total_map = 0.;
gdouble    total_expose = 0.;
gdouble    total_destroy = 0.;
gboolean   xml_output = FALSE;
gboolean   running_full_torture = FALSE;
int        n_iter;

static void 
initialize_types (void) {

  int i = 0;

  types = g_new (GType, 50);

  types[i++] = GTK_TYPE_ENTRY;
  types[i++] = GTK_TYPE_BUTTON;
  types[i++] = GTK_TYPE_CHECK_BUTTON;
  types[i++] = GTK_TYPE_FRAME;
  types[i++] = GTK_TYPE_LABEL;
  types[i++] = GTK_TYPE_NOTEBOOK;
  types[i++] = GTK_TYPE_RADIO_BUTTON;
  types[i++] = GTK_TYPE_PROGRESS_BAR;
  types[i++] = GTK_TYPE_HSCALE;
  types[i++] = GTK_TYPE_VSCALE;
  types[i++] = GTK_TYPE_SCROLLED_WINDOW;
  types[i++] = 0; 
}

static void
initialize_functions (void) {
	
  function_name = g_new (gchar *, N_GTK_PAINT_FUNCTIONS);

  function_name[GTK_PAINT_ARROWS]       = g_strdup_printf ("Arrows");
  function_name[GTK_PAINT_BOXES]        = g_strdup_printf ("Boxes");
  function_name[GTK_PAINT_BOX_GAPS]     = g_strdup_printf ("Box gaps");
  function_name[GTK_PAINT_CHECKS]       = g_strdup_printf ("Checks");
  function_name[GTK_PAINT_DIAMONDS]     = g_strdup_printf ("Diamonds");
  function_name[GTK_PAINT_EXTENSIONS]   = g_strdup_printf ("Extensions");
  function_name[GTK_PAINT_FLAT_BOXES]   = g_strdup_printf ("Flat boxes");
  function_name[GTK_PAINT_FOCUSES]      = g_strdup_printf ("Focuses");
  function_name[GTK_PAINT_HANDLES]      = g_strdup_printf ("Handles");
  function_name[GTK_PAINT_HLINES]       = g_strdup_printf ("HLines");

  function_name[GTK_PAINT_OPTIONS]      = g_strdup_printf ("Options");
  function_name[GTK_PAINT_POLYGONS]     = g_strdup_printf ("Polygons");
  function_name[GTK_PAINT_SHADOWS]      = g_strdup_printf ("Shadows");
  function_name[GTK_PAINT_SHADOW_GAPS]  = g_strdup_printf ("Shadow gaps");
  function_name[GTK_PAINT_SLIDERS]      = g_strdup_printf ("Sliders");
  function_name[GTK_PAINT_TABS]         = g_strdup_printf ("Tabs");
  function_name[GTK_PAINT_VLINES]       = g_strdup_printf ("VLines");
  function_name[GTK_PAINT_EXPANDERS]    = g_strdup_printf ("Expanders");
  function_name[GTK_PAINT_LAYOUTS]      = g_strdup_printf ("Layouts");
  function_name[GTK_PAINT_RESIZE_GRIPS] = g_strdup_printf ("Resize grips");
}

static GtkWidget *
create_widget (void) {
  const char *torture_string = "Aaaah! Please stop this!";
  GType       type;
  GtkWidget  *w;

  type = types[current_widget_type];

  if (type == GTK_TYPE_ENTRY) {
    w = gtk_entry_new ();
    gtk_entry_set_text (GTK_ENTRY (w), torture_string);
  }
  else if (type == GTK_TYPE_BUTTON)
    w = gtk_button_new_with_label (torture_string);
  else if (type == GTK_TYPE_CHECK_BUTTON)
    w = gtk_check_button_new_with_label (torture_string);
  else if (type == GTK_TYPE_FRAME)
    w = gtk_frame_new (torture_string);
  else if (type == GTK_TYPE_LABEL)
    w = gtk_label_new (torture_string);
  else if (type == GTK_TYPE_NOTEBOOK) {
    w = gtk_notebook_new ();
    gtk_notebook_append_page (GTK_NOTEBOOK (w), gtk_label_new (torture_string), NULL);
    gtk_notebook_append_page (GTK_NOTEBOOK (w), gtk_label_new (torture_string), NULL);
    gtk_notebook_append_page (GTK_NOTEBOOK (w), gtk_label_new (torture_string), NULL);
  }
  else if (type == GTK_TYPE_RADIO_BUTTON)
    w = gtk_radio_button_new_with_label (NULL, torture_string);
  else if (type == GTK_TYPE_PROGRESS_BAR) {
    w = gtk_progress_bar_new ();
    gtk_progress_bar_set_text (GTK_PROGRESS_BAR (w), torture_string);    
  }
  else if (type == GTK_TYPE_HSCALE)
    w = gtk_hscale_new (NULL);
  else if (type == GTK_TYPE_VSCALE)
    w = gtk_vscale_new (NULL);
  else if (type == GTK_TYPE_SCROLLED_WINDOW)
    w = gtk_scrolled_window_new (NULL, NULL);

  return w;
}

static GtkWidget *
profiler_create_widget_cb (GtkWidgetProfiler *profiler, gpointer data)
{
  return create_widget ();
}

static void
profiler_report_cb (GtkWidgetProfiler *profiler, GtkWidgetProfilerReport type, GtkWidget *widget, gdouble elapsed, gpointer data)
{
  StoredReport report = { type, elapsed };

  g_array_append_val (data, report);
}

gint
sort_report_entry (gconstpointer _a, gconstpointer _b)
{
  const StoredReport *a = _a;
  const StoredReport *b = _b;

  if (a->type < b->type)
    return -1;
  if (a->type > b->type)
    return 1;
  if (a->elapsed < b->elapsed)
    return -1;
  if (a->elapsed > b->elapsed)
    return 1;
  return 0;
}

/* reports must be sorted when calling this function */
static gdouble
calculate_average (GArray *reports, GtkWidgetProfilerReport type)
{
  guint i, first, last, discard;
  double sum, average;
/* percentage of results discarded at top and bottom each */
#define DISCARD_PERCENTAGE 20

  first = last = reports->len;
  for (i = 0; i < reports->len; i++) {
    if (g_array_index (reports, StoredReport, i).type == type) {
      first = i;
      break;
    }
  }
  if (first >= reports->len) /* none found */
    return 0.0;
  for (i = first; i < reports->len; i++) {
    if (g_array_index (reports, StoredReport, i).type != type) {
      last = i;
      break;
    }
  }
  sum = 0;
  discard = (last - first) * DISCARD_PERCENTAGE / 100;
  g_assert (discard * 2 < last - first);
  for (i = first + discard; i < last - discard; i++) {
    sum += g_array_index (reports, StoredReport, i).elapsed;
  }
  average = sum / (last - first - 2 * discard);
#if 0
  g_print ("%g (from %g to %g - %d%%) (full times from %g to %g - %d excluded) for %d reports\n", 
      average,
      g_array_index (reports, StoredReport, first + discard).elapsed,
      g_array_index (reports, StoredReport, last - 1 - discard).elapsed,
      (int) (g_array_index (reports, StoredReport, last - 1 - discard).elapsed * 100 / 
	     g_array_index (reports, StoredReport, first + discard).elapsed),
      g_array_index (reports, StoredReport, first).elapsed,
      g_array_index (reports, StoredReport, last - 1).elapsed,
      discard,
      last - first);
#endif
  return average;
}

static void
torture_widget (GtkWidget *not_useful, gpointer data) {
  int i;
  GtkWidgetProfiler *profiler;
  gdouble total_expose_in_boot, total_expose_in_expose, total_expose_in_resize;
  GArray *reports;
  
  i = GPOINTER_TO_INT (data);
  current_widget_type = i;
  n_iter = 12 * torture_level;

  /* Profiler init */
  profiler = gtk_widget_profiler_new ();
  reports = g_array_new (FALSE, FALSE, sizeof (StoredReport));
  g_signal_connect (profiler, "create-widget",
		    G_CALLBACK (profiler_create_widget_cb), NULL);
  g_signal_connect (profiler, "report",
		    G_CALLBACK (profiler_report_cb), reports);

  gtk_widget_profiler_set_num_iterations (profiler, n_iter);

  if (!running_full_torture) {
    printf ("\n# 3 x %i iterations -- Single widget torture\n# Widget\tBoot-create\tBoot-map\tBoot-expose\tBoot-destroy\tExpose\tResize\n", n_iter);
  }

  /* Boot */
  gtk_widget_profiler_profile_boot (profiler);
  g_array_sort (reports, sort_report_entry);
  total_expose_in_boot = calculate_average (reports, GTK_WIDGET_PROFILER_REPORT_EXPOSE);
  total_create = calculate_average (reports, GTK_WIDGET_PROFILER_REPORT_CREATE);
  total_map = calculate_average (reports, GTK_WIDGET_PROFILER_REPORT_MAP);
  total_destroy = calculate_average (reports, GTK_WIDGET_PROFILER_REPORT_DESTROY);

  /* Expose */
  g_array_set_size (reports, 0);
  gtk_widget_profiler_profile_expose (profiler);
  g_array_sort (reports, sort_report_entry);
  total_expose_in_expose = calculate_average (reports, GTK_WIDGET_PROFILER_REPORT_EXPOSE);

  /* Expose and resize */
  g_array_set_size (reports, 0);
  gtk_widget_profiler_profile_expose_with_resize (profiler, 0.1, 0.5);
  g_array_sort (reports, sort_report_entry);
  total_expose_in_resize = calculate_average (reports, GTK_WIDGET_PROFILER_REPORT_EXPOSE);

  g_array_free (reports, TRUE);

  if (!xml_output) {
    printf ("%s\t%g\t%g\t%g\t%g\t%g\t%g\n",
	    g_type_name (types[current_widget_type]),
	    total_create, total_map, total_expose_in_boot, total_destroy,
	    total_expose_in_expose,
	    total_expose_in_resize);
  } else {
    printf ("<widget name=\"%s\">\n", g_type_name (types[current_widget_type]));
    printf ("  <iters>%i</iters>\n", n_iter);
    printf ("  <timing name=\"boot\" subname=\"Create\">%g</timing>\n", total_create);
    printf ("  <timing name=\"boot\" subname=\"Map\">%g</timing>\n", total_map);
    printf ("  <timing name=\"boot\" subname=\"Expose\">%g</timing>\n", total_expose_in_boot);
    printf ("  <timing name=\"boot\" subname=\"Destroy\">%g</timing>\n", total_destroy);
    printf ("  <timing name=\"expose\" subname=\"Expose\">%g</timing>\n", total_expose_in_expose);
    printf ("  <timing name=\"expose/resize\" subname=\"Expose\">%g</timing>\n", total_expose_in_resize);
    printf ("</widget>\n");
  }
  
  g_object_unref (profiler);
}

static void
full_torture (void) {
  int i;

  running_full_torture = TRUE;
  
  if (xml_output) {
      printf ("<?xml version=\"1.0\"?>\n");
      printf ("<gtk-torture>\n");
  } else {
    printf ("\n# 3 x %i iterations -- Full torture\n# Widget\tBoot-create\tBoot-map\tBoot-expose\tBoot-destroy\tExpose\tResize\n", n_iter);
  }
  for (i = 0; types[i] != 0; i++) {
    torture_widget (NULL, GINT_TO_POINTER (i));
  }
  if (xml_output)
      printf ("</gtk-torture>\n");

  running_full_torture = FALSE;
}

static void
update_pain (GtkWidget *vscale) {
  torture_level = gtk_range_get_value (GTK_RANGE (vscale));
  if (torture_level < 1)
    torture_level = 1;
  if (torture_level > 100)
    torture_level = 100;
}

static void
make_widget_list (GtkWidget *vbox) {

  int i;

  GtkWidget *button;

  for (i = 0; types[i] != 0; i++) {
    button = gtk_button_new_with_label (g_type_name (types[i]));
    gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
    g_signal_connect (button, "clicked", G_CALLBACK (torture_widget), GINT_TO_POINTER (i));
  }
}

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

  g_free (types);
  gtk_main_quit ();
}

static void
crash_test_check_all (GtkWidget *useless, gpointer data) {
  int i;
  GtkWidget **function_checkboxes;
  
  function_checkboxes = (GtkWidget **) data;
  
  for (i = 0; i < N_GTK_PAINT_FUNCTIONS; i++) {
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (function_checkboxes[i]), TRUE);
  }
}

static void
crash_test_uncheck_all (GtkWidget *useless, gpointer data) {
  int i;
  GtkWidget **function_checkboxes;
  
  function_checkboxes = (GtkWidget **) data;
  
  for (i = 0; i < N_GTK_PAINT_FUNCTIONS; i++) {
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (function_checkboxes[i]), FALSE);
  }
}

static void
run_crash_test (GtkButton *useless, gpointer data) {
  int i;
  GtkWidget **function_checkboxes;
  guint32 tests = 0;
  
  function_checkboxes = (GtkWidget **) data;
  for (i = 0; i < N_GTK_PAINT_FUNCTIONS; i++) {
    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (function_checkboxes[i]))) {
	    ACTIVE(tests,i);
    }
  }

  gtk_paint_functions_test (tests);
}

static void
run_all_crash_test ()
{
  int i;
  guint32 tests = 0;

  for (i = 0; i < N_GTK_PAINT_FUNCTIONS; i++)
    ACTIVE(tests,i);

  gtk_paint_functions_test (tests);
  while (gtk_events_pending())
	  gtk_main_iteration();
}

void
create_main_window (void) {
  
  GtkWidget *window, *main_vbox, *notebook, *close_button, // General widgets
    *torture_tab_label, *main_torture_vbox, *widget_vbox, *middle_hbox, *label, *scrolled_window, // Torture widgets 
    *full_button, *torture_vbox, *torture_level_vscale, *torture_level_label,
    *crash_test_main_vbox, *crash_test_hbox, *crash_test_tab_label, *crash_test_button, **function_checkboxes,
    *crash_test_checkboxes_vbox1, *crash_test_checkboxes_vbox2, *crash_test_check_all_button,
    *crash_test_uncheck_all_button, *crash_test_check_uncheck_hbox;  // Crash test widgets
  int i;

  /* Usual GTK starting blocks */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
  g_signal_connect (window, "delete-event", G_CALLBACK (gtk_false), NULL);
  g_signal_connect (window, "destroy", G_CALLBACK (end_program), NULL);

  /* The main (global) vbox */
  main_vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (window), main_vbox);

  /* The main torture & crash test vboxes and tab labels*/
  main_torture_vbox = gtk_vbox_new (FALSE, 0);
  crash_test_main_vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_set_border_width (GTK_CONTAINER (crash_test_main_vbox), 10);
  torture_tab_label = gtk_label_new ("Widget Torture");
  crash_test_tab_label = gtk_label_new ("Crash Test");

  /* Notebook for Widget torture / Crash test */
  notebook = gtk_notebook_new ();
  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), main_torture_vbox, torture_tab_label);
  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), crash_test_main_vbox, crash_test_tab_label);
  gtk_box_pack_start (GTK_BOX (main_vbox), notebook, FALSE, FALSE, 10);

  /*                     */
  /* Widget Torture part */
  /*                     */

  /* Top label */
  label = gtk_label_new ("Select a widget to torture or press the \"Start full torture\" button:");
  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
  gtk_box_pack_start (GTK_BOX (main_torture_vbox), label, FALSE, FALSE, 10);

  /* The middle hbox */
  middle_hbox = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (main_torture_vbox), middle_hbox, TRUE, TRUE, 5);

  /* The vbox with the widget list, inside a scrolled window */
  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_placement (GTK_SCROLLED_WINDOW (scrolled_window),
				     GTK_CORNER_TOP_RIGHT);
  gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 10);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_widget_set_size_request (scrolled_window, 200, 500);
  widget_vbox = gtk_vbox_new (FALSE, 2);
  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), widget_vbox);
  gtk_box_pack_start (GTK_BOX (middle_hbox), scrolled_window, TRUE, TRUE, 2);

  /* The widget list */
  make_widget_list (widget_vbox);

  /* The torture level */
  torture_vbox = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (middle_hbox), torture_vbox, TRUE, TRUE, 7);

  torture_level_label = gtk_label_new ("Torture level");
  gtk_box_pack_start (GTK_BOX (torture_vbox), torture_level_label, FALSE, FALSE, 0);

  torture_level_vscale = gtk_vscale_new_with_range (1, 100, 1);
  gtk_range_set_inverted (GTK_RANGE (torture_level_vscale), TRUE);
  gtk_range_set_value (GTK_RANGE (torture_level_vscale), 7);
  g_signal_connect (torture_level_vscale, "value-changed", G_CALLBACK (update_pain), NULL);
  gtk_box_pack_start (GTK_BOX (torture_vbox), torture_level_vscale, TRUE, TRUE, 7);

  /* The "Full torture" button */
  full_button = gtk_button_new_with_mnemonic ("_Start full torture");
  g_signal_connect (full_button, "clicked", G_CALLBACK (full_torture), NULL);
  gtk_box_pack_start (GTK_BOX (main_torture_vbox), full_button, FALSE, FALSE, 3);
  gtk_widget_grab_focus (full_button);


  /*                 */
  /* Crash test part */
  /*                 */

  /* Crash test hbox */
  crash_test_hbox = gtk_hbox_new (TRUE, 0);
  gtk_box_pack_start (GTK_BOX (crash_test_main_vbox), crash_test_hbox, FALSE, FALSE, 10);

  /* The checkboxes vbox 1 & 2 */
  crash_test_checkboxes_vbox1 = gtk_vbox_new (FALSE, 0);
  crash_test_checkboxes_vbox2 = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (crash_test_hbox), crash_test_checkboxes_vbox1, TRUE, TRUE, 10);
  gtk_box_pack_start (GTK_BOX (crash_test_hbox), crash_test_checkboxes_vbox2, TRUE, TRUE, 10);

  /* The checkboxes */
  function_checkboxes = g_new (GtkWidget *, N_GTK_PAINT_FUNCTIONS);
  i = 0;
  for (i = 0; i < N_GTK_PAINT_FUNCTIONS; i++) {
    function_checkboxes[i] = gtk_check_button_new_with_label (function_name[i]);
    if (i < 10)
      gtk_box_pack_start (GTK_BOX (crash_test_checkboxes_vbox1), function_checkboxes[i], FALSE, FALSE, 0);
    else
      gtk_box_pack_start (GTK_BOX (crash_test_checkboxes_vbox2), function_checkboxes[i], FALSE, FALSE, 0);
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (function_checkboxes[i]), TRUE);
  }

  /* The "check all" and "uncheck all" buttons */
  crash_test_check_uncheck_hbox = gtk_hbox_new (TRUE, 0);
  gtk_box_pack_start (GTK_BOX (crash_test_main_vbox), crash_test_check_uncheck_hbox, FALSE, FALSE, 0);

  crash_test_uncheck_all_button = gtk_button_new_with_mnemonic ("_Uncheck all");
  g_signal_connect (crash_test_uncheck_all_button, "clicked", G_CALLBACK (crash_test_uncheck_all), function_checkboxes);
  gtk_box_pack_start (GTK_BOX (crash_test_check_uncheck_hbox), crash_test_uncheck_all_button, FALSE, FALSE, 0);

  crash_test_check_all_button = gtk_button_new_with_mnemonic ("_Check all");
  g_signal_connect (crash_test_check_all_button, "clicked", G_CALLBACK (crash_test_check_all), function_checkboxes);
  gtk_box_pack_start (GTK_BOX (crash_test_check_uncheck_hbox), crash_test_check_all_button, FALSE, FALSE, 0);

  /* The "Crash test" button */
  crash_test_button = gtk_button_new_with_mnemonic ("S_tart crash test");
  g_signal_connect (crash_test_button, "clicked", G_CALLBACK (run_crash_test), function_checkboxes);
  gtk_box_pack_start (GTK_BOX (crash_test_main_vbox), crash_test_button, FALSE, FALSE, 10);

  /* Label */
  crash_test_label = gtk_label_new ("");
  gtk_box_pack_start (GTK_BOX (crash_test_main_vbox), crash_test_label, FALSE, FALSE, 15);

  /* The close button */
  close_button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
  g_signal_connect (close_button, "clicked", G_CALLBACK (end_program), NULL);
  gtk_box_pack_start (GTK_BOX (main_vbox), close_button, FALSE, FALSE, 0);

  gtk_widget_show_all (window);
  gtk_main ();

  for (i = 0; i < N_GTK_PAINT_FUNCTIONS; i++) {
    g_free (function_name[i]);
  }
  g_free (function_name);

}

int
main (int argc, char **argv) {
  gboolean torture_full = FALSE;
  gboolean list_widgets = FALSE;
  gchar *widgets_str = NULL;
  gboolean crash_full = FALSE;
  gchar *crash_str = NULL;
  gboolean list_crash = FALSE;
  GError *error = NULL;


  GOptionEntry options[] = {
    { "torture-full", 't', 0, G_OPTION_ARG_NONE, &torture_full, "Run full torture", NULL },
    { "torture-widgets", 'w', 0, G_OPTION_ARG_STRING, &widgets_str, "Run torture over specified widgets", "GType coma separated list of widgets to torture" },
    { "torture-level", 'x', 0, G_OPTION_ARG_INT, &torture_level, "Torture level (default is 7)", "[1-100]" },
    { "list-torture-widgets", 'l', 0, G_OPTION_ARG_NONE, &list_widgets, "List supported widgets", NULL },
    { "crash-full", 'c', 0, G_OPTION_ARG_NONE, &crash_full, "Run full crashes", NULL },
    { "crash-functions", 'f', 0, G_OPTION_ARG_STRING, &crash_str, "Run crashes over specified functions", "coma separated list of functions to crash" },
    { "list-crash-functions", 'z', 0, G_OPTION_ARG_NONE, &list_crash, "List supported crash functions", NULL },
    { "xml-output", 'X', 0, G_OPTION_ARG_NONE, &xml_output, "XML Output", NULL },
    { NULL }
  };
  GOptionContext *ctx;

  g_set_application_name ("GTK+ Theme Torture Program");
  ctx = g_option_context_new("GTK+ Theme Torture Program");
  g_option_context_add_main_entries(ctx, options, "options");
  g_option_context_parse(ctx, &argc, &argv, &error);
  g_option_context_free(ctx);

  if (error) {
    g_print ("Error parsing command line arguments: %s\n", error->message);
    g_error_free (error);
    return 1;
  }


  gtk_init (&argc, &argv);

  initialize_types ();

  if (torture_level < 1 || torture_level > 100) {
    g_print ("Torture level should be between 1 and 100\n");
    return 1;
  }

  if (list_widgets) {
    int i;
    g_print ("List of supported widgets:\n");
    for (i = 0; types[i] != 0; i++) {
      g_print ("%s\n", g_type_name (types[i]));
    }
    return 0;
  }


  if (torture_full) {
    full_torture ();
    return 0;
  }
  if (widgets_str) {
    int i,j;
    gchar **widgets = g_strsplit (widgets_str, ",", 0);
    if (xml_output) {
	printf ("<?xml version=\"1.0\"?>\n");
	printf ("<gtk-torture>\n");
    } else {
      printf ("#Widget\tBoot-create\tBoot-map\tBoot-expose\tBoot-destroy\tExpose\tResize\n");
    }
    for (i = 0; widgets[i];  i++) {
      gboolean run = FALSE;
      for (j = 0; types[j] != 0; j++) {
        if (strcmp (widgets[i], g_type_name (types[j])) == 0) {
          torture_widget (NULL, GINT_TO_POINTER (j));
          run = TRUE;
          break;
        }
      }
      if (!run) {
         g_print ("Widget %s not supported\n", widgets[i]);
         g_strfreev(widgets);
         return 1;
      }
    }
    g_strfreev(widgets);
    if (xml_output)
	printf ("</gtk-torture>\n");
    return 0;
  }

  initialize_functions ();

  if (crash_full) {
    run_all_crash_test ();
    return 0;
  }

  if (crash_str) {
    int i,j;
    guint32 tests = 0;
    gchar **functions = g_strsplit (crash_str, ",", 0);
    for (i = 0; functions[i];  i++) {
      gboolean run = FALSE;
      for (j = 0; function_name[j] != 0; j++) {
        if (strcmp (functions[i], function_name[j]) == 0) {
          run = TRUE;
          ACTIVE(tests,j);
          break;
        }
      }
      if (!run) {
        g_print ("Function %s not supported\n", functions[i]);
        g_strfreev(functions);
        return 1;
      }
    }
    gtk_paint_functions_test (tests);

    while (gtk_events_pending())
      gtk_main_iteration();
    
    g_strfreev(functions);
    return 0;
  }

  if (list_crash) {
    int i;
    g_print ("List of supported funtions:\n");
    for (i = 0; function_name[i] != 0; i++) {
      g_print ("%s\n", function_name[i]);
    }
    return 0;
  }

  create_main_window ();

  return 0;
}
  

