#include <glibmm.h>
#include <gtkmm/alignment.h>
#include <gtkmm/box.h>
#include <gtkmm/button.h>
#include <gtkmm/checkbutton.h>
#include <gtkmm/label.h>
#include <gtkmm/main.h>
#include <gtkmm/table.h>
#include <gtkmm/window.h>
#include <cstdio>

#define ROWS    5
#define COLUMNS 5
#define ITERATIONS 100

namespace
{

static double time_first_expose = 0.0;
static double time_last_expose  = 0.0;
static int expose_count = 0;

static Glib::Timer& timer()
{
  static Glib::Timer global_timer;
  return global_timer;
}

class TestWindow : public Gtk::Window
{
public:
  TestWindow() {}

#ifdef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
protected:
  virtual bool on_expose_event(GdkEventExpose* e)
  {
    ++expose_count;

    if (expose_count == 1)
    {
      time_first_expose = timer().elapsed();
    }
    else if (expose_count >= ITERATIONS)
    {
      time_last_expose = timer().elapsed();
      Gtk::Main::quit();
    }

    return Gtk::Window::on_expose_event(e);
  }
#endif
};

#ifndef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
static gboolean
window_expose_cb(GtkWindow* win, GdkEventExpose* e)
{
  ++expose_count;

  if (expose_count == 1)
  {
    time_first_expose = timer().elapsed();
  }
  else if (expose_count >= ITERATIONS)
  {
    time_last_expose = timer().elapsed();
    Gtk::Main::quit();
  }

  return FALSE;
}
#endif

static Gtk::Widget* create_table_item(unsigned int index)
{
  using namespace Gtk;

  gchar * label = g_strdup_printf ("%d", index);

  VBox * item = new VBox (false, 0);
  HBox * top = new HBox (false, 0);
  HBox * bottom = new HBox (false, 0);

  item->add (*manage (top));
  item->add (*manage (bottom));

  top->add (*manage (new Button (label)));
  top->add (*manage (new Alignment (0.5, 0.5, 0.0, 0.0)));
  bottom->add (*manage (new Label ("")));
  bottom->add (*manage (new CheckButton ("")));

  g_free (label);
  return item;
}

static gboolean
idle_callback(gpointer data)
{
  GtkWidget* widget = static_cast<GtkWidget*>(data);

  gdk_window_invalidate_rect(widget->window, NULL, TRUE);
  gdk_window_process_updates(widget->window, TRUE);

  return TRUE;
}

} // anonymous namespace

int main(int argc, char** argv)
{
  timer().start();

  Gtk::Main loop (argc, argv);

  const double time_initialization = timer().elapsed();
  timer().start();

  TestWindow w;
  Gtk::Table table (ROWS, COLUMNS, false);

  for (unsigned i = 0; i < ROWS; i++)
  {
    for (unsigned j = 0; j < COLUMNS; j++)
    {
      Gtk::Widget* item = create_table_item(i * COLUMNS + j);
      table.attach(*Gtk::manage(item), j, j + 1, i, i + 1);
    }
  }

  w.add(table);
  table.show_all();

#ifndef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
  g_signal_connect(w.Glib::Object::gobj(), "expose-event",
                   G_CALLBACK(window_expose_cb), NULL);
#endif

  g_idle_add(&idle_callback, w.gobj());

  const double time_construction = timer().elapsed();
  timer().start();

  w.show();
  Gtk::Main::run();

  printf("%.4f: initialization\n%.4f: construction\n"
         "%.4f: first expose\n%.4f: last expose\n",
         time_initialization, time_construction,
         time_first_expose, time_last_expose);

  return 0;
}
