#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <glib.h>
#include <gio/gio.h>

GCancellable *cancellable;
GFile *src;

static gboolean cancel_func(void *data)
{
    g_cancellable_cancel (cancellable);
    printf("cancell\n");
    return FALSE;
}

static void maybe_cancel ()
{
    int r = rand() % 100;
    /* printf("r = %d\n", r); */
    if (r < 1)
        g_timeout_add (rand() % 100, cancel_func, NULL);
    else if (r < 2)
        g_idle_add (cancel_func, NULL);
}

static void enumerate_cb(GObject *source, GAsyncResult *res, void *data);

static void start_it (void)
{
    g_cancellable_reset (cancellable);
    static int count;

    printf("start it %d\n", count++);
    g_file_enumerate_children_async (src, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
                                          G_FILE_ATTRIBUTE_STANDARD_SIZE ","
                                          G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, 0, cancellable, enumerate_cb, NULL);
}

static void close_cb(GObject *source, GAsyncResult *res, void *data)
{
    GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source);
    GError *error = NULL;

    maybe_cancel ();

    g_file_enumerator_close_finish (enumerator, res, &error);
    if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
        g_error_free (error);
        g_object_unref (enumerator);
        start_it ();
        return;
    }

    if (error) {
        printf("Error1: %s\n", error->message);
        exit (1);
    }

    g_object_unref (enumerator);

    start_it ();
}

static void next_cb(GObject *source, GAsyncResult *res, void *data)
{
    GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source);
    GList *infos, *ptr;
    GError *error = NULL;

    maybe_cancel ();

    infos = g_file_enumerator_next_files_finish (enumerator, res, &error);
    if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
        g_error_free (error);
        g_object_unref (enumerator);
        start_it ();
        return;
    }

    if (error) {
        printf("Error2: %s\n", error->message);
        exit (1);
    }

    if (!infos) {
        g_file_enumerator_close_async (enumerator, 0, cancellable, close_cb, NULL);
        return;
    }

    for (ptr = infos; ptr; ptr = ptr->next) {
        GFileInfo *info = ptr->data;
        /* printf("got info\n"); */
        g_object_unref (info);
    }
    g_list_free (infos);

    g_file_enumerator_next_files_async (enumerator, 100, 0, cancellable, next_cb, NULL);
}

static void enumerate_cb(GObject *source, GAsyncResult *res, void *data)
{
    GFile *src = G_FILE (source);
    GError *error = NULL;
    GFileEnumerator *enumerator;

    maybe_cancel ();

    enumerator = g_file_enumerate_children_finish (src, res, &error);
    if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
        g_error_free (error);
        start_it ();
        return;
    }

    if (error) {
        printf("Error3: %s\n", error->message);
        exit (1);
    }

    g_file_enumerator_next_files_async (enumerator, 100, 0, cancellable, next_cb, NULL);
}

int main(int argc, char **argv)
{
    GMainLoop *loop = g_main_loop_new (NULL, FALSE);
    src = g_file_new_for_commandline_arg (argv[1]);
    cancellable = g_cancellable_new ();

    srand(time(NULL));

    start_it ();

    g_main_loop_run (loop);

    g_object_unref (src);

    return 0;
}
