/* GStreamer Editing Services
 *
 * Copyright (C) <2013> Thibault Saunier <thibault.saunier@collabora.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "test-utils.h"
#include <ges/ges.h>
#include <gst/check/gstcheck.h>

GST_START_TEST (test_move_group)
{
  GESAsset *asset;
  GESGroup *group;
  GESTimeline *timeline;
  GESLayer *layer, *layer1;
  GESClip *clip, *clip1, *clip2;

  GList *clips = NULL;

  ges_init ();

  timeline = ges_timeline_new_audio_video ();

  layer = ges_timeline_append_layer (timeline);
  layer1 = ges_timeline_append_layer (timeline);
  asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);

  /**
   * Our timeline:
   * -------------
   *
   *          0------------Group1---------------110
   *          |--------                          |
   * layer:   |  clip  |                         |
   *          |-------10                         |
   *          |----------------------------------|
   *          |        0---------    0-----------|
   * layer1:  |        | clip1   |    |  clip2   |
   *          |       10--------20   50----------|
   *          |----------------------------------|
   */
  clip = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
  clip1 =
      ges_layer_add_asset (layer1, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
  clip2 =
      ges_layer_add_asset (layer1, asset, 50, 0, 60, GES_TRACK_TYPE_UNKNOWN);
  clips = g_list_prepend (clips, clip);
  clips = g_list_prepend (clips, clip1);
  clips = g_list_prepend (clips, clip2);
  group = GES_GROUP (ges_container_group (clips));
  g_list_free (clips);
  ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);

  fail_unless (GES_IS_GROUP (group));
  ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
  fail_unless (g_list_length (GES_CONTAINER_CHILDREN (group)) == 3);
  assert_equals_int (GES_CONTAINER_HEIGHT (group), 2);

  /* Nothing should move */
  ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip1), 5);

  CHECK_OBJECT_PROPS (clip, 0, 0, 10);
  CHECK_OBJECT_PROPS (clip1, 10, 0, 10);
  CHECK_OBJECT_PROPS (clip2, 50, 0, 60);
  CHECK_OBJECT_PROPS (group, 0, 0, 110);

  /*
   *        0  10------------Group1---------------120
   *            |--------                          |
   * layer:     |  clip  |                         |
   *            |-------20                         |
   *            |----------------------------------|
   *            |        0---------    0-----------|
   * layer1:    |        | clip1   |    |  clip2   |
   *            |       20--------30   60----------|
   *            |----------------------------------|
   */
  ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (clip), 10);
  CHECK_OBJECT_PROPS (clip, 10, 0, 10);
  CHECK_OBJECT_PROPS (clip1, 20, 0, 10);
  CHECK_OBJECT_PROPS (clip2, 60, 0, 60);
  CHECK_OBJECT_PROPS (group, 10, 0, 110);

  /*
   *        0  10------------Group1---------------120
   *            |------                            |
   * layer:     |clip  |                           |
   *            |-----15                           |
   *            |----------------------------------|
   *            |        0---------    0-----------|
   * layer1:    |        | clip1   |    |  clip2   |
   *            |       20--------30   60----------|
   *            |----------------------------------|
   */
  ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip), 5);
  CHECK_OBJECT_PROPS (clip, 10, 0, 5);
  CHECK_OBJECT_PROPS (clip1, 20, 0, 10);
  CHECK_OBJECT_PROPS (clip2, 60, 0, 60);
  CHECK_OBJECT_PROPS (group, 10, 0, 110);
  ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);

  /*
   *        0  10------------Group1---------------110
   *            |------                            |
   * layer:     |clip  |                           |
   *            |-----15                           |
   *            |----------------------------------|
   *            |        0---------    0-----------|
   * layer1:    |        | clip1   |    |  clip2   |
   *            |       20--------30   60----------|
   *            |----------------------------------|
   */
  ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip2), 50);
  CHECK_OBJECT_PROPS (clip, 10, 0, 5);
  CHECK_OBJECT_PROPS (clip1, 20, 0, 10);
  CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
  CHECK_OBJECT_PROPS (group, 10, 0, 100);

  /*
   *        0  10------------Group1---------------110
   *            |------                            |
   * layer:     |clip  |                           |
   *            |-----15                           |
   *            |----------------------------------|
   *            |        5---------    0-----------|
   * layer1:    |        | clip1   |    |  clip2   |
   *            |       20--------30   60----------|
   *            |----------------------------------|
   */
  ges_timeline_element_set_inpoint (GES_TIMELINE_ELEMENT (clip1), 5);
  CHECK_OBJECT_PROPS (clip, 10, 0, 5);
  CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
  CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
  CHECK_OBJECT_PROPS (group, 10, 0, 100);

  /*
   *        0  10------------Group1---------------110
   *            |------                            |
   * layer:     |clip  |                           |
   *            |-----15                           |
   *            |----------------------------------|
   *            |        5---------    0-----------|
   * layer1:    |        | clip1   |    |  clip2   |
   *            |       20--------30   60----------|
   *            |----------------------------------|
   */
  ges_timeline_element_set_inpoint (GES_TIMELINE_ELEMENT (clip1), 5);
  CHECK_OBJECT_PROPS (clip, 10, 0, 5);
  CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
  CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
  CHECK_OBJECT_PROPS (group, 10, 0, 100);
  ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
  fail_if (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 20));
  CHECK_OBJECT_PROPS (clip, 10, 0, 5);
  CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
  CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
  CHECK_OBJECT_PROPS (group, 10, 0, 100);
  ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);

  fail_if (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 25));
  CHECK_OBJECT_PROPS (clip, 10, 0, 5);
  CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
  CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
  CHECK_OBJECT_PROPS (group, 10, 0, 100);
  ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);

  /* Same thing in the end... */
  ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 10);
  CHECK_OBJECT_PROPS (clip, 10, 0, 5);
  CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
  CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
  CHECK_OBJECT_PROPS (group, 10, 0, 100);
  ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);

  /*
   *        0  12------------Group1---------------110
   *            2------                            |
   * layer:     |clip  |                           |
   *            |-----15                           |
   *            |----------------------------------|
   *            |        7---------     2----------|
   * layer1:    |        | clip1   |    |  clip2   |
   *            |       20--------30   60----------|
   *            |----------------------------------|
   */
  ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 12);
  CHECK_OBJECT_PROPS (clip, 12, 2, 3);
  CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
  CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
  CHECK_OBJECT_PROPS (group, 12, 0, 98);
  ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);

  /* Setting the duration would lead to overlaps */
  fail_if (ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group),
          10));
  CHECK_OBJECT_PROPS (clip, 12, 2, 3);
  CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
  CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
  CHECK_OBJECT_PROPS (group, 12, 0, 98);
  ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), 100);
  CHECK_OBJECT_PROPS (clip, 12, 2, 3);
  CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
  CHECK_OBJECT_PROPS (clip2, 60, 0, 52);
  CHECK_OBJECT_PROPS (group, 12, 0, 100);

  ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (group), 20);
  CHECK_OBJECT_PROPS (clip, 20, 2, 3);
  CHECK_OBJECT_PROPS (clip1, 28, 5, 10);
  CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
  CHECK_OBJECT_PROPS (group, 20, 0, 100);

  /* Trim fails because clip inpoint would become negative */
  fail_if (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 10));
  CHECK_OBJECT_PROPS (clip, 20, 2, 3);
  CHECK_OBJECT_PROPS (clip1, 28, 5, 10);
  CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
  CHECK_OBJECT_PROPS (group, 20, 0, 100);

  fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 18));
  CHECK_OBJECT_PROPS (clip, 18, 0, 5);
  CHECK_OBJECT_PROPS (clip1, 28, 5, 10);
  CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
  CHECK_OBJECT_PROPS (group, 18, 0, 102);

  fail_unless (ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip),
          17));
  CHECK_OBJECT_PROPS (clip, 18, 0, 17);
  CHECK_OBJECT_PROPS (clip1, 28, 5, 10);
  CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
  CHECK_OBJECT_PROPS (group, 18, 0, 102);

  fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 30));
  CHECK_OBJECT_PROPS (clip, 30, 12, 5);
  CHECK_OBJECT_PROPS (clip1, 30, 7, 8);
  CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
  CHECK_OBJECT_PROPS (group, 30, 0, 90);

  fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 25));
  CHECK_OBJECT_PROPS (clip, 25, 7, 10);
  CHECK_OBJECT_PROPS (clip1, 25, 2, 13);
  CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
  CHECK_OBJECT_PROPS (group, 25, 0, 95);

  ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
  check_destroyed (G_OBJECT (timeline), G_OBJECT (group), NULL);
  gst_object_unref (asset);

  ges_deinit ();
}

