/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>  
 *
 *
 * Authors:
 *		Srinivasa Ragavan <sragavan@novell.com>
 *
 * Copyright (C) 2009 Novell, Inc. (www.novell.com)
 *
 */

#include <glib/gi18n.h>
#include <mail-utils.h>
#include <libebook/e-book.h>
#include <libedataserver/e-flag.h>
#include <camel/camel-operation.h>
#include <camel/camel-internet-address.h>
#include <mail/mail-mt.h>

static GtkIconTheme *theme = NULL;

GdkPixbuf *
mail_utils_get_icon (const char *icon, int icon_size)
{
	int width, height;

	if(!theme)
		theme = gtk_icon_theme_get_default ();

	gtk_icon_size_lookup (icon_size, &width, &height);
	return gtk_icon_theme_load_icon (theme, icon, width, GTK_ICON_LOOKUP_FORCE_SIZE|GTK_ICON_LOOKUP_USE_BUILTIN, NULL);	
}

char *
mail_utils_get_icon_path (const char *icon, int icon_size)
{
	int width, height;
	GtkIconInfo *info;
	char *file;

	if(!theme)
		theme = gtk_icon_theme_get_default ();

	gtk_icon_size_lookup (icon_size, &width, &height);

	info = gtk_icon_theme_lookup_icon (theme, icon, width, GTK_ICON_LOOKUP_FORCE_SIZE|GTK_ICON_LOOKUP_USE_BUILTIN);

	file = g_strdup(gtk_icon_info_get_filename(info));
	gtk_icon_info_free (info);

	return file;
}

static EBook *default_book = NULL;
GStaticMutex booklock = G_STATIC_MUTEX_INIT;
static GHashTable *contact_pixbuf_cache = NULL;

struct TryOpenEBookStruct {
	GError **error;
	EFlag *flag;
	gboolean result;
};

static void
try_open_e_book_cb (EBook *book, EBookStatus status, gpointer closure)
{
	struct TryOpenEBookStruct *data = (struct TryOpenEBookStruct *)closure;

	if (!data)
		return;

	data->result = status == E_BOOK_ERROR_OK;

	if (!data->result) {
		g_clear_error (data->error);
		g_set_error (data->error, E_BOOK_ERROR, status, "EBookStatus returned %d", status);
	}

	e_flag_set (data->flag);
}

/**
 * try_open_e_book:
 * Tries to open address book asynchronously, but acts as synchronous.
 * The advantage is it checks periodically whether the camel_operation
 * has been canceled or not, and if so, then stops immediately, with
 * result FALSE. Otherwise returns same as e_book_open
 **/
static gboolean
try_open_e_book (EBook *book, gboolean only_if_exists, GError **error)
{
	struct TryOpenEBookStruct data;
	gboolean canceled = FALSE;
	EFlag *flag = e_flag_new ();

	data.error = error;
	data.flag = flag;
	data.result = FALSE;
	
	if (e_book_async_open (book, only_if_exists, try_open_e_book_cb, &data) != FALSE) {
		e_flag_free (flag);
		g_clear_error (error);
		g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_OTHER_ERROR, "Failed to call e_book_async_open.");
		return FALSE;
	}

	while (canceled = camel_operation_cancel_check (NULL), !canceled && !e_flag_is_set (flag)) {
		GTimeVal wait;

		g_get_current_time (&wait);
		g_time_val_add (&wait, 250000); /* waits 250ms */

		e_flag_timed_wait (flag, &wait);
	}

	if (canceled) {
		g_clear_error (error);
		g_set_error (error, E_BOOK_ERROR, E_BOOK_ERROR_CANCELLED, "Operation has been canceled.");
		e_book_cancel_async_op (book, NULL);
		/* it had been canceled, the above callback may not be called, thus setting flag here */
		e_flag_set (flag);
	}

	e_flag_wait (flag);
	e_flag_free (flag);

	return data.result && (!error || !*error);
}

