/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
#include <stdlib.h>
#include <libebook/e-book.h>

#include "client-test-utils.h"

static const gchar *photo_data =
"/9j / 4AAQSkZJRgABAQEARwBHAAD//gAXQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q / 9sAQwAIBgYHB\
gUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04Mjw\
uMzQy / 9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyM\
jIyMjIyMjIyMjIyMjIyMjIy / 8AAEQgAMgAyAwEiAAIRAQMRAf / EABsAAQACAwEBAAAAAAAAAAA\
AAAAHCAQFBgID / 8QAMBAAAgEDAQYEBQQDAAAAAAAAAQIDAAQRBQYSEyExQQdhcYEiI0JRkRQVM\
qFiguH / xAAaAQADAQEBAQAAAAAAAAAAAAAABAUCBgED / 8QAIxEAAgICAQQCAwAAAAAAAAAAAAE\
CAwQRQRITITEUYQUiUf / aAAwDAQACEQMRAD8An + sHUtWtNKjVrmQ7754cajLvjrgfbzPIdzWdV\
fds9pJb3XdQkMrcFZGj + HqY0bdVV9Tz / wBia + N9vbjvkaxMb5E9N6SJB1HxLEEjJaWsUjD6QzS\
MPXdGB7E1zV74t63HINy1s4F7CWCTn77wrA0TY86jY3N1qsUk6wxBxBDvYjLHkoUH4j3JP / a0V\
3s1CvF / QM9tKpw0THeU + TLkj8VLnmzT8y0n9FujBx5bioba / rZLWx3iPZ7RzLp95GtnqRGVTez\
HNjruH7 / 4n + 67iqpq7Qi3uYWMMsNynfnE6sM8 / Lr6VamFi0KMepUE1Sx7XZHbI + fjxos1H0z3S\
lKYEjzISI2I64OKqsyu8sck2QYrmPjBvpIYg598Vauoh8VtlY7JW2isoBwpPl6hGByZTyD + o6E\
+h7UtlVOcPHA/+PyI1Wal6Zp7vaC / 06wnTTLtEeUDiKwzu4H8vI9AM9Tiuctkng1Nnk1G5cOoY\
ifB4nI / jB7VjWuoT21qPmwXUCHKlphHKvqG5N6g0 / cLi / Rg88FhbkbxlaUSu3kqpnn6kDzqGqb\
NdPB0XyK4 / svZr9RVntL50GePdcKEDqzhVBx7sKtPpayppNosxzKIlDHzxUFeG2zo2n2kivWhK\
6PpHwwoTnfk65J7kZyT9z5VYADAwKuYtfRA5zPv7tnjgUpSmREV8bq1hvbWW1uY1khlUo6MMhg\
eor7UoAje18FtmLe9eeQT3EXPcglkJRPbv71EWu7Dajp2o3MGmlRCkjKQ30jPUe1WlrlNW0Rpt\
TleNB84DnjkD0P9VlxT4Nqck9pmn8JuFp2zo0cgCWFi2e7555 / NSHXLadso2m3sU0NxlV65HM+\
VdTW3rgwvsUpSvAFKUoAUxSlAClKUAKUpQB//2Q==";

static GMainLoop *loop = NULL;
static gchar     *micheal_jackson_uid = NULL;
static gchar     *james_brown_uid = NULL;

/* Decide what to do with every "view-completed" signal */
enum {
	ITERATION_SWAP_FACE = 0,
	ITERATION_DELETE_JAMES,
	ITERATION_UPDATE_MICHEAL,
	ITERATION_DELETE_MICHEAL,
	ITERATION_FINISH
};
static gint       iteration = ITERATION_SWAP_FACE;

static void
print_contact (EContact *contact)
{
	EContactPhoto *photo = e_contact_get (contact, E_CONTACT_PHOTO);

	g_assert (photo != NULL);
	g_assert (photo->type == E_CONTACT_PHOTO_TYPE_URI);
	g_print ("Test passed with photo uri: %s\n", photo->data.uri);
}

static void
objects_added (EBookView *book_view,
               const GSList *contacts)
{
	const GSList *l;

	for (l = (GSList *) contacts; l; l = l->next) {
		print_contact (l->data);
	}
}

static void
objects_modified (EBookView *book_view,
                  const GSList *contacts)
{
	GSList *l;

	for (l = (GSList *) contacts; l; l = l->next) {
		print_contact (l->data);
	}
}

