/* sunone-invitation-list-model.c
 *
 * Copyright (C) 2002-2005 Sun Microsystems, Inc
 *
 * AUTHORS
 *     Jack Jia <jack.jia@sun.com>
 *     Harry Lu <harry.lu@sun.com>
 *     Alfred Peng <alfred.peng@sun.com>
 *     Rodrigo Moya <rodrigo@ximian.com>
 *
 */

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

#include <string.h>
#include <glib/gi18n.h>
#include <bonobo/bonobo-exception.h>
#include <bonobo/bonobo-moniker-util.h>
#include <libgnomevfs/gnome-vfs.h>
#include <libsoup/soup.h>
#include <libecal/e-cal-time-util.h>
#include <libecal/e-cal.h>
#include <libedataserver/e-time-utils.h>
#include "lib/sunone-util.h"
#include "calendar/cal-backend-wcap-events.h"
#include "sunone-invitation-list-model.h"
#include "sunone-component.h"
#include "sunone-offline-listener.h"

/* poll every 60 minutes */
#define POLL_INTERVAL  60*60*1000
#define ROW_VALID(model, row) (row >= 0 && row < g_hash_table_size (model->priv->invitations))

struct _SunOneInvitationListModelPrivate {
	SunOneConnection *so_cnc;
	GHashTable *invitations;
	GList *invitations_list;
	EConfigListener *config;
	gint timeout_id;
	gint stamp;
	char *uri;
	char *calid;
};

static void sunone_invitation_list_model_class_init (SunOneInvitationListModelClass *klass);
static void sunone_invitation_list_model_init (SunOneInvitationListModel *object);
static void sunone_invitation_list_model_dispose (GObject *object);
static void sunone_invitation_list_model_finalize (GObject *object);

static GObjectClass *parent_class = NULL;

typedef struct {
	ECalComponent *comp;
	int count;

	gboolean exists;
} SunOneInvitationData;

const char *
sunone_invitation_list_model_get_uri (SunOneInvitationListModel *model)
{
	SunOneInvitationListModelPrivate *priv = model->priv;

	g_return_val_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model), NULL);
	return priv->uri;
}

static void
sunone_invitation_list_model_class_init (SunOneInvitationListModelClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	parent_class = g_type_class_ref (G_TYPE_OBJECT);
	
	object_class->dispose = sunone_invitation_list_model_dispose;
	object_class->finalize = sunone_invitation_list_model_finalize;
}

static void
key_changed_cb (EConfigListener *listener, const char *key, void *data)
{
	SunOneInvitationListModel *model = SUNONE_INVITATION_LIST_MODEL (data);
	SunOneInvitationListModelPrivate *priv = model->priv;
	char *location;
	
	location = e_config_listener_get_string_with_default (priv->config, "/apps/evolution/calendar/display/timezone", "UTC", NULL);
	model->zone = icaltimezone_get_builtin_timezone (location);
	g_free (location);

	sunone_invitation_list_model_poll_cb (model);
}

static void
sunone_invitation_list_model_init (SunOneInvitationListModel *object)
{
	SunOneInvitationListModel *model = SUNONE_INVITATION_LIST_MODEL (object);
	SunOneInvitationListModelPrivate *priv;
	char *location;

	model->priv = g_new0 (SunOneInvitationListModelPrivate, 1);
	priv = model->priv;

	priv->uri = NULL;
	priv->calid = NULL;	

	priv->stamp = g_random_int ();
	priv->invitations = g_hash_table_new (g_str_hash, g_str_equal);
	priv->invitations_list = NULL;
	priv->config = e_config_listener_new ();
	g_signal_connect (G_OBJECT (priv->config), "key_changed", G_CALLBACK (key_changed_cb), model);
	priv->timeout_id = -1;

	model->account = NULL;
	location = e_config_listener_get_string_with_default (priv->config, "/apps/evolution/calendar/display/timezone", "UTC", NULL);
	model->zone = icaltimezone_get_builtin_timezone (location);
	g_free (location);

}

