/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * pas-book.c
 *
 * Copyright 2000, Ximian, Inc.
 */

#include <config.h>
#include <bonobo/bonobo-main.h>
#include <bonobo/bonobo-arg.h>
#include "e-util/e-list.h"
#include "ebook/e-contact.h"
#include "pas-book-view.h"
#include "pas-backend.h"
#include "pas-backend-card-sexp.h"
#include "pas-marshal.h"

static BonoboObjectClass *pas_book_parent_class;
POA_GNOME_Evolution_Addressbook_Book__vepv pas_book_vepv;

struct _PASBookPrivate {
	PASBackend                               *backend;
	GNOME_Evolution_Addressbook_BookListener  listener;
	char                                     *uri;
};

static void
impl_GNOME_Evolution_Addressbook_Book_open (PortableServer_Servant servant,
					    const CORBA_boolean only_if_exists,
					    CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));

	printf ("impl_GNOME_Evolution_Addressbook_Book_open\n");

	pas_backend_open (pas_book_get_backend (book), book, only_if_exists);
}

static void
impl_GNOME_Evolution_Addressbook_Book_remove (PortableServer_Servant servant,
					      CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));

	printf ("impl_GNOME_Evolution_Addressbook_Book_remove\n");

	pas_backend_remove (pas_book_get_backend (book), book);
}

static void
impl_GNOME_Evolution_Addressbook_Book_getContact (PortableServer_Servant servant,
						  const CORBA_char *id,
						  CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));

	printf ("impl_GNOME_Evolution_Addressbook_Book_getContact\n");

	pas_backend_get_contact (pas_book_get_backend (book), book, id);
}

static void
impl_GNOME_Evolution_Addressbook_Book_getContactList (PortableServer_Servant servant,
						      const CORBA_char *query,
						      CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));

	printf ("impl_GNOME_Evolution_Addressbook_Book_getContactList\n");

	pas_backend_get_contact_list (pas_book_get_backend (book), book, query);
}

static void
impl_GNOME_Evolution_Addressbook_Book_authenticateUser (PortableServer_Servant servant,
							const char* user,
							const char* passwd,
							const char* auth_method,
							CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));

	pas_backend_authenticate_user (pas_book_get_backend (book), book,
				       user, passwd, auth_method);
}

static void
impl_GNOME_Evolution_Addressbook_Book_addContact (PortableServer_Servant servant,
						  const CORBA_char *vcard,
						  CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));

	pas_backend_create_contact (pas_book_get_backend (book), book, vcard);
}

static void
impl_GNOME_Evolution_Addressbook_Book_removeContacts (PortableServer_Servant servant,
						      const GNOME_Evolution_Addressbook_ContactIdList *ids,
						      CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));
	int i;
	GList *id_list = NULL;
	
	for (i = 0; i < ids->_length; i ++)
		id_list = g_list_append (id_list, ids->_buffer[i]);

	pas_backend_remove_contacts (pas_book_get_backend (book), book, id_list);

	g_list_free (id_list);
}

static void
impl_GNOME_Evolution_Addressbook_Book_modifyContact (PortableServer_Servant servant,
						     const CORBA_char *vcard,
						     CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));

	pas_backend_modify_contact (pas_book_get_backend (book), book, vcard);
}

static void
view_listener_died_cb (gpointer cnx, gpointer user_data)
{
	PASBookView *book_view = PAS_BOOK_VIEW (user_data);

	pas_backend_stop_book_view (pas_book_view_get_backend (book_view), book_view);
	pas_backend_remove_book_view (pas_book_view_get_backend (book_view), book_view);
}

static void
impl_GNOME_Evolution_Addressbook_Book_getBookView (PortableServer_Servant servant,
				   const GNOME_Evolution_Addressbook_BookViewListener listener,
				   const CORBA_char *search,
				   const GNOME_Evolution_Addressbook_stringlist* requested_fields,
				   const CORBA_long max_results,
				   CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));
	PASBackend *backend = pas_book_get_backend (book);
	PASBackendCardSExp *card_sexp;
	PASBookView *view;

	g_warning ("impl_GNOME_Evolution_Addressbook_Book_getBookView (%s)\n", search);

	/* we handle this entirely here, since it doesn't require any
	   backend involvement now that we have pas_book_view_start to
	   actually kick off the search. */

	card_sexp = pas_backend_card_sexp_new (search);
	if (!card_sexp) {
		pas_book_respond_get_book_view (book, GNOME_Evolution_Addressbook_InvalidQuery, NULL);
		return;
	}

	view = pas_book_view_new (backend, listener, search, card_sexp);

	if (!view) {
		g_object_unref (card_sexp);
		pas_book_respond_get_book_view (book, GNOME_Evolution_Addressbook_OtherError, NULL);
		return;
	}

	ORBit_small_listen_for_broken (pas_book_view_get_listener (view), G_CALLBACK (view_listener_died_cb), view);

	pas_backend_add_book_view (backend, view);

	pas_book_respond_get_book_view (book, GNOME_Evolution_Addressbook_Success, view);

	g_object_unref (view);
}