GST_END_TEST;



static void
_changed_layer_cb (GESTimelineElement * clip,
    GParamSpec * arg G_GNUC_UNUSED, guint * nb_calls)
{
  *nb_calls = *nb_calls + 1;
}

GST_START_TEST (test_group_in_group)
{
  GESAsset *asset;
  GESTimeline *timeline;
  GESGroup *group, *group1;
  GESLayer *layer, *layer1, *layer2, *layer3;
  GESClip *c, *c1, *c2, *c3, *c4, *c5;

  guint nb_layer_notifies = 0;
  GList *clips = NULL;

  ges_init ();

  timeline = ges_timeline_new_audio_video ();

  /* Our timeline
   *
   *    --0------------10-Group-----20---------------30-------Group1----------70
   *      | +-----------+                             |+-----------50         |
   * L    | |    C      |                             ||     C3    |          |
   *      | +-----------+                             |+-----------+          |
   *    --|-------------------------------------------|-----40----------------|
   *      |            +------------+ +-------------+ |      +--------60      |
   * L1   |            |     C1     | |     C2      | |      |     C4 |       |
   *      |            +------------+ +-------------+ |      +--------+       |
   *    --|-------------------------------------------|-----------------------|
   *      |                                           |             +--------+|
   * L2   |                                           |             |  c5    ||
   *      |                                           |             +--------+|
   *    --+-------------------------------------------+-----------------------+
   *
   * L3
   *
   *    -----------------------------------------------------------------------
   */

  layer = ges_timeline_append_layer (timeline);
  layer1 = ges_timeline_append_layer (timeline);
  layer2 = ges_timeline_append_layer (timeline);
  layer3 = ges_timeline_append_layer (timeline);
  assert_equals_int (ges_layer_get_priority (layer3), 3);
  asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);

  c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
  c1 = ges_layer_add_asset (layer1, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
  c2 = ges_layer_add_asset (layer1, asset, 20, 0, 10, GES_TRACK_TYPE_UNKNOWN);
  clips = g_list_prepend (clips, c);
  clips = g_list_prepend (clips, c1);
  clips = g_list_prepend (clips, c2);
  group = GES_GROUP (ges_container_group (clips));
  fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
  g_list_free (clips);

  fail_unless (GES_IS_GROUP (group));
  CHECK_OBJECT_PROPS (c, 0, 0, 10);
  CHECK_OBJECT_PROPS (c1, 10, 0, 10);
  CHECK_OBJECT_PROPS (c2, 20, 0, 10);
  CHECK_OBJECT_PROPS (group, 0, 0, 30);

  c3 = ges_layer_add_asset (layer, asset, 30, 0, 20, GES_TRACK_TYPE_UNKNOWN);
  c4 = ges_layer_add_asset (layer1, asset, 40, 0, 20, GES_TRACK_TYPE_UNKNOWN);
  c5 = ges_layer_add_asset (layer2, asset, 50, 0, 20, GES_TRACK_TYPE_UNKNOWN);
  clips = g_list_prepend (NULL, c3);
  clips = g_list_prepend (clips, c4);
  clips = g_list_prepend (clips, c5);
  group1 = GES_GROUP (ges_container_group (clips));
  fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group1) == timeline);
  g_list_free (clips);

  fail_unless (GES_IS_GROUP (group1));
  CHECK_OBJECT_PROPS (c3, 30, 0, 20);
  CHECK_OBJECT_PROPS (c4, 40, 0, 20);
  CHECK_OBJECT_PROPS (c5, 50, 0, 20);
  CHECK_OBJECT_PROPS (group1, 30, 0, 40);
  check_layer (c, 0);
  check_layer (c1, 1);
  check_layer (c2, 1);
  check_layer (c3, 0);
  check_layer (c4, 1);
  check_layer (c5, 2);

  fail_unless (ges_container_add (GES_CONTAINER (group),
          GES_TIMELINE_ELEMENT (group1)));
  CHECK_OBJECT_PROPS (c, 0, 0, 10);
  CHECK_OBJECT_PROPS (c1, 10, 0, 10);
  CHECK_OBJECT_PROPS (c2, 20, 0, 10);
  CHECK_OBJECT_PROPS (c3, 30, 0, 20);
  CHECK_OBJECT_PROPS (c4, 40, 0, 20);
  CHECK_OBJECT_PROPS (c5, 50, 0, 20);
  CHECK_OBJECT_PROPS (group, 0, 0, 70);
  CHECK_OBJECT_PROPS (group1, 30, 0, 40);
  check_layer (c, 0);
  check_layer (c1, 1);
  check_layer (c2, 1);
  check_layer (c3, 0);
  check_layer (c4, 1);
  check_layer (c5, 2);

  fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
  fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group1) == timeline);

  ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (c4), 50);
  CHECK_OBJECT_PROPS (c, 10, 0, 10);
  CHECK_OBJECT_PROPS (c1, 20, 0, 10);
  CHECK_OBJECT_PROPS (c2, 30, 0, 10);
  CHECK_OBJECT_PROPS (c3, 40, 0, 20);
  CHECK_OBJECT_PROPS (c4, 50, 0, 20);
  CHECK_OBJECT_PROPS (c5, 60, 0, 20);
  CHECK_OBJECT_PROPS (group, 10, 0, 70);
  CHECK_OBJECT_PROPS (group1, 40, 0, 40);
  fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
  fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group1) == timeline);
  check_layer (c, 0);
  check_layer (c1, 1);
  check_layer (c2, 1);
  check_layer (c3, 0);
  check_layer (c4, 1);
  check_layer (c5, 2);

  /* Our timeline
   *
   * L
   *    -----------------------------------------------------------------------
   *      0------------10-Group-----20---------------30-------Group1----------70
   *      | +-----------+                             |+-----------50         |
   * L1   | |    C      |                             ||     C3    |          |
   *      | +-----------+                             |+-----------+          |
   *      |                                           |                       |
   *    --|-------------------------------------------|-----40----------------|
   *      |            +------------+ +-------------+ |      +--------60      |
   * L2   |            |     C1     | |     C2      | |      |     C4 |       |
   *      |            +------------+ +-------------+ |      +--------+       |
   *    --|-------------------------------------------|-----------------------|
   *      |                                           |             +--------+|
   * L3   |                                           |             |  c5    ||
   *      |                                           |             +--------+|
   *    --+-------------------------------------------+-----------------------+
   */
  fail_unless (ges_clip_move_to_layer (c, layer1));
  check_layer (c, 1);
  check_layer (c1, 2);
  check_layer (c2, 2);
  check_layer (c3, 1);
  check_layer (c4, 2);
  check_layer (c5, 3);
  assert_equals_int (_PRIORITY (group), 1);
  assert_equals_int (_PRIORITY (group1), 1);

  /* We can not move so far! */
  g_signal_connect_after (c4, "notify::layer",
      (GCallback) _changed_layer_cb, &nb_layer_notifies);
  fail_if (ges_clip_move_to_layer (c4, layer));
  assert_equals_int (nb_layer_notifies, 0);
  check_layer (c, 1);
  check_layer (c1, 2);
  check_layer (c2, 2);
  check_layer (c3, 1);
  check_layer (c4, 2);
  check_layer (c5, 3);
  assert_equals_int (_PRIORITY (group), 1);
  assert_equals_int (_PRIORITY (group1), 1);

  clips = ges_container_ungroup (GES_CONTAINER (group), FALSE);
  assert_equals_int (g_list_length (clips), 4);
  g_list_free_full (clips, gst_object_unref);

  gst_object_unref (timeline);
  gst_object_unref (asset);

  ges_deinit ();
}