static void
sunone_invitation_list_model_dispose (GObject *object)
{
	SunOneInvitationListModel *model =  SUNONE_INVITATION_LIST_MODEL (object);
	SunOneInvitationListModelPrivate *priv = model->priv;

	g_return_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model));

	if (model->account) {
		g_object_unref (G_OBJECT (model->account));
		model->account = NULL;
	}

	if (model->props) {
		sunone_connection_free_calprops (model->props);
		model->props = NULL;
	}	

	if (model->zone)
		model->zone = NULL;

	if (priv) {
		if (priv->uri) {
			g_free (priv->uri);
			priv->uri = NULL;
		}

		if (priv->calid) {
			g_free (priv->calid);
			priv->calid = NULL;
		}

		if (priv->config) {
			g_object_unref (G_OBJECT (priv->config));
			priv->config = NULL;
		}
		
		if (priv->timeout_id != -1) {
			g_source_remove (priv->timeout_id); 
			priv->timeout_id = -1;
		}
			
		if (priv->invitations) {
			/* FIXME, destroy elements */
			g_hash_table_destroy (priv->invitations);
			priv->invitations = NULL;
		}
		if (priv->invitations_list) {
			g_list_free (priv->invitations_list);
			priv->invitations_list = NULL;
		}

		g_free (priv);
		model->priv = NULL;
	}
	
	if (G_OBJECT_CLASS (parent_class) ->dispose)
		G_OBJECT_CLASS (parent_class) ->dispose (object);
}

static void
sunone_invitation_list_model_finalize (GObject *object)
{
	if (G_OBJECT_CLASS (parent_class) ->dispose)
		G_OBJECT_CLASS (parent_class) ->finalize (object);
}

static GtkTreeModelFlags
get_flags (GtkTreeModel *model)
{
	g_return_val_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model), 0);

	return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
}

static int
get_n_columns (GtkTreeModel *model)
{
	g_return_val_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model), 0);

	return LAST_COL;
}

static GType
get_column_type (GtkTreeModel *model, int col)
{
	g_return_val_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model), G_TYPE_INVALID);

	switch (col) {
	case START_COL:
	case END_COL:
	case LOCATION_COL:
	case SUMMARY_COL:
	case ORGANIZER_COL:
	case STATUS_COL:
		return G_TYPE_STRING;
	default:
		return G_TYPE_INVALID;
	}
}

static gboolean
get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path)
{
	SunOneInvitationListModelPrivate *priv = SUNONE_INVITATION_LIST_MODEL (model)->priv;
	int row;

	g_return_val_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model), FALSE);
	g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);

	row = gtk_tree_path_get_indices (path) [0];

	if (!ROW_VALID (SUNONE_INVITATION_LIST_MODEL (model), row))
	       return FALSE;

	iter->stamp = priv->stamp;
	iter->user_data = GINT_TO_POINTER (row);

	return TRUE;
}

static GtkTreePath *
get_path (GtkTreeModel *model, GtkTreeIter *iter)
{
	SunOneInvitationListModelPrivate *priv = SUNONE_INVITATION_LIST_MODEL (model)->priv;
	GtkTreePath *result;
	int row;

	g_return_val_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model), NULL);
	g_return_val_if_fail (iter->stamp == priv->stamp, NULL);

	row = GPOINTER_TO_INT (iter->user_data);

	g_return_val_if_fail (ROW_VALID (SUNONE_INVITATION_LIST_MODEL (model), row), NULL);

	result = gtk_tree_path_new ();
	gtk_tree_path_append_index (result, row);
	return result;
}
	