static void
impl_GNOME_Evolution_Addressbook_Book_getChanges (PortableServer_Servant servant,
				 const CORBA_char *change_id,
				 CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));

	pas_backend_get_changes (pas_book_get_backend (book), book, change_id);
}

static char *
impl_GNOME_Evolution_Addressbook_Book_getStaticCapabilities (PortableServer_Servant servant,
					     CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));
	char *temp;
	char *ret_val;

	temp = pas_backend_get_static_capabilities (book->priv->backend);
	ret_val = CORBA_string_dup(temp);
	g_free(temp);
	return ret_val;
}

static void
impl_GNOME_Evolution_Addressbook_Book_getSupportedFields (PortableServer_Servant servant,
							  CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));

	pas_backend_get_supported_fields (pas_book_get_backend (book), book);
}

static void
impl_GNOME_Evolution_Addressbook_Book_getSupportedAuthMethods (PortableServer_Servant servant,
							       CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));

	pas_backend_get_supported_auth_methods (pas_book_get_backend (book), book);
}

static GNOME_Evolution_Addressbook_CallStatus
impl_GNOME_Evolution_Addressbook_Book_cancelOperation (PortableServer_Servant servant,
						       CORBA_Environment *ev)
{
	PASBook *book = PAS_BOOK (bonobo_object (servant));

	return pas_backend_cancel_operation (pas_book_get_backend (book), book);
}

/**
 * pas_book_get_backend:
 */
PASBackend *
pas_book_get_backend (PASBook *book)
{
	g_return_val_if_fail (book && PAS_IS_BOOK (book), NULL);

	return book->priv->backend;
}

GNOME_Evolution_Addressbook_BookListener
pas_book_get_listener (PASBook *book)
{
	g_return_val_if_fail (book && PAS_IS_BOOK (book), CORBA_OBJECT_NIL);

	return book->priv->listener;
}

const char*
pas_book_get_uri (PASBook *book)
{
	return book->priv->uri;
}

/**
 * pas_book_respond_open:
 */
void
pas_book_respond_open (PASBook                           *book,
		       GNOME_Evolution_Addressbook_CallStatus  status)
{
	CORBA_Environment ev;

	CORBA_exception_init (&ev);
	GNOME_Evolution_Addressbook_BookListener_notifyBookOpened (book->priv->listener, status, &ev);

	if (ev._major != CORBA_NO_EXCEPTION) {
		g_warning ("pas_book_respond_open: Exception "
			   "responding to BookListener!\n");
	}

	CORBA_exception_free (&ev);
}

/**
 * pas_book_respond_remove:
 */
void
pas_book_respond_remove (PASBook                           *book,
			 GNOME_Evolution_Addressbook_CallStatus  status)
{
	CORBA_Environment ev;

	CORBA_exception_init (&ev);
	GNOME_Evolution_Addressbook_BookListener_notifyBookRemoved (book->priv->listener, status, &ev);

	if (ev._major != CORBA_NO_EXCEPTION) {
		g_warning ("pas_book_respond_remove: Exception "
			   "responding to BookListener!\n");
	}

	CORBA_exception_free (&ev);
}

/**
 * pas_book_respond_create:
 */
void
pas_book_respond_create (PASBook                                *book,
			 GNOME_Evolution_Addressbook_CallStatus  status,
			 EContact                               *contact)
{
	CORBA_Environment ev;

	if (status == GNOME_Evolution_Addressbook_Success) {
		pas_backend_notify_update (book->priv->backend, contact);
		pas_backend_notify_complete (book->priv->backend);
	}

	CORBA_exception_init (&ev);

	GNOME_Evolution_Addressbook_BookListener_notifyContactCreated (
		book->priv->listener, status,
		e_contact_get_const (contact, E_CONTACT_UID), &ev);

	if (ev._major != CORBA_NO_EXCEPTION) {
		g_warning ("pas_book_respond_create: Exception "
			   "responding to BookListener!\n");
	}

	CORBA_exception_free (&ev);
}

/**
 * pas_book_respond_remove_contacts:
 */
