/*
 * gajim.c
 *       gajim plugin for caja-sendto
 *
 * Copyright (C) 2006 Dimitur Kirov
 *               2006 Roberto Majadas <telemaco@openshine.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * 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
 * General Public License for more av.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301  USA.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <glib/gi18n-lib.h>

#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include "caja-sendto-plugin.h"

#define OBJ_PATH "/org/gajim/dbus/RemoteObject"
#define INTERFACE "org.gajim.dbus.RemoteInterface"
#define SERVICE "org.gajim.dbus"

const gchar *COMPLETION_PROPS[] = {"name", "jid"};
/* list of contacts, which are not offline */
static GHashTable *jid_table = NULL;
static gchar *iconset;

DBusGProxy *proxy = NULL;

/*
 * contact cb, gets property from contact dict
 * and put online contacts to jid_table
 */
static void
_foreach_contact(gpointer contact, gpointer user_data)
{
	const gchar *show;

	GValue *value;
	GHashTable *contact_table;

	/* holds contact props of already exisiting jid/nick */
	GHashTable *existing_contact;

	/* name of the contact in completion list
	   it may be jid, nick, jid (account), or nick(account) */
	GString *contact_str;

	gchar *jid;
	gchar *account;
	gint i;

	if (contact == NULL) {
		g_warning("Null contact in the list");
		return;
	}
	contact_table = (GHashTable *) contact;
	account = (gchar *) user_data;

	value = g_hash_table_lookup(contact_table, "show");
	if (value == NULL || !G_VALUE_HOLDS_STRING(value)) {
		g_warning("String expected (contact - show)");
		g_hash_table_destroy(contact_table);
		return;
	}
	show = g_value_get_string ((GValue *)value);
	if(g_str_equal(show, "offline") || g_str_equal(show, "error")) {
		g_hash_table_destroy(contact_table);
		return;
	}
	/* remove unneeded item with key resource and add account
	   to contact properties */
	g_hash_table_insert(contact_table, "account", account);
	g_hash_table_remove(contact_table, "resource");

	/* add nick the same way as jid */
	for(i=0;i<2;i++) {
		value = g_hash_table_lookup(contact_table, COMPLETION_PROPS[i]);
		if(value == NULL || !G_VALUE_HOLDS_STRING(value)) {
			g_warning("String expected (contact - name)");
			return;
		}
		jid = g_value_dup_string((GValue *)value);
		existing_contact = g_hash_table_lookup(jid_table, jid);
		if(existing_contact) {
			/* add existing contact as nick (account) */
			contact_str = g_string_new(jid);
			g_string_append(contact_str, " (");
			g_string_append(contact_str,
				g_hash_table_lookup(existing_contact, "account"));
			g_string_append(contact_str, ")");
			g_hash_table_insert(jid_table, contact_str->str,
													existing_contact);
			g_string_free(contact_str, FALSE);

			/* add current contact as nick (account) */
			contact_str = g_string_new(jid);
			g_string_append(contact_str, " (");
			g_string_append(contact_str,
				g_hash_table_lookup(contact_table, "account"));
			g_string_append(contact_str, ")");
			g_hash_table_insert(jid_table, contact_str->str,
													contact_table);
			g_string_free(contact_str, FALSE);
		}
		else {
			g_hash_table_insert(jid_table, jid, contact_table);
		}
	}

}

/*
 * connect to session bus, onsuccess return TRUE
 */
static gboolean
init_dbus (void)
{
	DBusGConnection *connection;
	GError *error;
	gchar **accounts;

	error = NULL;
	connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
	if(error != NULL) {
		g_warning("[Gajim] unable to get session bus, error was:\n %s", error->message);
		g_error_free(error);
		return FALSE;
	}
	proxy = dbus_g_proxy_new_for_name(connection,
									 SERVICE,
									 OBJ_PATH,
									 INTERFACE);
	dbus_g_connection_unref(connection);
	if (proxy == NULL){
		return FALSE;
	}

	error = NULL;
	if (!dbus_g_proxy_call (proxy, "list_accounts", &error, G_TYPE_INVALID,
				G_TYPE_STRV, &accounts, G_TYPE_INVALID))
	{
		g_object_unref(proxy);
		g_error_free(error);
		return FALSE;
	}
	g_strfreev(accounts);
	return TRUE;
}

/*
 * Print appropriate warnings when dbus raised error
 * on queries
 */
static void
_handle_dbus_exception (GError *error, gboolean empty_list_messages)
{
	if (error == NULL) {
		g_warning("[Gajim] unable to parse result");
		return;
	}
	else if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) {
		g_warning ("[Gajim] caught remote method exception %s: %s",
			dbus_g_error_get_name (error),
			error->message);
	}
	else if(empty_list_messages) {
		/* empty list and error goes here */
		g_warning ("[Gajim] empty result set: %d %d %s\n", error->domain,
			   error->code, error->message);
	}
	g_error_free (error);
}

/*
 * query object, about the contact list for each account
 * and fill all available contacts in the contacts table
 */