static void
objects_removed (EBookClientView *book_view,
                 const GSList *ids)
{
	GSList *l;

	for (l = (GSList *) ids; l; l = l->next) {
		g_print ("Removed contact: %s\n", (gchar *)l->data);
	}
}

/* This provokes the backend to handle a cross-referenced photo
 * between contacts, how the backend handles this is it's choice,
 * we should test that when deleting one of the contacts, the other
 * contact does not lose it's photo on disk as a result.
 */
static void
give_james_brown_micheal_jacksons_face (EBookClient *book)
{
	EContact       *micheal = NULL, *james = NULL;
	EContactPhoto  *micheal_face;
	EContactPhoto  *james_face;
	GError         *error = NULL;

	if (!e_book_client_get_contact_sync (book, micheal_jackson_uid, &micheal, NULL, &error))
	  g_error ("Unable to get micheal jackson's contact information: %s", error->message);

	if (!e_book_client_get_contact_sync (book, james_brown_uid, &james, NULL, &error))
	  g_error ("Unable to get james brown's contact information: %s", error->message);

	g_assert (micheal);
	g_assert (james);

	micheal_face = e_contact_get (micheal, E_CONTACT_PHOTO);
	g_assert (micheal_face->type == E_CONTACT_PHOTO_TYPE_URI);

	james_face  = g_new (EContactPhoto, 1);
	james_face->type     = E_CONTACT_PHOTO_TYPE_URI;
	james_face->data.uri = g_strdup (micheal_face->data.uri);

	e_contact_set (james, E_CONTACT_PHOTO, james_face);

	g_print ("Giving james brown micheal jacksons face: %s\n", micheal_face->data.uri);

	if (!e_book_client_modify_contact_sync (book, james, NULL, &error))
		g_error ("Failed to modify contact with cross referenced photo: %s", error->message);
}

static void
update_contact_inline (EBookClient *book,
                       const gchar *uid)
{
	EContact *contact = NULL;
	EContactPhoto *photo;
	guchar *data;
	gsize length = 0;
	GError *error = NULL;

	if (!e_book_client_get_contact_sync (book, uid, &contact, NULL, &error))
	  g_error ("Unable to get contact: %s", error->message);

	g_assert (contact);

	data = g_base64_decode (photo_data, &length);

	photo = g_new (EContactPhoto, 1);
	photo->type = E_CONTACT_PHOTO_TYPE_INLINED;
	photo->data.inlined.mime_type = NULL;
	photo->data.inlined.data = data;
	photo->data.inlined.length = length;

	/* set the photo */
	e_contact_set (contact, E_CONTACT_PHOTO, photo);

	if (!e_book_client_modify_contact_sync (book, contact, NULL, &error))
	    g_error ("Failed to modify contact with inline photo data: %s", error->message);
}

/* This assertion is made a couple of times in the view-complete
 * handler, we run it to ensure that binary blobs and cross-referenced
 * photo uris exist on disk while they should */
static void
assert_uri_exists (EBookClient *book,
                   const gchar *uid)
{
	EContact      *contact;
	EContactPhoto *photo;
	const gchar   *filename;
	GError        *error = NULL;

	if (!e_book_client_get_contact_sync (book, uid, &contact, NULL, &error))
	  g_error ("Unable to get contact: %s", error->message);

	g_assert (contact);

	photo = e_contact_get (contact, E_CONTACT_PHOTO);
	g_assert (photo);
	g_assert (photo->type == E_CONTACT_PHOTO_TYPE_URI);

	filename = g_filename_from_uri (photo->data.uri, NULL, NULL);
	g_assert (filename);

	/* The file should absolutely exist at this point */
	g_assert (g_file_test (filename, G_FILE_TEST_EXISTS));
}