static void
get_value (GtkTreeModel *model, GtkTreeIter *iter, int col, GValue *value)
{
	SunOneInvitationListModel *list_model;
	int row;
	SunOneInvitationData *id;
	ECalComponentDateTime dt;
	ECalComponentText text;
	ECalComponentOrganizer organizer;
	GSList *attendees, *l;
	icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION;
	struct tm t;
	const char *string;
	static char buffer[512];


	g_return_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model));
	g_return_if_fail (col >= 0 && col < LAST_COL);

	row = GPOINTER_TO_INT (iter->user_data);
	list_model = SUNONE_INVITATION_LIST_MODEL (model);	

	g_return_if_fail (iter->stamp == list_model->priv->stamp);
	g_return_if_fail (ROW_VALID (list_model, row));

	id = g_list_nth_data (list_model->priv->invitations_list, row);
	
	switch (col) {
	case START_COL:
		g_value_init (value, G_TYPE_STRING);
		e_cal_component_get_dtstart (id->comp, &dt);
		if (dt.value) {
			icaltimezone_convert_time (dt.value, icaltimezone_get_utc_timezone (), list_model->zone);
			t = icaltimetype_to_tm (dt.value);
			e_time_format_date_and_time (&t, FALSE, !dt.value->is_date, FALSE, buffer, 512);
		} else {
			buffer[0] ='\0';
		}
		e_cal_component_free_datetime (&dt);
		g_value_set_string (value, buffer);
		break;
	case END_COL:
		g_value_init (value, G_TYPE_STRING);
		e_cal_component_get_dtend (id->comp, &dt);
		if (dt.value) {
			icaltimezone_convert_time (dt.value, icaltimezone_get_utc_timezone (), list_model->zone);
			t = icaltimetype_to_tm (dt.value);
			e_time_format_date_and_time (&t, FALSE, !dt.value->is_date, FALSE, buffer, 512);
		} else {
			e_cal_component_free_datetime (&dt);
			e_cal_component_get_dtstart (id->comp, &dt);
			if (dt.value && dt.value->is_date) {
				t = icaltimetype_to_tm (dt.value);
				e_time_format_date_and_time (&t, FALSE, !dt.value->is_date, FALSE, buffer, 512);
			} else {
				buffer[0] ='\0';
			}
		}
		e_cal_component_free_datetime (&dt);
		g_value_set_string (value, buffer);
		break;
	case LOCATION_COL:
		g_value_init (value, G_TYPE_STRING);
		e_cal_component_get_location (id->comp, &string);
		g_value_set_string (value, string ? string: "");
		break;
	case SUMMARY_COL:
		g_value_init (value, G_TYPE_STRING);
		e_cal_component_get_summary (id->comp, &text);
		if (id->count == 1)
			g_value_set_string (value, text.value ? text.value : _("Untitled Appointment"));
		else {
			g_snprintf (buffer, 512, "%s (%d)", 
				    text.value ? (char *)text.value : _("Untitled Appointment"), id->count);
			g_value_set_string (value, buffer);
		}
		break;
	case ORGANIZER_COL:
		g_value_init (value, G_TYPE_STRING);
		e_cal_component_get_organizer (id->comp, &organizer);
		g_value_set_string (value, organizer.value ? organizer.value : "");
		break;
	case STATUS_COL:
		g_value_init (value, G_TYPE_STRING);
		e_cal_component_get_attendee_list (id->comp, &attendees);
		for (l = attendees; l != NULL; l = l->next) {
			ECalComponentAttendee *a = l->data;
			const char *attendee = a->value;
			
			if (!strncasecmp ("mailto:", attendee, 7)) {
				attendee = attendee + 7;
			}
			if (!strcasecmp (list_model->priv->calid, attendee) ||
				!strcasecmp (sunone_account_get_email (list_model->account), attendee))
				status = a->status;
		}
		e_cal_component_free_attendee_list (attendees);
		
		switch (status) {
		case ICAL_PARTSTAT_NEEDSACTION:
			string = _("Needs Action");
			break;
		case ICAL_PARTSTAT_ACCEPTED:
			string = _("Accepted");
			break;
		case ICAL_PARTSTAT_DECLINED:
			string = _("Declined");
			break;
		case ICAL_PARTSTAT_TENTATIVE:
			string = _("Tentative");
			break;
		default:
			string = _("N/A");
		}
		g_value_set_string (value, string);
		break;
	}
}

static gboolean
iter_next (GtkTreeModel *model, GtkTreeIter *iter)
{
	int row;

	g_return_val_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model), FALSE);
	g_return_val_if_fail (iter->stamp == SUNONE_INVITATION_LIST_MODEL (model)->priv->stamp, FALSE);

	row = GPOINTER_TO_INT (iter->user_data) + 1;
	iter->user_data = GINT_TO_POINTER (row);

	return ROW_VALID (SUNONE_INVITATION_LIST_MODEL (model), row);
}

