/*
  Confirm that no warnings/criticals get generated by including
  multiple add/removes in a match w/o waiting on the mainloop to iterate.

  Copyright 2013 Canonical Ltd.

  Original sample code by Drew Bliss <drewb@valvesoftware.com>
  Modified for automatic testing by Charles Kerr <charles.kerr@canonical.com>
 */

#include <stdlib.h> /* exit() */
#include <gtk/gtk.h>
#include <libdbusmenu-glib/server.h>
#include <libdbusmenu-gtk/menu.h>
#include <libdbusmenu-gtk/parser.h>

static GMainLoop * loop = NULL;
static GtkWidget * top_gtk = NULL;
static DbusmenuMenuitem * top_dbusmenu = NULL;
static DbusmenuServer * menuservice = NULL;

static void
rebuild_menu (void)
{
  GList * l;
  GList * children;
  int i;
  const int n = 10;
  static int count = 0;

  if (top_gtk == NULL)
    {
      top_gtk = gtk_menu_new ();
      gtk_widget_show (top_gtk);
      top_dbusmenu = dbusmenu_gtk_parse_menu_structure (top_gtk);
      menuservice = dbusmenu_server_new ("/org/ayatana/NotificationItem/test/Menu");
      dbusmenu_server_set_root (menuservice, top_dbusmenu);
    }

  // remove all the previous children
  children = gtk_container_get_children (GTK_CONTAINER(top_gtk));
  for (l=children; l!=NULL; l=l->next)
    gtk_widget_destroy (GTK_WIDGET (l->data));

  // add a handful of new children
  for (i=0; i<n; ++i)
    {
      char buf[80];
      GtkWidget * child;

      g_snprintf (buf, sizeof(buf), "Test item %d", ++count);
      child = gtk_menu_item_new_with_label (buf);
      gtk_menu_shell_append (GTK_MENU_SHELL(top_gtk), child);
      gtk_widget_show (child);
    }
}

/*
 * Periodically rebuild the menu.
 *
 * After we've looped a couple of times with only one rebuild,
 * attempt to reproduce the bug Drew reported by rebuilding multiple
 * times before returning control to the main loop.
 *
 * If we survive doing this a handful of times without encountering
 * a g_warning or g_critical, pass the test.
 */ 
static gint
on_timer (gpointer unused G_GNUC_UNUSED)
{
  static int iteration = 0;

  ++iteration;

  if (iteration > 5)
    {
      g_main_loop_quit (loop);
      return G_SOURCE_REMOVE;
    }

  if (iteration <= 2)
    {
      rebuild_menu ();
    }
  else
    {
      int i;

      for (i=0; i<iteration; ++i)
        rebuild_menu ();
    }

  return G_SOURCE_CONTINUE;
}

static void
warning_counter (const gchar    * log_domain,
                 GLogLevelFlags   log_level G_GNUC_UNUSED,
                 const gchar    * message,
                 gpointer         user_data G_GNUC_UNUSED)
{
  g_message ("Failing the test due to warning: %s %s", log_domain, message);
  exit (EXIT_FAILURE);
}

int
main (int argc, char ** argv)
{
  g_log_set_handler ("LIBDBUSMENU-GLIB", G_LOG_LEVEL_WARNING|G_LOG_LEVEL_CRITICAL, warning_counter, NULL);


  gtk_init (&argc, &argv);
  loop = g_main_loop_new (NULL, FALSE);
  g_timeout_add (200, on_timer, NULL);
  g_main_loop_run (loop);

  return 0;
}
