/* Benchmark/Fishbowl
 *
 * This demo models the fishbowl demos seen on the web in a GTK way.
 * It's also a neat little tool to see how fast your computer (or
 * your GTK version) is.
 */

#include <gtk/gtk.h>

#include "gtkfishbowl.h"
#include "gtkgears.h"
#include "gskshaderpaintable.h"

const char *const css =
".blurred-button {"
"  box-shadow: 0px 0px 5px 10px rgba(0, 0, 0, 0.5);"
"}"
"";

char **icon_names = NULL;
gsize n_icon_names = 0;

static void
init_icon_names (GtkIconTheme *theme)
{
  if (icon_names)
    return;

  icon_names = gtk_icon_theme_get_icon_names (theme);
  n_icon_names = g_strv_length (icon_names);
}

static const char *
get_random_icon_name (GtkIconTheme *theme)
{
  init_icon_names (theme);

  return icon_names[g_random_int_range(0, n_icon_names)];
}

/* Can't be static because it's also used in iconscroll.c */
GtkWidget *
create_icon (void)
{
  GtkWidget *image;

  image = gtk_image_new ();
  gtk_image_set_icon_size (GTK_IMAGE (image), GTK_ICON_SIZE_LARGE);
  gtk_image_set_from_icon_name (GTK_IMAGE (image),
                                get_random_icon_name (gtk_icon_theme_get_for_display (gtk_widget_get_display (image))));

  return image;
}

static GtkWidget *
create_button (void)
{
  return gtk_button_new_with_label ("Button");
}

static GtkWidget *
create_blurred_button (void)
{
  GtkWidget *w = gtk_button_new ();

  gtk_widget_add_css_class (w, "blurred-button");

  return w;
}

static GtkWidget *
create_font_button (void)
{
  return gtk_font_button_new ();
}

static GtkWidget *
create_level_bar (void)
{
  GtkWidget *w = gtk_level_bar_new_for_interval (0, 100);

  gtk_level_bar_set_value (GTK_LEVEL_BAR (w), 50);

  /* Force them to be a bit larger */
  gtk_widget_set_size_request (w, 200, -1);

  return w;
}

static GtkWidget *
create_spinner (void)
{
  GtkWidget *w = gtk_spinner_new ();

  gtk_spinner_start (GTK_SPINNER (w));

  return w;
}

static GtkWidget *
create_spinbutton (void)
{
  GtkWidget *w = gtk_spin_button_new_with_range (0, 10, 1);

  return w;
}

static GtkWidget *
create_label (void)
{
  GtkWidget *w = gtk_label_new ("pLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.");

  gtk_label_set_wrap (GTK_LABEL (w), TRUE);
  gtk_label_set_max_width_chars (GTK_LABEL (w), 100);

  return w;
}

static GtkWidget *
create_video (void)
{
  GtkWidget *w = gtk_video_new ();

  gtk_widget_set_size_request (w, 64, 64);
  gtk_video_set_loop (GTK_VIDEO (w), TRUE);
  gtk_video_set_autoplay (GTK_VIDEO (w), TRUE);
  gtk_video_set_resource (GTK_VIDEO (w), "/images/gtk-logo.webm");

  return w;
}

static GtkWidget *
create_gears (void)
{
  GtkWidget *w = gtk_gears_new ();

  gtk_widget_set_size_request (w, 100, 100);

  return w;
}

static GtkWidget *
create_switch (void)
{
  GtkWidget *w = gtk_switch_new ();

  gtk_switch_set_state (GTK_SWITCH (w), TRUE);

  return w;
}

static gboolean
update_paintable (GtkWidget     *widget,
                  GdkFrameClock *frame_clock,
                  gpointer       user_data)
{
  GskShaderPaintable *paintable;
  gint64 frame_time;

  paintable = GSK_SHADER_PAINTABLE (gtk_picture_get_paintable (GTK_PICTURE (widget)));
  frame_time = gdk_frame_clock_get_frame_time (frame_clock);
  gsk_shader_paintable_update_time (paintable, 0, frame_time);

  return G_SOURCE_CONTINUE;
}

static GtkWidget *
create_cogs (void)
{
  GtkWidget *picture;
  static GskGLShader *cog_shader = NULL;
  GdkPaintable *paintable;

 if (cog_shader == NULL)
    cog_shader = gsk_gl_shader_new_from_resource ("/gltransition/cogs2.glsl");
  paintable = gsk_shader_paintable_new (g_object_ref (cog_shader), NULL);
  picture = gtk_picture_new_for_paintable (paintable);
  gtk_widget_set_size_request (picture, 150, 75);
  gtk_widget_add_tick_callback (picture, update_paintable, NULL, NULL);

  return picture;
}