static gboolean
iter_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent)
{
	SunOneInvitationListModel *list_model;

	g_return_val_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model), FALSE);

	list_model = SUNONE_INVITATION_LIST_MODEL (model);	

	if (parent || g_hash_table_size (list_model->priv->invitations) <= 0)
		return FALSE;

	iter->stamp = list_model->priv->stamp;
	iter->user_data = GINT_TO_POINTER (0);
	
	return TRUE;
}

static gboolean
iter_has_child (GtkTreeModel *model, GtkTreeIter *iter)
{
	return FALSE;
}

static int
iter_n_children (GtkTreeModel *model, GtkTreeIter *iter)
{
	g_return_val_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model), -1);
	
	if (!iter)
		return g_hash_table_size (SUNONE_INVITATION_LIST_MODEL (model)->priv->invitations);

	g_return_val_if_fail (iter->stamp == SUNONE_INVITATION_LIST_MODEL (model)->priv->stamp, -1);

	return 0;
}

static gboolean
iter_nth_child (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent, int n)
{
	g_return_val_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model), FALSE);

	if (parent || !ROW_VALID (SUNONE_INVITATION_LIST_MODEL (model), n))
		return FALSE;

	iter->stamp = SUNONE_INVITATION_LIST_MODEL (model)->priv->stamp;
	iter->user_data = GINT_TO_POINTER (n);

	return TRUE;
}

static gboolean
iter_parent (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *child)
{
	return FALSE;
}

static void
_tree_model_init (GtkTreeModelIface *iface)
{
	iface->get_flags = get_flags;
	iface->get_n_columns = get_n_columns;
	iface->get_column_type = get_column_type;
	iface->get_iter = get_iter;
	iface->get_path = get_path;
	iface->get_value = get_value;
	iface->iter_next = iter_next;
	iface->iter_children = iter_children;
	iface->iter_has_child = iter_has_child;
	iface->iter_n_children = iter_n_children;
	iface->iter_nth_child = iter_nth_child;
	iface->iter_parent = iter_parent;
}

GType
sunone_invitation_list_model_get_type (void)
{
	static GType the_type = 0;

	if (!the_type) {
		static const GTypeInfo the_info = {
				sizeof (SunOneInvitationListModelClass),
				NULL,           /* base_init */
				NULL,           /* base_finalize */
				(GClassInitFunc) sunone_invitation_list_model_class_init,
				NULL,           /* class_finalize */
				NULL,           /* class_data */
				sizeof (SunOneInvitationListModel),
				0,
				(GInstanceInitFunc) sunone_invitation_list_model_init };
				                                                                                                
		static const GInterfaceInfo tree_model_info = {
				(GInterfaceInitFunc) _tree_model_init,
				NULL,
				NULL };
					                                                                                              
		the_type = g_type_register_static (GTK_TYPE_LIST_STORE, 
						   "SunOneInvitationListModel",
						   &the_info, 0);
 
		g_type_add_interface_static (the_type, 
					     GTK_TYPE_TREE_MODEL, 
					     &tree_model_info);
	}

	return the_type;
}

