/* Build with gcc folks-test.c `pkg-config folks --cflags --libs` */

#include <folks/folks.h>

static gint64 start_time;
static guint idle_id;
static GHashTable *acc_added;
static GHashTable *acc_removed;


static void
aggregator_individuals_changed_cb (FolksIndividualAggregator *aggregator,
    GeeMultiMap *changes,
    gpointer user_data)
{
  GeeSet *removed;
  GeeCollection *added;
  GeeIterator *iter;

  removed = gee_multi_map_get_keys (changes);
  added = gee_multi_map_get_values (changes);

  g_print ("%f: Added: %d, Removed: %d, quiescent: %s\n",
      (g_get_monotonic_time () - start_time) / 1000000.0,
      gee_collection_get_size (GEE_COLLECTION (added)),
      gee_collection_get_size (GEE_COLLECTION (removed)),
      folks_individual_aggregator_get_is_quiescent (aggregator) ? "Yes" : "No");

  iter = gee_iterable_iterator (GEE_ITERABLE (removed));
  while (gee_iterator_next (iter))
  {
    FolksIndividual *contact = gee_iterator_get (iter);

    if (contact == NULL)
      continue;

    g_hash_table_insert (acc_removed, g_object_ref (contact), contact);
    g_hash_table_remove (acc_added, contact);

    g_object_unref (contact);
  }
  g_clear_object (&iter);

  iter = gee_iterable_iterator (GEE_ITERABLE (added));
  while (gee_iterator_next (iter))
  {
    FolksIndividual *contact = gee_iterator_get (iter);

    if (contact == NULL)
      continue;

    g_hash_table_insert (acc_added, g_object_ref (contact), contact);
    g_hash_table_remove (acc_removed, contact);

    g_object_unref (contact);
  }
  g_clear_object (&iter);

  g_object_unref (added);
  g_object_unref (removed);
}

static gboolean
idle_cb (gpointer user_data)
{
  FolksIndividualAggregator *aggregator = user_data;

  g_print ("%f: idle, quiescent: %s\n",
      (g_get_monotonic_time () - start_time) / 1000000.0,
      folks_individual_aggregator_get_is_quiescent (aggregator) ? "Yes" : "No");

  return TRUE;
}

static void
aggregator_quiescent_cb (FolksIndividualAggregator *aggregator)
{
  g_print ("%f: quiescent changed: %s\n",
      (g_get_monotonic_time () - start_time) / 1000000.0,
      folks_individual_aggregator_get_is_quiescent (aggregator) ? "Yes" : "No");

  if (folks_individual_aggregator_get_is_quiescent (aggregator))
    {
      g_source_remove (idle_id);
      idle_id = 0;

      g_print ("accumulated changes: %d added, %d removed\n",
          g_hash_table_size (acc_added),
          g_hash_table_size (acc_removed));
      g_hash_table_remove_all (acc_added);
      g_hash_table_remove_all (acc_removed);

    }
  else if (idle_id == 0)
    {
      idle_id = g_idle_add (idle_cb, aggregator);
    }
}

static void
aggregator_prepared_cb (GObject *source,
    GAsyncResult *result,
    gpointer user_data)
{
  FolksIndividualAggregator *aggregator = FOLKS_INDIVIDUAL_AGGREGATOR (source);
  GError *error = NULL;

  folks_individual_aggregator_prepare_finish (aggregator, result, &error);
  g_assert_no_error (error);

  g_print ("%f: aggregator prepared\n",
      (g_get_monotonic_time () - start_time) / 1000000.0);
}

int
main (int argc,
  char **argv)
{
  FolksIndividualAggregator *aggregator;
  GMainLoop *loop;

  g_type_init ();

  start_time = g_get_monotonic_time ();

  acc_added = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
  acc_removed = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);

  aggregator = folks_individual_aggregator_new ();
  g_signal_connect (aggregator, "notify::is-quiescent",
      G_CALLBACK (aggregator_quiescent_cb),
      NULL);
  g_signal_connect (aggregator, "individuals-changed-detailed",
      G_CALLBACK (aggregator_individuals_changed_cb),
      NULL);
  folks_individual_aggregator_prepare (aggregator, aggregator_prepared_cb, NULL);

  idle_id = g_idle_add (idle_cb, aggregator);

  loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (loop);

  g_object_unref (aggregator);
  g_hash_table_unref (acc_added);
  g_hash_table_unref (acc_removed);
  g_main_loop_unref (loop);
}