static void
mapped (GtkWidget *w)
{
  gtk_menu_button_popup (GTK_MENU_BUTTON (w));
}

static GtkWidget *
create_menu_button (void)
{
  GtkWidget *w = gtk_menu_button_new ();
  GtkWidget *popover = gtk_popover_new ();

  gtk_popover_set_child (GTK_POPOVER (popover), gtk_button_new_with_label ("Hey!"));
  gtk_popover_set_autohide (GTK_POPOVER (popover), FALSE);
  gtk_menu_button_set_popover (GTK_MENU_BUTTON (w), popover);
  g_signal_connect (w, "map", G_CALLBACK (mapped), NULL);

  return w;
}

static const struct {
  const char *name;
  GtkWidget * (*create_func) (void);
} widget_types[] = {
  { "Icon",       create_icon           },
  { "Button",     create_button         },
  { "Blurbutton", create_blurred_button },
  { "Fontbutton", create_font_button    },
  { "Levelbar",   create_level_bar      },
  { "Label",      create_label          },
  { "Spinner",    create_spinner        },
  { "Spinbutton", create_spinbutton     },
  { "Video",      create_video          },
  { "Gears",      create_gears          },
  { "Switch",     create_switch         },
  { "Menubutton", create_menu_button    },
  { "Shader",     create_cogs           },
};

static int selected_widget_type = -1;
static const int N_WIDGET_TYPES = G_N_ELEMENTS (widget_types);

static void
set_widget_type (GtkFishbowl *fishbowl,
                 int          widget_type_index)
{
  GtkWidget *window;

  if (widget_type_index == selected_widget_type)
    return;

  selected_widget_type = widget_type_index;

  gtk_fishbowl_set_creation_func (fishbowl,
                                  widget_types[selected_widget_type].create_func);

  window = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (fishbowl)));
  gtk_window_set_title (GTK_WINDOW (window),
                        widget_types[selected_widget_type].name);
}

G_MODULE_EXPORT void
fishbowl_next_button_clicked_cb (GtkButton *source,
                                 gpointer   user_data)
{
  GtkFishbowl *fishbowl = user_data;
  int new_index;

  if (selected_widget_type + 1 >= N_WIDGET_TYPES)
    new_index = 0;
  else
    new_index = selected_widget_type + 1;

  set_widget_type (fishbowl, new_index);
}

G_MODULE_EXPORT void
fishbowl_prev_button_clicked_cb (GtkButton *source,
                                 gpointer   user_data)
{
  GtkFishbowl *fishbowl = user_data;
  int new_index;

  if (selected_widget_type - 1 < 0)
    new_index = N_WIDGET_TYPES - 1;
  else
    new_index = selected_widget_type - 1;

  set_widget_type (fishbowl, new_index);
}

G_MODULE_EXPORT void
fishbowl_changes_toggled_cb (GtkToggleButton *button,
                             gpointer         user_data)
{
  if (gtk_toggle_button_get_active (button))
    gtk_button_set_icon_name (GTK_BUTTON (button), "changes-prevent");
  else
    gtk_button_set_icon_name (GTK_BUTTON (button), "changes-allow");
}

G_MODULE_EXPORT char *
format_header_cb (GObject *object,
                  guint    count,
                  double   fps)
{
  return g_strdup_printf ("%u Icons, %.2f fps", count, fps);
}

GtkWidget *
do_fishbowl (GtkWidget *do_widget)
{
  static GtkWidget *window = NULL;
  static GtkCssProvider *provider = NULL;

  if (provider == NULL)
    {
      provider = gtk_css_provider_new ();
      gtk_css_provider_load_from_data (provider, css, -1);
      gtk_style_context_add_provider_for_display (gdk_display_get_default (),
                                                  GTK_STYLE_PROVIDER (provider),
                                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    }

  if (!window)
    {
      GtkBuilder *builder;
      GtkWidget *bowl;

      g_type_ensure (GTK_TYPE_FISHBOWL);

      builder = gtk_builder_new_from_resource ("/fishbowl/fishbowl.ui");
      window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
      g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);

      bowl = GTK_WIDGET (gtk_builder_get_object (builder, "bowl"));
      selected_widget_type = -1;
      set_widget_type (GTK_FISHBOWL (bowl), 0);
      gtk_window_set_display (GTK_WINDOW (window),
                              gtk_widget_get_display (do_widget));

      gtk_widget_realize (window);
      g_object_unref (builder);
    }

  if (!gtk_widget_get_visible (window))
    gtk_widget_show (window);
  else
    gtk_window_destroy (GTK_WINDOW (window));

  return window;
}
