/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

#include "ebook/e-book-async.h"

static GThread *worker_thread;
static GAsyncQueue *to_worker_queue;
static GAsyncQueue *from_worker_queue;

typedef struct _EBookMsg EBookMsg;

typedef void (*EBookMsgHandler)(EBookMsg* msg);
typedef void (*EBookMsgDtor)(EBookMsg* msg);

struct _EBookMsg {
	EBookMsgHandler handler;
	EBookMsgDtor dtor;
};

static gpointer
worker (gpointer data)
{
	while (TRUE) {
		EBookMsg *msg = g_async_queue_pop (to_worker_queue);
		msg->handler (msg);
		msg->dtor (msg);
	}

	return NULL;
}

static gboolean
main_thread_check_for_response (gpointer data)
{
	EBookMsg *msg;

	while ((msg = g_async_queue_try_pop (from_worker_queue)) != NULL) {
		msg->handler (msg);
		msg->dtor (msg);
	}
	
	return TRUE;
}

static void
e_book_msg_init (EBookMsg *msg, EBookMsgHandler handler, EBookMsgDtor dtor)
{
	msg->handler = handler;
	msg->dtor = dtor;
}

static void
init_async()
{
	static gboolean init_done = FALSE;
	if (!init_done) {
		init_done = TRUE;
		to_worker_queue = g_async_queue_new ();
		from_worker_queue = g_async_queue_new ();
		worker_thread = g_thread_create (worker, NULL, FALSE, NULL);
		g_timeout_add (300, main_thread_check_for_response, NULL);
	}
}



typedef struct {
	EBookMsg msg;

	EBook *book;
	char *uri;
	EBookCallback open_response;
	gpointer closure;
} LoadUriMsg;

typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookStatus status;
	EBookCallback open_response;
	gpointer closure;
} LoadUriResponse;

static void
_load_uri_response_handler (EBookMsg *msg)
{
	LoadUriResponse *resp = (LoadUriResponse*)msg;

	resp->open_response (resp->book, resp->status, resp->closure);
}

static void
_load_uri_response_dtor (EBookMsg *msg)
{
	LoadUriResponse *resp = (LoadUriResponse*)msg;

	g_object_unref (resp->book);
	g_free (resp);
}

static void
_load_uri_handler (EBookMsg *msg)
{
	LoadUriMsg *uri_msg = (LoadUriMsg *)msg;
	LoadUriResponse *response;
	GError *error = NULL;

	response = g_new (LoadUriResponse, 1);
	e_book_msg_init ((EBookMsg*)response, _load_uri_response_handler, _load_uri_response_dtor);

	response->status = E_BOOK_ERROR_OK;
	if (!e_book_load_uri (uri_msg->book, uri_msg->uri, FALSE, &error)) {
		response->status = error->code;
		g_error_free (error);
	}

	response->book = uri_msg->book;
	response->open_response = uri_msg->open_response;
	response->closure = uri_msg->closure;

	g_async_queue_push (from_worker_queue, response);
}

static void
_load_uri_dtor (EBookMsg *msg)
{
	LoadUriMsg *uri_msg = (LoadUriMsg *)msg;
	
	g_free (uri_msg->uri);
	g_free (uri_msg);
}

void
e_book_async_load_uri (EBook                 *book,
		       const char            *uri,
		       EBookCallback          open_response,
		       gpointer               closure)
{
	LoadUriMsg *msg;

	init_async ();

	msg = g_new (LoadUriMsg, 1);
	e_book_msg_init ((EBookMsg*)msg, _load_uri_handler, _load_uri_dtor);

	msg->book = g_object_ref (book);
	msg->uri = g_strdup (uri);
	msg->open_response = open_response;
	msg->closure = closure;

	g_async_queue_push (to_worker_queue, msg);
}


void
e_book_async_unload_uri (EBook                 *book)
{
	e_book_unload_uri (book, NULL);
}




typedef struct {
	EBookMsg msg;

	EBookCallback open_response;
	gpointer closure;
} DefaultBookMsg;

typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookStatus status;
	EBookCallback open_response;
	gpointer closure;
} DefaultBookResponse;

