/* Fixed layout
 *
 * GtkFixed is a container that allows placing and transforming
 * widgets manually.
 */

#include <gtk/gtk.h>

/* This enumeration determines the paint order */
enum {
  FACE_BACK,
  FACE_LEFT,
  FACE_BOTTOM,
  FACE_RIGHT,
  FACE_TOP,
  FACE_FRONT,

  N_FACES
};

/* Map face widgets to CSS classes */
static struct {
  GtkWidget *face;
  const char *css_class;
} faces[N_FACES] = {
  [FACE_BACK] = { NULL, "back", },
  [FACE_LEFT] = { NULL, "left", },
  [FACE_RIGHT] = { NULL, "right", },
  [FACE_TOP] = { NULL, "top", },
  [FACE_BOTTOM] = { NULL, "bottom", },
  [FACE_FRONT] = { NULL, "front", },
};

static GtkWidget *
create_faces (void)
{
  GtkWidget *fixed = gtk_fixed_new ();
  int face_size = 200;
  float w, h, d, p;

  gtk_widget_set_overflow (fixed, GTK_OVERFLOW_VISIBLE);

  w = (float) face_size / 2.f;
  h = (float) face_size / 2.f;
  d = (float) face_size / 2.f;
  p = face_size * 3.f;

  for (int i = 0; i < N_FACES; i++)
    {
      GskTransform *transform = NULL;

      /* Add a face */
      faces[i].face = gtk_frame_new (NULL);
      gtk_widget_set_size_request (faces[i].face, face_size, face_size);
      gtk_style_context_add_class (gtk_widget_get_style_context (faces[i].face), faces[i].css_class);
      gtk_container_add (GTK_CONTAINER (fixed), faces[i].face);

      /* Set up the transformation for each face */
      transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (w, h));
      transform = gsk_transform_perspective (transform, p);
      transform = gsk_transform_rotate_3d (transform, -30.f, graphene_vec3_x_axis ());
      transform = gsk_transform_rotate_3d (transform, 135.f, graphene_vec3_y_axis ());
      transform = gsk_transform_translate_3d (transform, &GRAPHENE_POINT3D_INIT (0, 0, -face_size / 6.f));

      switch (i)
        {
        case FACE_FRONT:
          transform = gsk_transform_rotate_3d (transform, 0.f, graphene_vec3_y_axis ());
          break;

        case FACE_BACK:
          transform = gsk_transform_rotate_3d (transform, -180.f, graphene_vec3_y_axis ());
          break;

        case FACE_RIGHT:
          transform = gsk_transform_rotate_3d (transform, 90.f, graphene_vec3_y_axis ());
          break;

        case FACE_LEFT:
          transform = gsk_transform_rotate_3d (transform, -90.f, graphene_vec3_y_axis ());
          break;

        case FACE_TOP:
          transform = gsk_transform_rotate_3d (transform, 90.f, graphene_vec3_x_axis ());
          break;

        case FACE_BOTTOM:
          transform = gsk_transform_rotate_3d (transform, -90.f, graphene_vec3_x_axis ());
          break;

        default:
          break;
        }

      transform = gsk_transform_translate_3d (transform, &GRAPHENE_POINT3D_INIT (0, 0, d));
      transform = gsk_transform_translate_3d (transform, &GRAPHENE_POINT3D_INIT (-w, -h, 0));

      gtk_fixed_set_child_transform (GTK_FIXED (fixed), faces[i].face, transform);
      gsk_transform_unref (transform);
    }

  return fixed;
}

static GtkWidget *demo_window = NULL;
static GtkCssProvider *provider = NULL;

static void
close_window (GtkWidget *widget)
{
  /* Reset the state */
  for (int i = 0; i < N_FACES; i++)
    faces[i].face = NULL;

  gtk_style_context_remove_provider_for_display (gdk_display_get_default (),
                                                 GTK_STYLE_PROVIDER (provider));
  provider = NULL;

  demo_window = NULL;
}

static GtkWidget *
create_demo_window (GtkWidget *do_widget)
{
  GtkWidget *window, *sw, *fixed, *cube;

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_display (GTK_WINDOW (window),  gtk_widget_get_display (do_widget));
  gtk_window_set_title (GTK_WINDOW (window), "Fixed layout");
  gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
  g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);

  sw = gtk_scrolled_window_new (NULL, NULL);
  gtk_container_add (GTK_CONTAINER (window), sw);

  fixed = gtk_fixed_new ();
  gtk_container_add (GTK_CONTAINER (sw), fixed);
  gtk_widget_set_halign (GTK_WIDGET (fixed), GTK_ALIGN_CENTER);
  gtk_widget_set_valign (GTK_WIDGET (fixed), GTK_ALIGN_CENTER);

  cube = create_faces ();
  gtk_container_add (GTK_CONTAINER (fixed), cube);
  gtk_widget_set_overflow (fixed, GTK_OVERFLOW_VISIBLE);

  provider = gtk_css_provider_new ();
  gtk_css_provider_load_from_resource (provider, "/fixed/fixed.css");
  gtk_style_context_add_provider_for_display (gdk_display_get_default (),
                                              GTK_STYLE_PROVIDER (provider),
                                              800);
  g_object_unref (provider);

  return window;
}

GtkWidget*
do_fixed (GtkWidget *do_widget)
{
  if (demo_window == NULL)
    demo_window = create_demo_window (do_widget);

  if (!gtk_widget_get_visible (demo_window))
    gtk_widget_show (demo_window);
  else
    gtk_widget_destroy (demo_window);

  return demo_window;
}