static gboolean
_get_contacts (void)
{
	GError *error;
	GSList *contacts_list;
	GHashTable *prefs_map;
	gchar **accounts;
	gchar **account_iter;
	gchar *account;

	error = NULL;

	if (proxy == NULL) {
		g_warning("[Gajim] unable to connect to session bus");
		return FALSE;
	}
	/* get gajim prefs and lookup for iconset */
	if (!dbus_g_proxy_call(proxy, "prefs_list", &error, G_TYPE_INVALID,
			dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING),
			&prefs_map, G_TYPE_INVALID))
	{
		_handle_dbus_exception(error, TRUE);
		return FALSE;
	}
	gpointer iconset_ptr = g_hash_table_lookup(prefs_map, "iconset");
	if (iconset_ptr != NULL) {
		iconset = g_strdup((gchar *)iconset_ptr);
	} else {
		g_warning("[Gajim] unable to get prefs value for iconset");
		return FALSE;
	}
	g_hash_table_destroy(prefs_map);
	/* END get gajim prefs */
	error= NULL;
	if (!dbus_g_proxy_call (proxy, "list_accounts", &error, G_TYPE_INVALID,
			G_TYPE_STRV,
			&accounts, G_TYPE_INVALID))
	{
		_handle_dbus_exception(error, TRUE);
		return FALSE;
	}
	for(account_iter = accounts; *account_iter ; account_iter++) {
		account = g_strdup(*account_iter);
		error = NULL;
		/* query gajim remote object and put results in 'contacts_list' */
		if (!dbus_g_proxy_call (proxy, "list_contacts", &error,
				G_TYPE_STRING, account, /* call arguments */
				G_TYPE_INVALID, /* delimiter */
				/* return value is collection of maps */
				dbus_g_type_get_collection ("GSList",
					dbus_g_type_get_map ("GHashTable",
						G_TYPE_STRING, G_TYPE_VALUE)),
				&contacts_list, G_TYPE_INVALID))
		{
			_handle_dbus_exception(error, FALSE);
			error = NULL;
			continue;
		}
		g_slist_foreach (contacts_list, _foreach_contact, account);
		g_slist_free(contacts_list);
	}
	g_strfreev (accounts);
	return TRUE;
}

static gboolean
init (NstPlugin *plugin)
{
	g_print ("Init gajim plugin\n");

#ifdef ENABLE_NLS
	bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif /* ENABLE_NLS */

	/* connect to gajim dbus service */
	jid_table = g_hash_table_new (g_str_hash, g_str_equal);
	if (!init_dbus()) {
		return FALSE;
	}
	return TRUE;
}

static void
_set_pixbuf_from_status (const gchar *show, GdkPixbuf **pixbuf)
{
	GString *pixbuf_path;
	GError *error;

	pixbuf_path = g_string_new(GAJIM_SHARE_DIR);
	g_string_append_c(pixbuf_path, '/');
	g_string_append(pixbuf_path, "data");
	g_string_append_c(pixbuf_path, '/');
	g_string_append(pixbuf_path, "iconsets");
	g_string_append_c(pixbuf_path, '/');
	g_string_append(pixbuf_path, iconset);
	g_string_append_c(pixbuf_path, '/');
	g_string_append(pixbuf_path, "16x16");
	g_string_append_c(pixbuf_path, '/');
	g_string_append(pixbuf_path, show);
	g_string_append(pixbuf_path, ".png");
	if(g_file_test(pixbuf_path->str, G_FILE_TEST_EXISTS) &&
		g_file_test(pixbuf_path->str, G_FILE_TEST_IS_REGULAR)) {
		error = NULL;
		*pixbuf = gdk_pixbuf_new_from_file(pixbuf_path->str, &error);
		if(error != NULL) {
			g_error_free(error);
		}
	}
	g_string_free(pixbuf_path, FALSE);
}

static void
_add_contact_to_model(gpointer key, gpointer value, gpointer user_data)
{
	GtkTreeIter *iter;
	GtkListStore *store;
	GdkPixbuf *pixbuf;
	GValue *val;
	GHashTable *contact_props;
	const gchar *show;

	contact_props = (GHashTable *) value;
	pixbuf = NULL;
	val = g_hash_table_lookup(contact_props, "show");
	if (value == NULL || !G_VALUE_HOLDS_STRING(val)) {
		g_warning("String expected (contact - show)");
		pixbuf = NULL;
	} else {
		show = g_value_get_string ((GValue *)val);
		_set_pixbuf_from_status(show, &pixbuf);
	}

	store = (GtkListStore *) user_data;
	iter = g_malloc (sizeof(GtkTreeIter));
	gtk_list_store_append (store, iter);
	gtk_list_store_set (store, iter, 0, pixbuf, 1, key, -1);
	g_free (iter);
}

/*
 * put gajim contacts to jid_list
 * filtering only these which are connected
 */
static gboolean
add_gajim_contacts_to_model (GtkListStore *store)
{
	if(!_get_contacts()) {
		return FALSE;
	}
	if(g_hash_table_size(jid_table) == 0) {
		return FALSE;
	}
	g_hash_table_foreach(jid_table, _add_contact_to_model, store);
	return TRUE;
}