static void
_default_book_response_handler (EBookMsg *msg)
{
	LoadUriResponse *resp = (LoadUriResponse*)msg;

	resp->open_response (resp->book, resp->status, resp->closure);
}

static void
_default_book_response_dtor (EBookMsg *msg)
{
	LoadUriResponse *resp = (LoadUriResponse*)msg;

	g_object_unref (resp->book);
	g_free (resp);
}

static void
_default_book_handler (EBookMsg *msg)
{
	DefaultBookMsg *dfb_msg = (DefaultBookMsg *)msg;
	DefaultBookResponse *response;
	GError *error = NULL;

	response = g_new (DefaultBookResponse, 1);
	e_book_msg_init ((EBookMsg*)response, _default_book_response_handler, _default_book_response_dtor);

	response->status = E_BOOK_ERROR_OK;
	if (!e_book_get_default_addressbook (&response->book, &error)) {
		response->status = error->code;
		g_error_free (error);
	}

	response->open_response = dfb_msg->open_response;
	response->closure = dfb_msg->closure;

	g_async_queue_push (from_worker_queue, response);
}

static void
_default_book_dtor (EBookMsg *msg)
{
	DefaultBookMsg *dfb_msg = (DefaultBookMsg *)msg;
	
	g_free (dfb_msg);
}

void
e_book_async_get_default_addressbook (EBookCallback open_response,
				      gpointer      closure)
{
	DefaultBookMsg *msg;

	init_async ();

	msg = g_new (DefaultBookMsg, 1);
	e_book_msg_init ((EBookMsg*)msg, _default_book_handler, _default_book_dtor);

	msg->open_response = open_response;
	msg->closure = closure;

	g_async_queue_push (to_worker_queue, msg);
}



typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookFieldsCallback cb;
	gpointer closure;
} GetFieldsMsg;

typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookStatus status;
	GList *fields;
	EBookFieldsCallback cb;
	gpointer closure;
} GetFieldsResponse;

static void
_get_fields_response_handler (EBookMsg *msg)
{
	GetFieldsResponse *resp = (GetFieldsResponse*)msg;
	GList *l;
	EList *fields = e_list_new ((EListCopyFunc) g_strdup, 
				    (EListFreeFunc) g_free,
				    NULL);

	for (l = resp->fields; l; l = l->next)
		e_list_append (fields, l->data);

	if (resp->cb)
		resp->cb (resp->book, resp->status, fields, resp->closure);

	g_object_unref (fields);
}

static void
_get_fields_response_dtor (EBookMsg *msg)
{
	GetFieldsResponse *resp = (GetFieldsResponse*)msg;

	g_list_foreach (resp->fields, (GFunc)g_free, NULL);
	g_list_free (resp->fields);
	g_object_unref (resp->book);
	g_free (resp);
}

static void
_get_fields_handler (EBookMsg *msg)
{
	GetFieldsMsg *fields_msg = (GetFieldsMsg *)msg;
	GetFieldsResponse *response;
	GError *error = NULL;

	response = g_new (GetFieldsResponse, 1);
	e_book_msg_init ((EBookMsg*)response, _get_fields_response_handler, _get_fields_response_dtor);

	response->status = E_BOOK_ERROR_OK;
	if (!e_book_get_supported_fields (fields_msg->book, &response->fields, &error)) {
		response->status = error->code;
		g_error_free (error);
	}
	response->book = fields_msg->book;
	response->cb = fields_msg->cb;
	response->closure = fields_msg->closure;

	g_async_queue_push (from_worker_queue, response);
}

guint
e_book_async_get_supported_fields (EBook                 *book,
				   EBookFieldsCallback    cb,
				   gpointer               closure)
{
	GetFieldsMsg *msg;

	init_async ();

	msg = g_new (GetFieldsMsg, 1);
	e_book_msg_init ((EBookMsg*)msg, _get_fields_handler, (EBookMsgDtor)g_free);

	msg->book = g_object_ref (book);
	msg->cb = cb;
	msg->closure = closure;

	g_async_queue_push (to_worker_queue, msg);

	return 0;
}



typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookAuthMethodsCallback cb;
	gpointer closure;
} GetMethodsMsg;

typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookStatus status;
	GList *methods;
	EBookAuthMethodsCallback cb;
	gpointer closure;
} GetMethodsResponse;

static void
_get_methods_response_handler (EBookMsg *msg)
{
	GetMethodsResponse *resp = (GetMethodsResponse*)msg;
	GList *l;
	EList *methods = e_list_new ((EListCopyFunc) g_strdup, 
				    (EListFreeFunc) g_free,
				    NULL);

	for (l = resp->methods; l; l = l->next)
		e_list_append (methods, l->data);

	if (resp->cb)
		resp->cb (resp->book, resp->status, methods, resp->closure);

	g_object_unref (methods);
}

static void
_get_methods_response_dtor (EBookMsg *msg)
{
	GetMethodsResponse *resp = (GetMethodsResponse*)msg;

	g_list_foreach (resp->methods, (GFunc)g_free, NULL);
	g_list_free (resp->methods);
	g_object_unref (resp->book);
	g_free (resp);
}

static void
_get_methods_handler (EBookMsg *msg)
{
	GetMethodsMsg *methods_msg = (GetMethodsMsg *)msg;
	GetMethodsResponse *response;
	GError *error = NULL;

	response = g_new (GetMethodsResponse, 1);
	e_book_msg_init ((EBookMsg*)response, _get_methods_response_handler, _get_methods_response_dtor);

	response->status = E_BOOK_ERROR_OK;
	if (!e_book_get_supported_auth_methods (methods_msg->book, &response->methods, &error)) {
		response->status = error->code;
		g_error_free (error);
	}

	response->book = methods_msg->book;
	response->cb = methods_msg->cb;
	response->closure = methods_msg->closure;

	g_async_queue_push (from_worker_queue, response);
}

guint
e_book_async_get_supported_auth_methods (EBook                    *book,
					 EBookAuthMethodsCallback  cb,
					 gpointer                  closure)
{
	GetMethodsMsg *msg;

	init_async ();

	msg = g_new (GetMethodsMsg, 1);
	e_book_msg_init ((EBookMsg*)msg, _get_methods_handler, (EBookMsgDtor)g_free);

	msg->book = g_object_ref (book);
	msg->cb = cb;
	msg->closure = closure;

	g_async_queue_push (to_worker_queue, msg);

	return 0;
}



typedef struct {
	EBookMsg msg;

	EBook *book;
	char *user;
	char *passwd;
	char *auth_method;
	EBookCallback cb;
	gpointer closure;
} AuthUserMsg;

typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookStatus status;
	EBookCallback cb;
	gpointer closure;
} AuthUserResponse;

static void
_auth_user_response_handler (EBookMsg *msg)
{
	AuthUserResponse *resp = (AuthUserResponse*)msg;

	if (resp->cb)
		resp->cb (resp->book, resp->status, resp->closure);
}

static void
_auth_user_response_dtor (EBookMsg *msg)
{
	AuthUserResponse *resp = (AuthUserResponse*)msg;

	g_object_unref (resp->book);
	g_free (resp);
}

static void
_auth_user_handler (EBookMsg *msg)
{
	AuthUserMsg *auth_msg = (AuthUserMsg *)msg;
	AuthUserResponse *response;
	GError *error = NULL;

	response = g_new (AuthUserResponse, 1);
	e_book_msg_init ((EBookMsg*)response, _auth_user_response_handler, _auth_user_response_dtor);

	response->status = E_BOOK_ERROR_OK;
	if (!e_book_authenticate_user (auth_msg->book, auth_msg->user, auth_msg->passwd, auth_msg->auth_method, &error)) {
		response->status = error->code;
		g_error_free (error);
	}
	response->book = auth_msg->book;
	response->cb = auth_msg->cb;
	response->closure = auth_msg->closure;

	g_async_queue_push (from_worker_queue, response);
}