gboolean
sunone_invitation_list_model_poll_cb (gpointer data)
{
	SunOneInvitationListModel *model = SUNONE_INVITATION_LIST_MODEL (data);
	SunOneInvitationListModelPrivate *priv = model->priv;
	icalcomponent *icalcomp, *subcomp;
	icalcomponent_kind kind;
	GList *removals = NULL, *l;
	guint error_code;
	int state;
	SunOneConnection *cnc;

	g_return_val_if_fail (SUNONE_IS_INVITATION_LIST_MODEL (model), FALSE);

	sunone_account_is_offline (model->account, &state);
	/* if we are offline, clear invitation list*/
	if (state != ONLINE_MODE)
		goto removal;

	cnc = sunone_account_get_connection (model->account);

	/* Make sure we have permissions to view to items */
	if (model->props)
		sunone_connection_free_calprops (model->props);

	model->props = sunone_connection_get_calprops (cnc, priv->calid, FALSE);
	if (!model->props)
		goto removal;

	if (!sunone_util_has_permissions (model->props,
					sunone_connection_get_user (cnc), 
					SUNONE_ACE_CONTEXT_CALENDAR_COMPONENTS, 
					SUNONE_ACE_PERMISSION_READ | SUNONE_ACE_PERMISSION_REPLY) &&
				!sunone_util_has_permissions (model->props,
					sunone_connection_get_uid (cnc),
					SUNONE_ACE_CONTEXT_CALENDAR_COMPONENTS, 
					SUNONE_ACE_PERMISSION_READ | SUNONE_ACE_PERMISSION_REPLY))
		goto removal;

	/* Now fetch the invitations */
	error_code = sunone_connection_fetchcomponents_by_lastmod (cnc,
								   priv->calid,
								   icaltime_null_time (),
								   icaltime_null_time (),
								   TYPE_EVENT,
								   REQUEST_NEEDS_ACTION,
								   &icalcomp, NULL);

	if (!SUNONE_ERROR_IS_SUCCESSFUL (error_code))
		return TRUE;

	subcomp = icalcomponent_get_first_component (icalcomp, ICAL_ANY_COMPONENT);
	while (subcomp) {
		kind = icalcomponent_isa (subcomp);
		if (kind == ICAL_VEVENT_COMPONENT || kind == ICAL_VTODO_COMPONENT) {
			SunOneInvitationData *id;
			ECalComponent *comp;
			const char *uid;
			
			comp = e_cal_component_new ();
			e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp));

			e_cal_component_get_uid (comp, &uid);
			id = g_hash_table_lookup (priv->invitations, uid);
			if (id) {
				if (id->exists) {
					id->count++;
					g_object_unref (G_OBJECT (comp));
				} else {
					int row;
					GtkTreePath *path;
					GtkTreeIter iter;
					
					id->count = 1;
					id->exists = TRUE;
					g_object_unref (G_OBJECT (id->comp));
					id->comp = comp;

					row = g_list_index (priv->invitations_list, id);
					path = gtk_tree_path_new ();
					gtk_tree_path_append_index (path, row);
					get_iter (GTK_TREE_MODEL (model), &iter, path);
					gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
					gtk_tree_path_free (path);
				}
			} else {
				GtkTreePath *path;
				GtkTreeIter iter;

				id = g_new0 (SunOneInvitationData, 1);
				id->comp = comp;
				id->count = 1;
				id->exists = TRUE;

				g_hash_table_insert (priv->invitations, g_strdup (uid), id);
				priv->invitations_list = g_list_append (priv->invitations_list, id);
				path = gtk_tree_path_new ();
				gtk_tree_path_append_index (path, g_hash_table_size (priv->invitations) - 1);
				get_iter (GTK_TREE_MODEL (model), &iter, path);
				gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
				gtk_tree_path_free (path);
			}
		}
		subcomp = icalcomponent_get_next_component (icalcomp, ICAL_ANY_COMPONENT);
	}

	icalcomponent_free (icalcomp);

 removal:
	for (l = priv->invitations_list; l != NULL; l = l->next) {
		SunOneInvitationData *id = l->data;

		if (!id->exists) {
			gpointer orig_key, orig_value;
			const char *uid;
			
			removals = g_list_append (removals, l);

			e_cal_component_get_uid (id->comp, &uid);
			if (g_hash_table_lookup_extended (priv->invitations, uid, &orig_key, &orig_value)) {
				id = orig_value;

				g_hash_table_remove (priv->invitations, uid);
				g_free (orig_key);
			}
		} else {
			id->exists = FALSE;
		}
	}

	removals = g_list_reverse (removals);
	for (l = removals; l != NULL; l = l->next) {
		SunOneInvitationData *id;		
		int row;
		GtkTreePath *path;

		row = g_list_position (priv->invitations_list, l->data);
		priv->invitations_list = g_list_remove_link (priv->invitations_list, l->data);
		
		id = ((GList *)(l->data))->data;		
		g_object_unref (G_OBJECT (id->comp));
		g_free (id);
		g_list_free (l->data);

		path = gtk_tree_path_new ();
		gtk_tree_path_append_index (path, row);
		gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
		gtk_tree_path_free (path);
	}

	g_list_free (removals);

	return TRUE;
}