void
pas_book_respond_remove_contacts (PASBook                                *book,
				  GNOME_Evolution_Addressbook_CallStatus  status,
				  GList                                  *ids)
{
	CORBA_Environment ev;
	GList *i;

	CORBA_exception_init (&ev);

	if (ids) {
		for (i = ids; i; i = i->next)
			pas_backend_notify_remove (book->priv->backend, i->data);
		pas_backend_notify_complete (book->priv->backend);
	}

	GNOME_Evolution_Addressbook_BookListener_notifyContactsRemoved (
		book->priv->listener, status, &ev);

	if (ev._major != CORBA_NO_EXCEPTION) {
		g_warning ("pas_book_respond_remove: Exception "
			   "responding to BookListener!\n");
	}

	CORBA_exception_free (&ev);
}

/**
 * pas_book_respond_modify:
 */
void
pas_book_respond_modify (PASBook                                *book,
			 GNOME_Evolution_Addressbook_CallStatus  status,
			 EContact                               *contact)
{
	CORBA_Environment ev;

	if (status == GNOME_Evolution_Addressbook_Success) {
		pas_backend_notify_update (book->priv->backend, contact);
		pas_backend_notify_complete (book->priv->backend);
	}

	CORBA_exception_init (&ev);

	GNOME_Evolution_Addressbook_BookListener_notifyContactModified (
		book->priv->listener, status, &ev);

	if (ev._major != CORBA_NO_EXCEPTION) {
		g_warning ("pas_book_respond_modify: Exception "
			   "responding to BookListener!\n");
	}

	CORBA_exception_free (&ev);
}

/**
 * pas_book_respond_authenticate_user:
 */
void
pas_book_respond_authenticate_user (PASBook                           *book,
				    GNOME_Evolution_Addressbook_CallStatus  status)
{
	CORBA_Environment ev;

	CORBA_exception_init (&ev);

	GNOME_Evolution_Addressbook_BookListener_notifyAuthenticationResult (
		book->priv->listener, status, &ev);

	if (ev._major != CORBA_NO_EXCEPTION) {
		g_warning ("pas_book_respond_authenticate_user: Exception "
			   "responding to BookListener!\n");
	}

	CORBA_exception_free (&ev);
}

void
pas_book_respond_get_supported_fields (PASBook *book,
				       GNOME_Evolution_Addressbook_CallStatus  status,
				       GList   *fields)
{
	CORBA_Environment ev;
	GNOME_Evolution_Addressbook_stringlist stringlist;
	int num_fields;
	int i;
	GList *iter;

	CORBA_exception_init (&ev);

	num_fields = g_list_length (fields);

	stringlist._buffer = CORBA_sequence_CORBA_string_allocbuf (num_fields);
	stringlist._maximum = num_fields;
	stringlist._length = num_fields;

	for (i = 0, iter = fields; iter; iter = iter->next, i ++) {
		stringlist._buffer[i] = CORBA_string_dup ((char*)iter->data);
	}

	printf ("calling GNOME_Evolution_Addressbook_BookListener_notifySupportedFields\n");

	GNOME_Evolution_Addressbook_BookListener_notifySupportedFields (
			book->priv->listener, status,
			&stringlist,
			&ev);

	CORBA_exception_free (&ev);

	CORBA_free(stringlist._buffer);
}

void
pas_book_respond_get_supported_auth_methods (PASBook *book,
					     GNOME_Evolution_Addressbook_CallStatus  status,
					     GList   *auth_methods)
{
	CORBA_Environment ev;
	GNOME_Evolution_Addressbook_stringlist stringlist;
	int num_auth_methods;
	GList *iter;
	int i;

	CORBA_exception_init (&ev);

	num_auth_methods = g_list_length (auth_methods);

	stringlist._buffer = CORBA_sequence_CORBA_string_allocbuf (num_auth_methods);
	stringlist._maximum = num_auth_methods;
	stringlist._length = num_auth_methods;

	for (i = 0, iter = auth_methods; iter; iter = iter->next, i ++) {
		stringlist._buffer[i] = CORBA_string_dup ((char*)iter->data);
	}

	GNOME_Evolution_Addressbook_BookListener_notifySupportedAuthMethods (
			book->priv->listener, status,
			&stringlist,
			&ev);

	CORBA_exception_free (&ev);

	CORBA_free(stringlist._buffer);
}