GST_END_TEST;

GST_START_TEST (test_group_in_group_layer_moving)
{
  GESAsset *asset;
  GESTimeline *timeline;
  GESGroup *group;
  GESLayer *layer, *layer1, *layer2, *layer3;
  GESClip *c, *c1;

  GList *clips = NULL;

  ges_init ();

  timeline = ges_timeline_new_audio_video ();

  /* Our timeline
   *
   *    --0------------10-Group-----20
   *      | +-----------+           |
   * L    | |    C      |           |
   *      | +-----------+           |
   *    --|--------------------------
   *      |            +------------+
   * L1   |            |     C1     |
   *      |            +------------+
   *    -----------------------------
   */

  layer = ges_timeline_append_layer (timeline);
  layer1 = ges_timeline_append_layer (timeline);
  layer2 = ges_timeline_append_layer (timeline);
  layer3 = ges_timeline_append_layer (timeline);
  fail_unless (layer2 && layer3);
  assert_equals_int (ges_layer_get_priority (layer3), 3);
  asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);

  c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
  c1 = ges_layer_add_asset (layer1, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
  clips = g_list_prepend (clips, c);
  clips = g_list_prepend (clips, c1);
  group = GES_GROUP (ges_container_group (clips));
  fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
  g_list_free (clips);

  fail_unless (GES_IS_GROUP (group));
  CHECK_OBJECT_PROPS (c, 0, 0, 10);
  CHECK_OBJECT_PROPS (c1, 10, 0, 10);
  CHECK_OBJECT_PROPS (group, 0, 0, 20);

  /* Our timeline
   *
   *    --0--------10-----------20-Group----30
   *      |         +-----------+           |
   * L    |         |    C      |           |
   *      |         +-----------+           |
   *    --|-----------------------------------
   *      |                    +------------+
   * L1   |                    |     C1     |
   *      |                    +------------+
   *    -------------------------------------
   */
  fail_unless (ges_container_edit (GES_CONTAINER (c), NULL,
          -1, GES_EDIT_MODE_NORMAL, GES_EDGE_NONE, 10));

  CHECK_OBJECT_PROPS (c, 10, 0, 10);
  CHECK_OBJECT_PROPS (c1, 20, 0, 10);
  CHECK_OBJECT_PROPS (group, 10, 0, 20);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c), 0);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c1), 1);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (group), 0);

  ges_layer_set_priority (layer2, 0);
  /* no change since none of the clips are in layer2 */
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c), 0);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c1), 1);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (group), 0);

  ges_layer_set_priority (layer, 1);
  /* c's layer now has priority 1 (conflicts with layer1) */
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c), 1);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c1), 1);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (group), 1);

  ges_layer_set_priority (layer1, 2);
  /* conflicting layer priorities now resolved */
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c), 1);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c1), 2);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (group), 1);

  /* Our timeline
   *
   *    --0--------10-----------20-Group----30
   *      |         +-----------+           |
   * L2   |         |    C      |           |
   *      |         +-----------+           |
   *    --|-----------------------------------
   *      |                    +------------+
   * L    |                    |     C1     |
   *      |                    +------------+
   *    -------------------------------------
   *
   * L1
   *    -------------------------------------
   */
  fail_unless (ges_container_edit (GES_CONTAINER (c), NULL,
          0, GES_EDIT_MODE_NORMAL, GES_EDGE_NONE, 10));
  CHECK_OBJECT_PROPS (c, 10, 0, 10);
  CHECK_OBJECT_PROPS (c1, 20, 0, 10);
  CHECK_OBJECT_PROPS (group, 10, 0, 20);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c), 0);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c1), 1);

  /* Our timeline
   *
   *    --0--------10-----------20-Group----30
   * L2   |                                 |
   *   --------------------------------------
   *      |         +-----------+           |
   * L    |         |    C      |           |
   *      |         +-----------+           |
   *    --|-----------------------------------
   *      |                    +------------+
   * L1   |                    |     C1     |
   *      |                    +------------+
   *    -------------------------------------
   */
  fail_unless (ges_container_edit (GES_CONTAINER (c), NULL,
          1, GES_EDIT_MODE_NORMAL, GES_EDGE_NONE, 10));
  CHECK_OBJECT_PROPS (c, 10, 0, 10);
  CHECK_OBJECT_PROPS (c1, 20, 0, 10);
  CHECK_OBJECT_PROPS (group, 10, 0, 20);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c), 1);
  assert_equals_int (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (c1), 2);

  gst_object_unref (timeline);
  gst_object_unref (asset);

  ges_deinit ();
}