static void
_auth_user_dtor (EBookMsg *msg)
{
	AuthUserMsg *auth_msg = (AuthUserMsg *)msg;

	g_free (auth_msg->user);
	g_free (auth_msg->passwd);
	g_free (auth_msg->auth_method);

	g_free (auth_msg);
}

/* User authentication. */
void
e_book_async_authenticate_user (EBook                 *book,
				const char            *user,
				const char            *passwd,
				const char            *auth_method,
				EBookCallback         cb,
				gpointer              closure)
{
	AuthUserMsg *msg;

	init_async ();

	msg = g_new (AuthUserMsg, 1);
	e_book_msg_init ((EBookMsg*)msg, _auth_user_handler, _auth_user_dtor);

	msg->book = g_object_ref (book);
	msg->user = g_strdup (user);
	msg->passwd = g_strdup (passwd);
	msg->auth_method = g_strdup (auth_method);
	msg->cb = cb;
	msg->closure = closure;

	g_async_queue_push (to_worker_queue, msg);
}



typedef struct {
	EBookMsg msg;

	EBook *book;
	char *id;
	EBookContactCallback cb;
	gpointer closure;
} GetContactMsg;

typedef struct {
	EBookMsg msg;

	EBook *book;
	EContact *contact;
	EBookStatus status;
	EBookContactCallback cb;
	gpointer closure;
} GetContactResponse;

static void
_get_contact_response_handler (EBookMsg *msg)
{
	GetContactResponse *resp = (GetContactResponse*)msg;

	if (resp->cb)
		resp->cb (resp->book, resp->status, resp->contact, resp->closure);
}

static void
_get_contact_response_dtor (EBookMsg *msg)
{
	GetContactResponse *resp = (GetContactResponse*)msg;

	g_object_unref (resp->contact);
	g_object_unref (resp->book);
	g_free (resp);
}

static void
_get_contact_handler (EBookMsg *msg)
{
	GetContactMsg *get_contact_msg = (GetContactMsg *)msg;
	GetContactResponse *response;
	GError *error = NULL;

	response = g_new (GetContactResponse, 1);
	e_book_msg_init ((EBookMsg*)response, _get_contact_response_handler, _get_contact_response_dtor);

	response->status = E_BOOK_ERROR_OK;
	if (!e_book_get_contact (get_contact_msg->book, get_contact_msg->id, &response->contact, &error)) {
		response->status = error->code;
		g_error_free (error);
	}
	response->book = get_contact_msg->book;
	response->cb = get_contact_msg->cb;
	response->closure = get_contact_msg->closure;

	g_async_queue_push (from_worker_queue, response);
}

static void
_get_contact_dtor (EBookMsg *msg)
{
	GetContactMsg *get_contact_msg = (GetContactMsg *)msg;

	g_free (get_contact_msg->id);
	g_free (get_contact_msg);
}

/* Fetching contacts. */
guint
e_book_async_get_contact (EBook                 *book,
			  const char            *id,
			  EBookContactCallback   cb,
			  gpointer               closure)
{
	GetContactMsg *msg;

	init_async ();

	msg = g_new (GetContactMsg, 1);
	e_book_msg_init ((EBookMsg*)msg, _get_contact_handler, _get_contact_dtor);

	msg->book = g_object_ref (book);
	msg->id = g_strdup (id);
	msg->cb = cb;
	msg->closure = closure;

	g_async_queue_push (to_worker_queue, msg);

	return 0;
}



/* Deleting cards. */
gboolean
e_book_async_remove_contact (EBook                 *book,
			     EContact              *contact,
			     EBookCallback          cb,
			     gpointer               closure)
{
	const char *id = e_contact_get_const (contact, E_CONTACT_UID);

	return e_book_async_remove_contact_by_id (book, id, cb, closure);
}

gboolean
e_book_async_remove_contact_by_id (EBook                 *book,
				const char            *id,
				EBookCallback          cb,
				gpointer               closure)
{
	GList *list = g_list_append (NULL, g_strdup (id));

	return e_book_async_remove_contacts (book, list, cb, closure);
}


typedef struct {
	EBookMsg msg;

	EBook *book;
	GList *id_list;
	EBookCallback cb;
	gpointer closure;
} RemoveContactsMsg;

typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookStatus status;
	EBookCallback cb;
	gpointer closure;
} RemoveContactsResponse;

static void
_remove_contacts_response_handler (EBookMsg *msg)
{
	RemoveContactsResponse *resp = (RemoveContactsResponse*)msg;

	if (resp->cb)
		resp->cb (resp->book, resp->status, resp->closure);
}

static void
_remove_contacts_response_dtor (EBookMsg *msg)
{
	RemoveContactsResponse *resp = (RemoveContactsResponse*)msg;

	g_object_unref (resp->book);
	g_free (resp);
}

static void
_remove_contacts_handler (EBookMsg *msg)
{
	RemoveContactsMsg *remove_contacts_msg = (RemoveContactsMsg *)msg;
	RemoveContactsResponse *response;
	GError *error = NULL;

	response = g_new (RemoveContactsResponse, 1);
	e_book_msg_init ((EBookMsg*)response, _remove_contacts_response_handler, _remove_contacts_response_dtor);

	response->status = E_BOOK_ERROR_OK;
	if (!e_book_remove_contacts (remove_contacts_msg->book, remove_contacts_msg->id_list, &error)) {
		response->status = error->code;
		g_error_free (error);
	}
	response->book = remove_contacts_msg->book;
	response->cb = remove_contacts_msg->cb;
	response->closure = remove_contacts_msg->closure;

	g_async_queue_push (from_worker_queue, response);
}

static void
_remove_contacts_dtor (EBookMsg *msg)
{
	RemoveContactsMsg *remove_contacts_msg = (RemoveContactsMsg *)msg;

	g_list_foreach (remove_contacts_msg->id_list, (GFunc)g_free, NULL);
	g_list_free (remove_contacts_msg->id_list);
	g_free (remove_contacts_msg);
}

gboolean
e_book_async_remove_contacts (EBook                 *book,
			   GList                 *id_list,
			   EBookCallback          cb,
			   gpointer               closure)
{
	RemoveContactsMsg *msg;
	GList *l;
	init_async ();

	msg = g_new (RemoveContactsMsg, 1);
	e_book_msg_init ((EBookMsg*)msg, _remove_contacts_handler, _remove_contacts_dtor);

	msg->book = g_object_ref (book);
	msg->id_list = g_list_copy (id_list);
	for (l = msg->id_list; l; l = l->next)
		l->data = g_strdup (l->data);
	msg->cb = cb;
	msg->closure = closure;

	g_async_queue_push (to_worker_queue, msg);

	return 0;
}



/* Adding contacts. */
typedef struct {
	EBookMsg msg;

	EBook *book;
	EContact *contact;
	EBookIdCallback cb;
	gpointer closure;
} AddContactMsg;

typedef struct {
	EBookMsg msg;

	EBook *book;
	char *id;
	EBookStatus status;
	EBookIdCallback cb;
	gpointer closure;
} AddContactResponse;

static void
_add_contact_response_handler (EBookMsg *msg)
{
	AddContactResponse *resp = (AddContactResponse*)msg;

	if (resp->cb)
		resp->cb (resp->book, resp->status, resp->id, resp->closure);
}

static void
_add_contact_response_dtor (EBookMsg *msg)
{
	AddContactResponse *resp = (AddContactResponse*)msg;

	g_object_unref (resp->book);
	g_free (resp->id);
	g_free (resp);
}

static void
_add_contact_handler (EBookMsg *msg)
{
	AddContactMsg *add_contact_msg = (AddContactMsg *)msg;
	AddContactResponse *response;
	GError *error = NULL;

	response = g_new (AddContactResponse, 1);
	e_book_msg_init ((EBookMsg*)response, _add_contact_response_handler, _add_contact_response_dtor);

	response->status = E_BOOK_ERROR_OK;
	if (!e_book_add_contact (add_contact_msg->book, add_contact_msg->contact, &error)) {
		response->status = error->code;
		response->id = NULL;
		g_error_free (error);
	}
	else {
		response->id = e_contact_get (add_contact_msg->contact, E_CONTACT_UID);
	}
	response->book = add_contact_msg->book;
	response->cb = add_contact_msg->cb;
	response->closure = add_contact_msg->closure;

	g_async_queue_push (from_worker_queue, response);
}