static EContact *
mu_get_contact (const char *addr)
{
	GError *error = NULL;
	GList *contacts = NULL;
	EContact *contact;
	EBookQuery *query;

	if (!default_book) {
		default_book = e_book_new_default_addressbook (&error);
		contact_pixbuf_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);

		if (!try_open_e_book (default_book, TRUE, &error)) {
			default_book = NULL;
			g_message ("Unable to open addressbook: %s\n", error->message);
			return NULL;
		}
	}

	query = e_book_query_field_test(E_CONTACT_EMAIL, E_BOOK_QUERY_IS, addr);
	
	if (!e_book_get_contacts(default_book, query, &contacts, &error)) {
		g_message ("Unable to get contacts: %s\n", error->message);
		return NULL;
	}

	if (contacts) {
		contact = contacts->data;
		g_object_ref(contact);
		g_list_foreach (contacts, (GFunc)g_object_unref, NULL);
		g_list_free (contacts);
	} else {
		printf("Unable to get contact for %s\n", addr);
	}

	return contact;
}

struct _mail_contact_msg {
	MailMsg base;

	GtkWidget *widget;
	char *address;
	EContact *contact;
	GdkPixbuf *pixbuf;
};

static gchar *
mu_contact_desc (struct _mail_contact_msg *m)
{
	return g_strdup(_("Rendering image"));
}

static void
mu_contact_exec (struct _mail_contact_msg *m)
{
	g_static_mutex_lock (&booklock);
	if (!contact_pixbuf_cache || !g_hash_table_lookup_extended (contact_pixbuf_cache, m->address, NULL, (gpointer)&m->pixbuf)) {
		m->contact = mu_get_contact (m->address);

		if (m->contact) {
			EContactPhoto *photo;

			photo = e_contact_get (m->contact, E_CONTACT_PHOTO);
			if (!photo)
				photo = e_contact_get (m->contact, E_CONTACT_LOGO);
			if (photo && photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
				GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type ("image/png", NULL);
				GdkPixbuf *pbuf;
	
				gdk_pixbuf_loader_set_size (loader, 72, 72);
				gdk_pixbuf_loader_write (loader, photo->data.inlined.data, photo->data.inlined.length, NULL);
				gdk_pixbuf_loader_close (loader, NULL);
				e_contact_photo_free (photo);
	
				pbuf = gdk_pixbuf_loader_get_pixbuf (loader);
				m->pixbuf = pbuf;

				g_object_ref (pbuf);
				g_hash_table_insert (contact_pixbuf_cache, g_strdup(m->address), pbuf);

				m->address = NULL; /* Saved one free/alloc cycle */
	
				g_object_unref (loader);
			} else if (photo && photo->type == E_CONTACT_PHOTO_TYPE_URI) {
				/* Handle URI */
			}
		} else
			g_hash_table_insert (contact_pixbuf_cache, g_strdup(m->address), NULL);
	} 
	g_static_mutex_unlock (&booklock);

}

static void
mu_contact_done (struct _mail_contact_msg *m)
{
	if (m->pixbuf) {
		gtk_image_set_from_pixbuf((GtkImage *)m->widget, m->pixbuf);
	}
}

static void
mu_contact_free (struct _mail_contact_msg *m)
{
	if (m->contact)
		g_object_unref (m->contact);
	g_free (m->address);
}

static MailMsgInfo mail_contact_info = {
	sizeof (struct _mail_contact_msg),
	(MailMsgDescFunc) mu_contact_desc,
	(MailMsgExecFunc) mu_contact_exec,
	(MailMsgDoneFunc) mu_contact_done,
	(MailMsgFreeFunc) mu_contact_free
};


int
mail_util_fill_photo (GtkWidget *widget, const char *address)
{
	struct _mail_contact_msg *m;
	gint id;
	CamelInternetAddress *cia;
	const char *email;

	cia = camel_internet_address_new();
	camel_address_decode((CamelAddress *) cia, (const gchar *) address);

	m = mail_msg_new (&mail_contact_info);

	m->widget = widget;
	camel_internet_address_get(cia, 0, NULL, &email);
	m->address = g_strdup(email);
	camel_object_unref(cia);
	id = m->base.seq;
	mail_msg_unordered_push (m);

	return id;
}

void
mail_util_clear_photo_cache ()
{
	g_static_mutex_lock (&booklock);
	g_hash_table_remove_all (contact_pixbuf_cache);
	g_static_mutex_unlock (&booklock);

}