GST_END_TEST;


GST_START_TEST (test_group_in_self)
{
  GESLayer *layer;
  GESClip *c, *c1;
  GESAsset *asset;
  GESTimeline *timeline;
  GESGroup *group;

  GList *clips = NULL;

  ges_init ();

  timeline = ges_timeline_new_audio_video ();

  layer = ges_timeline_append_layer (timeline);
  asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);

  c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);
  c1 = ges_layer_add_asset (layer, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);
  clips = g_list_prepend (clips, c);
  clips = g_list_prepend (clips, c1);


  group = GES_GROUP (ges_container_group (clips));
  fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
  g_list_free (clips);

  fail_if (ges_container_add (GES_CONTAINER (group),
          GES_TIMELINE_ELEMENT (group)));
  clips = ges_container_get_children (GES_CONTAINER (group), TRUE);
  assert_equals_int (g_list_length (clips), 6);
  g_list_free_full (clips, g_object_unref);

  gst_object_unref (timeline);
  gst_object_unref (asset);

  ges_deinit ();
}

GST_END_TEST;

static void
project_loaded_cb (GESProject * project, GESTimeline * timeline,
    GMainLoop * mainloop)
{
  GST_ERROR ("LOADED!");
  g_main_loop_quit (mainloop);
}

GST_START_TEST (test_group_serialization)
{
  gchar *tmpuri;
  GESLayer *layer;
  GESClip *c, *c1, *c2, *c3;
  GESAsset *asset;
  GESTimeline *timeline;
  GESGroup *group;
  GESProject *project;
  GMainLoop *mainloop;

  GError *err = NULL;
  GList *tmp, *clips = NULL;

  ges_init ();

  timeline = ges_timeline_new_audio_video ();

  layer = ges_timeline_append_layer (timeline);
  asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);

  c = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN);

  c1 = ges_layer_add_asset (layer, asset, 10, 0, 10, GES_TRACK_TYPE_UNKNOWN);

  clips = g_list_prepend (clips, c);
  clips = g_list_prepend (clips, c1);

  c2 = ges_layer_add_asset (layer, asset, 20, 0, 10, GES_TRACK_TYPE_UNKNOWN);

  c3 = ges_layer_add_asset (layer, asset, 30, 0, 10, GES_TRACK_TYPE_UNKNOWN);

  group = GES_GROUP (ges_container_group (clips));
  fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
  g_list_free (clips);


  clips = g_list_append (NULL, group);
  clips = g_list_append (clips, c2);
  group = GES_GROUP (ges_container_group (clips));
  fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
  g_list_free (clips);

  clips = g_list_append (NULL, group);
  clips = g_list_append (clips, c3);
  group = GES_GROUP (ges_container_group (clips));
  fail_unless (GES_TIMELINE_ELEMENT_TIMELINE (group) == timeline);
  g_list_free (clips);

  project =
      GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE (timeline)));

  tmpuri = ges_test_get_tmp_uri ("test-auto-transition-save.xges");
  fail_unless (ges_project_save (project, timeline, tmpuri, NULL, TRUE, NULL));
  gst_object_unref (timeline);
  gst_object_unref (asset);

  project = ges_project_new (tmpuri);
  mainloop = g_main_loop_new (NULL, FALSE);
  g_signal_connect (project, "loaded", (GCallback) project_loaded_cb, mainloop);
  timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), &err));
  g_main_loop_run (mainloop);

  fail_unless (err == NULL, "%s", err ? err->message : "Nothing");
  fail_unless (timeline != NULL);

  layer = timeline->layers->data;
  clips = ges_layer_get_clips (layer);
  for (tmp = clips; tmp; tmp = tmp->next) {
    fail_unless (GES_IS_GROUP (GES_TIMELINE_ELEMENT_PARENT (tmp->data)),
        "%s parent is %p, NOT a group", GES_TIMELINE_ELEMENT_NAME (tmp->data),
        GES_TIMELINE_ELEMENT_PARENT (tmp->data));
  }
  g_list_free_full (clips, g_object_unref);

  g_free (tmpuri);
  gst_object_unref (timeline);
  gst_object_unref (asset);

  ges_deinit ();
}