static void
_add_contact_dtor (EBookMsg *msg)
{
	AddContactMsg *add_contact_msg = (AddContactMsg *)msg;

	g_object_unref (add_contact_msg->contact);
	g_free (add_contact_msg);
}

gboolean
e_book_async_add_contact (EBook                 *book,
			  EContact              *contact,
			  EBookIdCallback        cb,
			  gpointer               closure)
{
	AddContactMsg *msg;

	init_async ();

	msg = g_new (AddContactMsg, 1);
	e_book_msg_init ((EBookMsg*)msg, _add_contact_handler, _add_contact_dtor);

	msg->book = g_object_ref (book);
	msg->contact = g_object_ref (contact); /* XXX maybe we should _duplicate it here instead */
	msg->cb = cb;
	msg->closure = closure;

	g_async_queue_push (to_worker_queue, msg);

	return TRUE;
}



/* Modifying cards. */
typedef struct {
	EBookMsg msg;

	EBook *book;
	EContact *contact;
	EBookCallback cb;
	gpointer closure;
} CommitContactMsg;

typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookStatus status;
	EBookCallback cb;
	gpointer closure;
} CommitContactResponse;

static void
_commit_contact_response_handler (EBookMsg *msg)
{
	CommitContactResponse *resp = (CommitContactResponse*)msg;

	if (resp->cb)
		resp->cb (resp->book, resp->status, resp->closure);
}

static void
_commit_contact_response_dtor (EBookMsg *msg)
{
	CommitContactResponse *resp = (CommitContactResponse*)msg;

	g_object_unref (resp->book);
	g_free (resp);
}

static void
_commit_contact_handler (EBookMsg *msg)
{
	CommitContactMsg *commit_contact_msg = (CommitContactMsg *)msg;
	CommitContactResponse *response;
	GError *error = NULL;

	response = g_new (CommitContactResponse, 1);
	e_book_msg_init ((EBookMsg*)response, _commit_contact_response_handler, _commit_contact_response_dtor);

	response->status = E_BOOK_ERROR_OK;
	if (!e_book_commit_contact (commit_contact_msg->book, commit_contact_msg->contact, &error)) {
		response->status = error->code;
		g_error_free (error);
	}
	response->book = commit_contact_msg->book;
	response->cb = commit_contact_msg->cb;
	response->closure = commit_contact_msg->closure;

	g_async_queue_push (from_worker_queue, response);
}

static void
_commit_contact_dtor (EBookMsg *msg)
{
	CommitContactMsg *commit_contact_msg = (CommitContactMsg *)msg;

	g_object_unref (commit_contact_msg->contact);
	g_free (commit_contact_msg);
}


gboolean
e_book_async_commit_contact (EBook                 *book,
			     EContact              *contact,
			     EBookCallback          cb,
			     gpointer               closure)
{
	CommitContactMsg *msg;

	init_async ();

	msg = g_new (CommitContactMsg, 1);
	e_book_msg_init ((EBookMsg*)msg, _commit_contact_handler, _commit_contact_dtor);

	msg->book = g_object_ref (book);
	msg->contact = g_object_ref (contact); /* XXX maybe we should _duplicate it here instead */
	msg->cb = cb;
	msg->closure = closure;

	g_async_queue_push (to_worker_queue, msg);

	return TRUE;
}



typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookQuery *query;
	EBookBookViewCallback cb;
	gpointer closure;
} GetBookViewMsg;

typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookStatus status;
	EBookView *book_view;
	EBookBookViewCallback cb;
	gpointer closure;
} GetBookViewResponse;

static void
_get_book_view_response_handler (EBookMsg *msg)
{
	GetBookViewResponse *resp = (GetBookViewResponse*)msg;

	if (resp->cb)
		resp->cb (resp->book, resp->status, resp->book_view, resp->closure);
}