static void
view_destroy(gpointer data, GObject *where_object_was)
{
	PASBook           *book = (PASBook *)data;
	EIterator         *iterator;
	gboolean success = FALSE;
	EList *views = pas_backend_get_book_views (book->priv->backend);

	for (iterator = e_list_get_iterator(views);
	     e_iterator_is_valid(iterator);
	     e_iterator_next(iterator)) {
		const PASBookView *view = e_iterator_get(iterator);
		if (view == (PASBookView*)where_object_was) {
			e_iterator_delete(iterator);
			success = TRUE;
			break;
		}
	}
	if (!success)
		g_warning ("Failed to remove from book_views list");
	g_object_unref(iterator);
	g_object_unref(views);
}

/**
 * pas_book_respond_get_book_view:
 */
void
pas_book_respond_get_book_view (PASBook                           *book,
				GNOME_Evolution_Addressbook_CallStatus  status,
				PASBookView                       *book_view)
{
	CORBA_Environment ev;
	CORBA_Object      object = CORBA_OBJECT_NIL;

	printf ("pas_book_respond_get_book_view\n");

	CORBA_exception_init (&ev);

	if (book_view) {
		object = bonobo_object_corba_objref(BONOBO_OBJECT(book_view));

		g_object_weak_ref (G_OBJECT (book_view), view_destroy, book);
	}

	GNOME_Evolution_Addressbook_BookListener_notifyViewRequested (
		book->priv->listener, status, object, &ev);

	if (ev._major != CORBA_NO_EXCEPTION) {
		g_warning ("pas_book_respond_get_book_view: Exception "
			   "responding to BookListener!\n");
	}

	CORBA_exception_free (&ev);
}

/**
 * pas_book_respond_get_contact:
 */
void
pas_book_respond_get_contact (PASBook                           *book,
			      GNOME_Evolution_Addressbook_CallStatus  status,
			      char                              *vcard)
{
	CORBA_Environment ev;

	CORBA_exception_init (&ev);

	GNOME_Evolution_Addressbook_BookListener_notifyContactRequested (book->priv->listener,
									 status,
									 vcard,
									 &ev);

	if (ev._major != CORBA_NO_EXCEPTION)
		g_message ("could not notify listener of get-contact response");

	CORBA_exception_free (&ev);
}

/**
 * pas_book_respond_get_contact_list:
 */
void
pas_book_respond_get_contact_list (PASBook                           *book,
				   GNOME_Evolution_Addressbook_CallStatus  status,
				   GList                             *card_list)
{
	CORBA_Environment ev;
	GNOME_Evolution_Addressbook_stringlist stringlist;
	int num_cards;
	int i;
	GList *l;

	CORBA_exception_init (&ev);

	num_cards = g_list_length (card_list);

	stringlist._buffer = CORBA_sequence_CORBA_string_allocbuf (num_cards);
	stringlist._maximum = num_cards;
	stringlist._length = num_cards;

	for (i = 0, l = card_list; l; l = l->next, i ++)
		stringlist._buffer[i] = CORBA_string_dup (l->data);

	g_list_foreach (card_list, (GFunc)g_free, NULL);
	g_list_free (card_list);


	GNOME_Evolution_Addressbook_BookListener_notifyContactListRequested (book->priv->listener,
									     status,
									     &stringlist,
									     &ev);

	if (ev._major != CORBA_NO_EXCEPTION)
		g_message ("could not notify listener of get-contact-list response");

	CORBA_exception_free (&ev);

	CORBA_free(stringlist._buffer);
}

/**
 * pas_book_respond_get_changes:
 */
void
pas_book_respond_get_changes (PASBook                                *book,
			      GNOME_Evolution_Addressbook_CallStatus  status,
			      GList                                  *changes)
{
	CORBA_Environment ev;
	GNOME_Evolution_Addressbook_BookChangeList changelist;
	int num_changes;
	int i;
	GList *l;

	CORBA_exception_init (&ev);

	num_changes = g_list_length (changes);

	changelist._buffer = CORBA_sequence_GNOME_Evolution_Addressbook_BookChangeItem_allocbuf (num_changes);
	changelist._maximum = num_changes;
	changelist._length = num_changes;

	for (i = 0, l = changes; l; l = l->next, i ++) {
		GNOME_Evolution_Addressbook_BookChangeItem *change = (GNOME_Evolution_Addressbook_BookChangeItem*)l->data;
		changelist._buffer[i] = *change;
		changelist._buffer[i].vcard = CORBA_string_dup (change->vcard);
	}

	g_list_foreach (changes, (GFunc)CORBA_free, NULL);
	g_list_free (changes);

	GNOME_Evolution_Addressbook_BookListener_notifyChangesRequested (book->priv->listener,
									 status,
									 &changelist,
									 &ev);

	if (ev._major != CORBA_NO_EXCEPTION)
		g_message ("could not notify listener of get-changes response");

	CORBA_exception_free (&ev);

	CORBA_free(changelist._buffer);
}