static gboolean
first_poll_cb (gpointer data)
{
	SunOneInvitationListModel *model = SUNONE_INVITATION_LIST_MODEL (data);
	SunOneInvitationListModelPrivate *priv = model->priv;

	sunone_invitation_list_model_poll_cb (model);
	priv->timeout_id = g_timeout_add (POLL_INTERVAL, sunone_invitation_list_model_poll_cb, model);

	return FALSE;
}

void
sunone_invitation_list_model_set_value (SunOneInvitationListModel *model, int col, int row, const char *val)
{
	SunOneInvitationListModelPrivate *priv = model->priv;
	SunOneInvitationData *id;
	icalcomponent *icalcomp, *clonecomp;
	icalproperty *prop;
	SunOneConnection *cnc;
	CalBackendWCAP *wcap;
	char *key;

	cnc = sunone_account_get_connection (model->account);
	
	id = g_list_nth_data (priv->invitations_list, row);
	switch (col) {
 	case STATUS_COL:
		icalcomp = e_cal_component_get_icalcomponent (id->comp);

		for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
			 prop != NULL;
			 prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) {
			icalvalue *value;
			icalparameter *param;
			const char *attendee;
			icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION;
			
			value = icalproperty_get_value (prop);
			if (!value)
				continue;

			attendee = icalvalue_get_string (value);
			if (!strncasecmp ("mailto:", attendee, 7)) {
				attendee = attendee + 7;
			} if (!strcasecmp (priv->calid, attendee) ||
				!strcasecmp (sunone_account_get_email (model->account), attendee)) {
				if (!strcmp (val, _("Needs Action")))
					status = ICAL_PARTSTAT_NEEDSACTION;
				else if (!strcmp (val, _("Accepted")))
					status = ICAL_PARTSTAT_ACCEPTED;
				else if (!strcmp (val, _("Declined")))
					status = ICAL_PARTSTAT_DECLINED;
				else if (!strcmp (val, _("Tentative")))
					status = ICAL_PARTSTAT_TENTATIVE;

				icalproperty_remove_parameter (prop, ICAL_PARTSTAT_PARAMETER);
				param = icalparameter_new_partstat (status);
				icalproperty_add_parameter (prop, param);

				e_cal_component_rescan (id->comp);
			}
		}

		clonecomp = icalcomponent_new_clone(icalcomp);
		key = g_strconcat (priv->calid, ":calendar", NULL);
		wcap = sunone_connection_get_wcap (cnc, key);
		g_free (key);
		if (wcap) {
			char *string = icalcomponent_as_ical_string (clonecomp);

			cal_backend_wcap_events_update_objects (E_CAL_BACKEND_SYNC (wcap),
					NULL, string, CALOBJ_MOD_ALL, NULL, NULL);
			g_free (string);
		}
		icalcomponent_free (clonecomp);
	}
}

SunOneInvitationListModel *
sunone_invitation_list_model_new (const char *uristr)
{
	SunOneInvitationListModelPrivate *priv;
	SunOneInvitationListModel *model;
	SunOneAccount *account;

	g_return_val_if_fail (uristr != NULL, NULL);

	account = sunone_component_get_account_from_uri (global_sunone_component, uristr);

	if (!account) {
		return NULL;
	}

	model = g_object_new (SUNONE_TYPE_INVITATION_LIST_MODEL, NULL);
	priv = model->priv;

	/* Get a connection to use for polling for invites */
	priv->uri = g_strdup (uristr);

	priv->calid = sunone_util_get_calid_from_uri (uristr);
	if (!priv->calid) {
		g_object_unref (G_OBJECT (model));
		return NULL;
	}

	model->account = account;

	g_idle_add (first_poll_cb, model);
	
	return model;
}

ECalComponent *
sunone_invitation_list_model_get_comp (SunOneInvitationListModel *model, int row)
{
	SunOneInvitationListModelPrivate *priv = model->priv;
	SunOneInvitationData *id;

	id = g_list_nth_data (priv->invitations_list, row);
	if (!id)
		return NULL;
	
	return id->comp;
}