GST_END_TEST;

GST_START_TEST (test_children_properties_contain)
{
  GESTimeline *timeline;
  GESLayer *layer;
  GESAsset *asset;
  GESTimelineElement *c1, *c2, *c3, *g1, *g2;
  GParamSpec **child_props1, **child_props2;
  guint num_props1, num_props2;

  ges_init ();

  timeline = ges_timeline_new_audio_video ();
  layer = ges_timeline_append_layer (timeline);

  asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
  /* choose one audio and one video to give them different properties */
  c1 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 0, 0, 10,
          GES_TRACK_TYPE_AUDIO));
  c2 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 20, 0, 10,
          GES_TRACK_TYPE_VIDEO));
  /* but c3 will have the same child properties as c1! */
  c3 = GES_TIMELINE_ELEMENT (ges_layer_add_asset (layer, asset, 40, 0, 10,
          GES_TRACK_TYPE_AUDIO));

  fail_unless (c1);
  fail_unless (c2);

  g1 = GES_TIMELINE_ELEMENT (ges_group_new ());
  g2 = GES_TIMELINE_ELEMENT (ges_group_new ());

  /* group should have the same as its children */
  fail_unless (ges_container_add (GES_CONTAINER (g1), c1));

  num_props1 = 0;
  child_props1 = append_children_properties (NULL, c1, &num_props1);
  num_props2 = 0;
  child_props2 = append_children_properties (NULL, g1, &num_props2);

  assert_property_list_match (child_props1, num_props1,
      child_props2, num_props2);

  /* add next child and gain its children properties as well */
  fail_unless (ges_container_add (GES_CONTAINER (g1), c2));

  /* add the child properties of c2 to the existing list for c1 */
  child_props1 = append_children_properties (child_props1, c2, &num_props1);

  free_children_properties (child_props2, num_props2);
  num_props2 = 0;
  child_props2 = append_children_properties (NULL, g1, &num_props2);

  assert_property_list_match (child_props1, num_props1,
      child_props2, num_props2);

  /* FIXME: if c1 and c3 have the same child properties (they use the
   * same GParamSpec) then ges_timeline_element_add_child_property_full
   * will fail, even though the corresponding GObject child is not the
   * same instance */

  fail_unless (ges_container_add (GES_CONTAINER (g1), c3));

  /* FIXME: regarding the above comment, ideally we would append the
   * children properties for c3 to child_props1, so that its children
   * properties appear twice in the list:
   * child_props1 =
   * append_children_properties (child_props1, c3, &num_props1); */

  free_children_properties (child_props2, num_props2);
  num_props2 = 0;
  child_props2 = append_children_properties (NULL, g1, &num_props2);

  assert_property_list_match (child_props1, num_props1,
      child_props2, num_props2);

  /* remove c3 */
  fail_unless (ges_container_remove (GES_CONTAINER (g1), c3));

  /* FIXME: regarding the above comment, ideally we would reset
   * child_props1 to only contain the child properties for c1 and c2
   * Currently, we at least want to make sure that the child properties
   * for c1 remain.
   * Currently, if we removed c1 first, all its children properties would
   * be removed from g1, and this would *not* automatically register the
   * children properties for c3. */

  free_children_properties (child_props2, num_props2);
  num_props2 = 0;
  child_props2 = append_children_properties (NULL, g1, &num_props2);

  assert_property_list_match (child_props1, num_props1,
      child_props2, num_props2);

  /* remove c1 */
  fail_unless (ges_container_remove (GES_CONTAINER (g1), c1));

  free_children_properties (child_props1, num_props1);
  num_props1 = 0;
  child_props1 = append_children_properties (NULL, c2, &num_props1);

  free_children_properties (child_props2, num_props2);
  num_props2 = 0;
  child_props2 = append_children_properties (NULL, g1, &num_props2);

  assert_property_list_match (child_props1, num_props1,
      child_props2, num_props2);

  /* add g1 and c1 to g2 */
  fail_unless (ges_container_add (GES_CONTAINER (g2), g1));
  fail_unless (ges_container_add (GES_CONTAINER (g2), c1));

  free_children_properties (child_props1, num_props1);
  num_props1 = 0;
  child_props1 = append_children_properties (NULL, g2, &num_props1);

  free_children_properties (child_props2, num_props2);
  num_props2 = 0;
  child_props2 = append_children_properties (NULL, c1, &num_props2);
  child_props2 = append_children_properties (child_props2, g1, &num_props2);

  assert_property_list_match (child_props1, num_props1,
      child_props2, num_props2);

  free_children_properties (child_props1, num_props1);
  free_children_properties (child_props2, num_props2);

  gst_object_unref (timeline);
  gst_object_unref (asset);

  ges_deinit ();
}

GST_END_TEST;




static Suite *
ges_suite (void)
{
  Suite *s = suite_create ("ges-group");
  TCase *tc_chain = tcase_create ("group");

  suite_add_tcase (s, tc_chain);

  tcase_add_test (tc_chain, test_move_group);
  tcase_add_test (tc_chain, test_group_in_group);
  tcase_add_test (tc_chain, test_group_in_self);
  tcase_add_test (tc_chain, test_group_serialization);
  tcase_add_test (tc_chain, test_group_in_group_layer_moving);
  tcase_add_test (tc_chain, test_children_properties_contain);

  return s;
}

GST_CHECK_MAIN (ges);