static void
_get_book_view_response_dtor (EBookMsg *msg)
{
	GetBookViewResponse *resp = (GetBookViewResponse*)msg;

	g_object_unref (resp->book);
	g_object_unref (resp->book_view);
	g_free (resp);
}

static void
_get_book_view_handler (EBookMsg *msg)
{
	GetBookViewMsg *view_msg = (GetBookViewMsg *)msg;
	GetBookViewResponse *response;
	GError *error = NULL;

	response = g_new (GetBookViewResponse, 1);
	e_book_msg_init ((EBookMsg*)response, _get_book_view_response_handler, _get_book_view_response_dtor);

	response->status = E_BOOK_ERROR_OK;
	if (!e_book_get_book_view (view_msg->book, view_msg->query, NULL, -1, &response->book_view, &error)) {
		response->status = error->code;
		g_error_free (error);
	}
	response->book = view_msg->book;
	response->cb = view_msg->cb;
	response->closure = view_msg->closure;

	g_async_queue_push (from_worker_queue, response);
}

static void
_get_book_view_dtor (EBookMsg *msg)
{
	GetBookViewMsg *view_msg = (GetBookViewMsg *)msg;
	
	e_book_query_unref (view_msg->query);
	g_free (view_msg);
}

guint
e_book_async_get_book_view (EBook                 *book,
			    const gchar           *query,
			    EBookBookViewCallback  cb,
			    gpointer               closure)
{
	GetBookViewMsg *msg;

	init_async ();

	msg = g_new (GetBookViewMsg, 1);
	e_book_msg_init ((EBookMsg*)msg, _get_book_view_handler, _get_book_view_dtor);

	msg->book = g_object_ref (book);
	msg->query = e_book_query_from_string (query);
	msg->cb = cb;
	msg->closure = closure;

	g_async_queue_push (to_worker_queue, msg);

	return 0;
}



typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookQuery *query;
	EBookContactsCallback cb;
	gpointer closure;
} GetContactsMsg;

typedef struct {
	EBookMsg msg;

	EBook *book;
	EBookStatus status;
	GList *contacts;
	EBookContactsCallback cb;
	gpointer closure;
} GetContactsResponse;

static void
_get_contacts_response_handler (EBookMsg *msg)
{
	GetContactsResponse *resp = (GetContactsResponse*)msg;

	if (resp->cb)
		resp->cb (resp->book, resp->status, resp->contacts, resp->closure);
}

static void
_get_contacts_response_dtor (EBookMsg *msg)
{
	GetContactsResponse *resp = (GetContactsResponse*)msg;

	g_object_unref (resp->book);
	g_free (resp);
}

static void
_get_contacts_handler (EBookMsg *msg)
{
	GetContactsMsg *view_msg = (GetContactsMsg *)msg;
	GetContactsResponse *response;
	GError *error = NULL;

	response = g_new (GetContactsResponse, 1);
	e_book_msg_init ((EBookMsg*)response, _get_contacts_response_handler, _get_contacts_response_dtor);

	response->status = E_BOOK_ERROR_OK;
	if (!e_book_get_contacts (view_msg->book, view_msg->query, &response->contacts, &error)) {
		response->status = error->code;
		g_error_free (error);
	}
	response->book = view_msg->book;
	response->cb = view_msg->cb;
	response->closure = view_msg->closure;

	g_async_queue_push (from_worker_queue, response);
}

static void
_get_contacts_dtor (EBookMsg *msg)
{
	GetContactsMsg *view_msg = (GetContactsMsg *)msg;
	
	e_book_query_unref (view_msg->query);
	g_free (view_msg);
}

guint
e_book_async_get_contacts (EBook                 *book,
			   const gchar           *query,
			   EBookContactsCallback  cb,
			   gpointer              closure)
{
	GetContactsMsg *msg;

	init_async ();

	msg = g_new (GetContactsMsg, 1);
	e_book_msg_init ((EBookMsg*)msg, _get_contacts_handler, _get_contacts_dtor);

	msg->book = g_object_ref (book);
	msg->query = e_book_query_from_string (query);
	msg->cb = cb;
	msg->closure = closure;

	g_async_queue_push (to_worker_queue, msg);

	return 0;
}