/**
 * pas_book_report_writable:
 */
void
pas_book_report_writable (PASBook                           *book,
			  gboolean                           writable)
{
	CORBA_Environment ev;

	CORBA_exception_init (&ev);

	GNOME_Evolution_Addressbook_BookListener_notifyWritable (
		book->priv->listener, (CORBA_boolean) writable, &ev);

	if (ev._major != CORBA_NO_EXCEPTION) {
		g_warning ("pas_book_report_writable: Exception "
			   "responding to BookListener!\n");
	}

	CORBA_exception_free (&ev);
}

static void
pas_book_construct (PASBook                *book,
		    PASBackend             *backend,
		    const char             *uri,
		    GNOME_Evolution_Addressbook_BookListener listener)
{
	PASBookPrivate *priv;
	CORBA_Environment ev;

	g_return_if_fail (book != NULL);

	priv = book->priv;

	CORBA_exception_init (&ev);
	book->priv->listener = CORBA_Object_duplicate (listener, &ev);

	if (ev._major != CORBA_NO_EXCEPTION) {
		g_message ("pas_book_construct(): could not duplicate the listener");
		CORBA_exception_free (&ev);
		return;
	}

	CORBA_exception_free (&ev);

	priv->backend   = backend;
	priv->uri       = g_strdup (uri);

}

/**
 * pas_book_new:
 */
PASBook *
pas_book_new (PASBackend                               *backend,
	      const char                               *uri,
	      GNOME_Evolution_Addressbook_BookListener  listener)
{
	PASBook *book;
	char *caps = pas_backend_get_static_capabilities (backend);

	book = g_object_new (PAS_TYPE_BOOK,
			     "poa", bonobo_poa_get_threaded (ORBIT_THREAD_HINT_PER_REQUEST, NULL),
			     NULL);

	pas_book_construct (book, backend, uri, listener);

	g_free (caps);

	return book;
}

static void
pas_book_dispose (GObject *object)
{
	PASBook *book = PAS_BOOK (object);

	if (book->priv) {
		CORBA_Environment ev;

		CORBA_exception_init (&ev);
		CORBA_Object_release (book->priv->listener, &ev);

		if (ev._major != CORBA_NO_EXCEPTION)
			g_message ("pas_book_construct(): could not release the listener");

		CORBA_exception_free (&ev);

		g_free (book->priv->uri);
		g_free (book->priv);
		book->priv = NULL;
	}

	if (G_OBJECT_CLASS (pas_book_parent_class)->dispose)
		G_OBJECT_CLASS (pas_book_parent_class)->dispose (object);
}

static void
pas_book_class_init (PASBookClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	POA_GNOME_Evolution_Addressbook_Book__epv *epv;

	pas_book_parent_class = g_type_class_peek_parent (klass);

	object_class->dispose = pas_book_dispose;

	epv = &klass->epv;

	epv->open                    = impl_GNOME_Evolution_Addressbook_Book_open;
	epv->remove                  = impl_GNOME_Evolution_Addressbook_Book_remove;
	epv->getContact              = impl_GNOME_Evolution_Addressbook_Book_getContact;
	epv->getContactList          = impl_GNOME_Evolution_Addressbook_Book_getContactList;
	epv->authenticateUser        = impl_GNOME_Evolution_Addressbook_Book_authenticateUser;
	epv->addContact              = impl_GNOME_Evolution_Addressbook_Book_addContact;
	epv->removeContacts          = impl_GNOME_Evolution_Addressbook_Book_removeContacts;
	epv->modifyContact           = impl_GNOME_Evolution_Addressbook_Book_modifyContact;
	epv->getStaticCapabilities   = impl_GNOME_Evolution_Addressbook_Book_getStaticCapabilities;
	epv->getSupportedFields      = impl_GNOME_Evolution_Addressbook_Book_getSupportedFields;
	epv->getSupportedAuthMethods = impl_GNOME_Evolution_Addressbook_Book_getSupportedAuthMethods;
	epv->getBookView             = impl_GNOME_Evolution_Addressbook_Book_getBookView;
	epv->getChanges              = impl_GNOME_Evolution_Addressbook_Book_getChanges;
	epv->cancelOperation         = impl_GNOME_Evolution_Addressbook_Book_cancelOperation;
}

static void
pas_book_init (PASBook *book)
{
	book->priv                = g_new0 (PASBookPrivate, 1);
}

BONOBO_TYPE_FUNC_FULL (
		       PASBook,
		       GNOME_Evolution_Addressbook_Book,
		       BONOBO_TYPE_OBJECT,
		       pas_book);