/*
 * fill completion model for the entry, using list of
 * available gajim contacts
 */
static GtkWidget *
get_contacts_widget (NstPlugin *plugin)
{
	GtkWidget *entry;
	GtkEntryCompletion *completion;
	GtkListStore *store;
	GtkCellRenderer *renderer;
	GtkTreeModel *completion_model;

	entry = gtk_entry_new ();
	completion = gtk_entry_completion_new ();

	renderer = gtk_cell_renderer_pixbuf_new ();
	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
					renderer,
					FALSE);
	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (completion), renderer,
					"pixbuf", 0, NULL);

	store = gtk_list_store_new (2, GDK_TYPE_PIXBUF, G_TYPE_STRING);
	if(!add_gajim_contacts_to_model (store)) {
		gtk_widget_set_sensitive(entry, FALSE);
	}
	completion_model = GTK_TREE_MODEL (store);
	gtk_entry_completion_set_model (completion, completion_model);
	gtk_entry_set_completion (GTK_ENTRY (entry), completion);
	gtk_entry_completion_set_text_column (completion, 1);
	g_object_unref (completion_model);
	g_object_unref (completion);
	return entry;
}

static void
show_error (const gchar *title, const gchar *message)
{
	GtkWidget *dialog;

	dialog = gtk_message_dialog_new_with_markup(NULL,
								GTK_DIALOG_DESTROY_WITH_PARENT,
								GTK_MESSAGE_ERROR,
								GTK_BUTTONS_CLOSE, NULL);

	gchar *msg = g_markup_printf_escaped("<b>%s</b>\n\n%s", title, message);
	gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), msg);
	g_free (msg);

	gtk_dialog_run (GTK_DIALOG (dialog));
	gtk_widget_destroy (dialog);
}

static gboolean
send_files (NstPlugin *plugin,
	    GtkWidget *contact_widget,
	    GList *file_list)
{
	GError *error;
	GValue *value;
	GList *file_iter;
	GHashTable *contact_props;

	gchar *send_to;
	gchar *jid;
	gchar *account;
	gchar *file_path;

	if(proxy == NULL) {
		show_error(_("Unable to send file"),
			   _("There is no connection to gajim remote service."));
		return FALSE;
	}
	send_to = (gchar *) gtk_entry_get_text (GTK_ENTRY(contact_widget));
	g_debug("[Gajim] sending to: %s", send_to);
	if (strlen (send_to) != 0){
		contact_props = g_hash_table_lookup (jid_table, send_to);
		if(contact_props == NULL) {
			jid = send_to;
			account = NULL;
		}
		else {
			value = g_hash_table_lookup(contact_props, "jid");
			if(value == NULL || !G_VALUE_HOLDS_STRING(value)) {
				g_warning("[Gajim] string expected (contact - jid)");
				return FALSE;
			}

			jid = g_value_dup_string((GValue *)value);
			account = g_hash_table_lookup(contact_props, "account");
		}
	}
	else {
		g_warning("[Gajim] missing recipient");
		show_error(_("Sending file failed"),
						_("Recipient is missing."));
		return FALSE;
	}

	error= NULL;
	for(file_iter = file_list; file_iter != NULL; file_iter = file_iter->next) {
		char *uri = file_iter->data;

		g_debug("[Gajim] file: %s", uri);
		error= NULL;
		file_path = g_filename_from_uri(uri, NULL, &error);
		if(error != NULL) {
			g_warning("%d Unable to convert URI `%s' to absolute file path",
				error->code, uri);
			g_error_free(error);
			continue;
		}

		g_debug("[Gajim] file: %s", file_path);
		if(account) {
			dbus_g_proxy_call (proxy, "send_file", &error,
					   G_TYPE_STRING, file_path,
					   G_TYPE_STRING, jid,
					   G_TYPE_STRING, account,
					   G_TYPE_INVALID,
					   G_TYPE_INVALID);
		} else {
			dbus_g_proxy_call (proxy, "send_file", &error,
					   G_TYPE_STRING, file_path,
					   G_TYPE_STRING, jid,
					   G_TYPE_INVALID,
					   G_TYPE_INVALID);
		}
		g_free(file_path);
		if(error != NULL)
		{
			if(error->domain != DBUS_GERROR || error->code != DBUS_GERROR_INVALID_ARGS) {
				g_warning("[Gajim] sending file %s to %s failed:", uri, send_to);
				g_error_free(error);
				show_error(_("Sending file failed"), _("Unknown recipient."));
				return FALSE;
			}
			g_error_free(error);
		}
	}
	return TRUE;
}

static gboolean
destroy (NstPlugin *plugin)
{
	if (proxy != NULL) {
		g_object_unref(proxy);
	}
	g_hash_table_destroy(jid_table);
	return TRUE;
}

static
NstPluginInfo plugin_info = {
	"im-jabber",
	"gajim",
	N_("Instant Message (Gajim)"),
	NULL,
	CAJA_CAPS_NONE,
	init,
	get_contacts_widget,
	NULL,
	send_files,
	destroy
};

NST_INIT_PLUGIN (plugin_info)