static void
complete (EBookClientView *view,
          const GError *error)
{
	EBookClient *book = e_book_client_view_get_client (view);
	GError *local_error = NULL;

	g_print ("View complete, iteration %d\n", iteration);

	/* We get another "complete" notification after removing or modifying a contact */
	switch (iteration++) {
	case ITERATION_SWAP_FACE:
		give_james_brown_micheal_jacksons_face (book);
		break;
	case ITERATION_DELETE_JAMES:
		assert_uri_exists (book, james_brown_uid);

		if (!e_book_client_remove_contact_by_uid_sync (book, james_brown_uid, NULL, &local_error))
			g_error ("Error removing contact: %s", local_error->message);

		g_free (james_brown_uid);
		james_brown_uid = NULL;
		break;
	case ITERATION_UPDATE_MICHEAL:
		assert_uri_exists (book, micheal_jackson_uid);

		update_contact_inline (book, micheal_jackson_uid);
		break;
	case ITERATION_DELETE_MICHEAL:
		assert_uri_exists (book, micheal_jackson_uid);

		if (!e_book_client_remove_contact_by_uid_sync (book, micheal_jackson_uid, NULL, &local_error))
			g_error ("Error removing contact: %s", local_error->message);

		g_free (micheal_jackson_uid);
		micheal_jackson_uid = NULL;
		break;
	case ITERATION_FINISH:
		e_book_client_view_stop (view, NULL);
		g_object_unref (view);
		g_main_loop_quit (loop);
		break;
	default:
		g_assert_not_reached ();
		break;
	}

}

static void
setup_and_start_view (EBookClientView *view)
{
	GError *error = NULL;

	g_signal_connect (view, "objects-added", G_CALLBACK (objects_added), NULL);
	g_signal_connect (view, "objects-removed", G_CALLBACK (objects_removed), NULL);
	g_signal_connect (view, "objects-modified", G_CALLBACK (objects_modified), NULL);
	g_signal_connect (view, "complete", G_CALLBACK (complete), NULL);

	e_book_client_view_set_fields_of_interest (view, NULL, &error);
	if (error)
		report_error ("set fields of interest", &error);

	e_book_client_view_start (view, &error);
	if (error)
		report_error ("start view", &error);
}

static void
add_contact_inline (EBookClient *book)
{
	EContact *contact;
	EContactPhoto *photo;
	GError *error = NULL;
	guchar *data;
	gsize length = 0;

	contact = e_contact_new ();

	data = g_base64_decode (photo_data, &length);

	photo = g_new (EContactPhoto, 1);
	photo->type = E_CONTACT_PHOTO_TYPE_INLINED;
	photo->data.inlined.mime_type = NULL;
	photo->data.inlined.data = data;
	photo->data.inlined.length = length;

	/* set the photo */
	e_contact_set (contact, E_CONTACT_PHOTO, photo);
	e_contact_set (contact, E_CONTACT_FULL_NAME, "Micheal Jackson");

	if (!add_contact_verify  (book, contact))
	  g_error ("Failed to add contact: %s", error->message);

	micheal_jackson_uid = e_contact_get (contact, E_CONTACT_UID);
}

static void
add_contact_uri (EBookClient *book)
{
	EContact *contact;
	EContactPhoto *photo;
	GError *error = NULL;

	contact = e_contact_new ();

	photo           = g_new (EContactPhoto, 1);
	photo->type     = E_CONTACT_PHOTO_TYPE_URI;
	photo->data.uri = g_strdup ("http://en.wikipedia.org/wiki/File:Jamesbrown4.jpg");

	/* set the photo */
	e_contact_set (contact, E_CONTACT_PHOTO, photo);
	e_contact_set (contact, E_CONTACT_FULL_NAME, "James Brown");

	if (!add_contact_verify  (book, contact))
	  g_error ("Failed to add contact: %s", error->message);

	james_brown_uid = e_contact_get (contact, E_CONTACT_UID);
}

static void
setup_book (EBookClient **book_out)
{
	EBookClient *book;
	GError      *error = NULL;

	book = new_temp_client (NULL);
	g_assert (book != NULL);

	if (!e_client_open_sync (E_CLIENT (book), FALSE, NULL, &error)) {
		g_error ("failed to open client: %s", error->message);
	}

	add_contact_inline (book);
	add_contact_uri (book);

	*book_out = book;
}

gint
main (gint argc,
      gchar **argv)
{
	EBookClient *book;
	EBookClientView *view;
	EBookQuery *query;
	GError     *error = NULL;
	gchar      *sexp;

	g_type_init ();

	setup_book (&book);

	query = e_book_query_any_field_contains ("");
	sexp = e_book_query_to_string (query);
	e_book_query_unref (query);
	if (!e_book_client_get_view_sync (book, sexp, &view, NULL, &error)) {
		report_error ("get book view sync", &error);
		g_free (sexp);
		g_object_unref (book);

		return 1;
	}

	g_free (sexp);

	setup_and_start_view (view);

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

	if (!e_client_remove_sync (E_CLIENT (book), NULL, &error)) {
		report_error ("client remove sync", &error);
		g_object_unref (book);

		return 1;
	}

	g_object_unref (book);

	return 0;
}
