/* sunone-invitation-list.c
 *
 * Copyright (C) 2002-2004 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 <bonobo/bonobo-stream-memory.h>
#include <bonobo/bonobo-widget.h>
#include <libgnomevfs/gnome-vfs.h>
#include <libsoup/soup.h>
#include <gtk/gtk.h>
#include <libecal/e-cal.h>
#include <libecal/e-cal-component.h>
#include <libecal/e-cal-time-util.h>
#include <libedataserver/e-time-utils.h>
#include <misc/e-cell-renderer-combo.h>
#include "lib/sunone-util.h"
#include "evolution-calendar.h"
#include "sunone-account.h"
#include "sunone-invitation-list.h"
#include "sunone-itip-view.h"

#define INVITATION_CONTROL_IID "OAFIID:GNOME_Evolution_Calendar_iTip_Control:" EVOLUTION_BASE_VERSION
#define COMPEDITORFACTORY "OAFIID:GNOME_Evolution_Calendar_CompEditorFactory:" EVOLUTION_BASE_VERSION

#define USE_ITIP_VIEW 0

struct _SunOneInvitationListPrivate {
	EConfigListener *config;
	
	SunOneInvitationListModel *model;	
	
	GtkWidget *pane;
	GtkTreeView *treeview;
	GtkWidget *control;
};

extern char *evolution_dir;

static void sunone_invitation_list_class_init (SunOneInvitationListClass *klass);
static void sunone_invitation_list_init (SunOneInvitationList *object);
static void sunone_invitation_list_destroy (GtkObject *object);

static GObjectClass *parent_class = NULL;

static void
sunone_invitation_list_class_init (SunOneInvitationListClass *klass)
{
	GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);

	parent_class = g_type_class_ref (GTK_TYPE_VBOX);
	
	object_class->destroy = sunone_invitation_list_destroy;
}

static void
sunone_invitation_list_init (SunOneInvitationList *object)
{
	SunOneInvitationList *list = SUNONE_INVITATION_LIST (object);
	SunOneInvitationListPrivate *priv;
	GtkWidget *scroll;
	int pos;
	
	list->priv = g_new0 (SunOneInvitationListPrivate, 1);
	priv = list->priv;

	priv->config = e_config_listener_new ();
	priv->model = NULL;
	priv->treeview = NULL;
	priv->pane = gtk_vpaned_new ();

	gtk_box_set_homogeneous (GTK_BOX (list), FALSE);
	
	scroll = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), 
					GTK_POLICY_AUTOMATIC, 
					GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), 
					GTK_SHADOW_IN);
#if USE_ITIP_VIEW
	priv->control = sunone_itip_view_new ();
#else
	priv->control = bonobo_widget_new_control (INVITATION_CONTROL_IID, NULL);
#endif
	gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll),
						priv->control);
	
	gtk_paned_add2 (GTK_PANED (priv->pane), GTK_WIDGET (scroll));

	pos = e_config_listener_get_long_with_default (priv->config, "/apps/evolution/evolution-jescs/invitationList/panePosition", 100, NULL);
	gtk_paned_set_position (GTK_PANED (priv->pane), pos);

	gtk_box_pack_start (GTK_BOX (list), priv->pane, TRUE, TRUE, 0);
}

static void
sunone_invitation_list_destroy (GtkObject *object)
{
	SunOneInvitationList *list = SUNONE_INVITATION_LIST (object);
	SunOneInvitationListPrivate *priv = list->priv;

	g_return_if_fail (IS_SUNONE_INVITATION_LIST (list));

	if (priv) {
		if (priv->config) {
			int pos = gtk_paned_get_position (GTK_PANED (priv->pane));
			
			e_config_listener_set_long (priv->config, "/apps/evolution/evolution-jescs/invitationList/panePosition", pos);
			g_object_unref (G_OBJECT (priv->config));
			priv->config = NULL;
		}
		if (priv->model) {
			/* Don't listen to model updates any more */
			g_object_unref (G_OBJECT (priv->model));
			priv->model = NULL;
		}

		if (priv->treeview) {
			priv->treeview = NULL;
		}

#if USE_ITIP_VIEW
		if (priv->control) {
			g_object_unref (priv->control);
		}
#endif
		g_free (priv);
		list->priv = NULL;
	}

	if ( GTK_OBJECT_CLASS (parent_class)->destroy)
		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}

G_DEFINE_TYPE (SunOneInvitationList, sunone_invitation_list, GTK_TYPE_VBOX)

static icalcomponent *
toplevel_with_zones (SunOneInvitationList *list, ECalComponent *comp)
{
	icalcomponent *top_level, *icomp;
	icalproperty *prop;
	icalvalue *value;

	top_level = e_cal_util_new_top_level ();

	prop = icalproperty_new (ICAL_METHOD_PROPERTY);
	value = icalvalue_new_method (ICAL_METHOD_REQUEST);
	icalproperty_set_value (prop, value);
	icalcomponent_add_property (top_level, prop);

	icomp = e_cal_component_get_icalcomponent (comp);
	icomp = icalcomponent_new_clone (icomp);

	icalcomponent_add_component (top_level, icomp);

	return top_level;
}

static void
set_data (BonoboWidget *control, const char *string)
{
	Bonobo_PersistStream persist;
	BonoboObject *bstream;

	CORBA_Environment ev;

	persist = (Bonobo_PersistStream) Bonobo_Unknown_queryInterface (
		       bonobo_widget_get_objref (control), 
		       "IDL:Bonobo/PersistStream:1.0", &ev);
			
	if (persist == CORBA_OBJECT_NIL) {
			g_object_ref_sink (G_OBJECT (control));
			return ;
	}

	bstream = bonobo_stream_mem_create (string, strlen (string), TRUE, FALSE);

	CORBA_exception_init (&ev);
	Bonobo_PersistStream_load (persist, BONOBO_OBJREF (bstream),
				   "text/calendar", &ev);
	bonobo_object_unref (BONOBO_OBJECT (bstream));
	Bonobo_Unknown_unref (persist, &ev);
	CORBA_Object_release (persist, &ev);
}

static void
set_data_and_props (SunOneInvitationList *list, int row) 
{
	SunOneInvitationListPrivate *priv = list->priv;
	BonoboControlFrame *control_frame;
	Bonobo_PropertyBag prop_bag;
	ECalComponent *comp;
	icalcomponent *top_level;
	char *string;
	CORBA_Environment ev;

	if (row == -1) {
		set_data (BONOBO_WIDGET (priv->control), "");
		return;
	}
		
	comp = sunone_invitation_list_model_get_comp (priv->model, row);
	if (!comp)
		return;

	/* Set the from address on the control */
	control_frame = bonobo_widget_get_control_frame (BONOBO_WIDGET (priv->control));
	prop_bag = bonobo_control_frame_get_control_property_bag (control_frame, NULL);		
	if (prop_bag != CORBA_OBJECT_NIL){
		ECalComponentOrganizer organizer;

		CORBA_exception_init (&ev);
		
		e_cal_component_get_organizer (comp, &organizer);
		bonobo_property_bag_client_set_value_string (prop_bag, "from_address", 
							     organizer.value, &ev);


		CORBA_exception_init (&ev);

		bonobo_property_bag_client_set_value_gint (prop_bag, "view_only", 
							   1, &ev);

		Bonobo_Unknown_unref (prop_bag, &ev);
		CORBA_exception_free (&ev);
	}

	/* Send the icalendar code over */
	top_level = toplevel_with_zones (list, comp);
	string = icalcomponent_as_ical_string (top_level);
	set_data (BONOBO_WIDGET (priv->control), string);
	g_free (string);
	icalcomponent_free (top_level);
}

#if USE_ITIP_VIEW

static const gchar *
itip_strip_mailto (const gchar *address) 
{
	if (address == NULL)
		return NULL;
	
	if (!g_strncasecmp (address, "mailto:", 7))
		address += 7;

	return address;
}

static void
set_itip_view (SunOneInvitationList *list, int row)
{
	SunOneInvitationListPrivate *priv = list->priv;
	ECalComponent *comp;
	ECalComponentText text;
	const char *string;
	ECalComponentDateTime datetime;
	GString *gstring = NULL;
	GSList *description_list, *l;

	SunOneItipView *itip_view;
	ECalComponentOrganizer organizer;

	if (row == -1) {
		if (GTK_WIDGET_VISIBLE (priv->control))
			gtk_widget_hide (priv->control);
		return;
	}
	comp = sunone_invitation_list_model_get_comp (priv->model, row);
	if (!comp)
		return;

	itip_view  = (SunOneItipView *)priv->control;

	sunone_itip_view_set_mode (itip_view, SUNONE_ITIP_VIEW_MODE_REQUEST);
	sunone_itip_view_set_item_type (itip_view, E_CAL_SOURCE_TYPE_EVENT);

	e_cal_component_get_organizer (comp, &organizer);
	sunone_itip_view_set_organizer (itip_view, organizer.cn ? organizer.cn : itip_strip_mailto (organizer.value));
	sunone_itip_view_set_sentby (itip_view, organizer.sentby);

	e_cal_component_get_summary (comp, &text);
	sunone_itip_view_set_summary (itip_view, text.value ? text.value : _("None"));

	e_cal_component_get_location (comp, &string);
	sunone_itip_view_set_location (itip_view, string);

	e_cal_component_get_description_list (comp, &description_list);
	for (l = description_list; l; l = l->next) {
		ECalComponentText *text = l->data;
		
		if (!gstring && text->value)
			gstring = g_string_new (text->value);
		else if (text->value)
			g_string_append_printf (gstring, "\n\n%s", text->value);
	}
	e_cal_component_free_text_list (description_list);

	if (gstring) {
		sunone_itip_view_set_description (itip_view, gstring->str);
		g_string_free (gstring, TRUE);
	} else
		sunone_itip_view_set_description (itip_view, NULL);
	
	
	e_cal_component_get_dtstart (comp, &datetime);
	if (datetime.value) {
		struct tm start_tm;
		
		start_tm = icaltimetype_to_tm_with_zone (datetime.value, icaltimezone_get_utc_timezone (), priv->model->zone);

		sunone_itip_view_set_start (itip_view, &start_tm);
	}
	e_cal_component_free_datetime (&datetime);

	e_cal_component_get_dtend (comp, &datetime);
	if (datetime.value) {
		struct tm end_tm;

		end_tm = icaltimetype_to_tm_with_zone (datetime.value, icaltimezone_get_utc_timezone (), priv->model->zone);
		
		sunone_itip_view_set_end (itip_view, &end_tm);
	}
	e_cal_component_free_datetime (&datetime);

	/* Recurrence info */
	sunone_itip_view_clear_upper_info_items (itip_view);
	if (e_cal_component_has_recurrences (comp)) {
		sunone_itip_view_add_upper_info_item (itip_view, SUNONE_ITIP_VIEW_INFO_ITEM_TYPE_INFO, "This meeting recurs");
	}

	sunone_itip_view_set_status (itip_view, NULL);
	sunone_itip_view_set_comment (itip_view, NULL);
	sunone_itip_view_set_show_rsvp (itip_view, FALSE);

	if (!GTK_WIDGET_VISIBLE (priv->control))
		gtk_widget_show (priv->control);
}
#endif

static void
invitation_row_changed_cb (GtkTreeModel *model, GtkTreePath *path, 
				GtkTreeIter *iter, gpointer data)
{
	SunOneInvitationList *list = SUNONE_INVITATION_LIST (data);
	SunOneInvitationListPrivate *priv = list->priv;
	GtkTreeSelection *selection;
	GtkTreePath *select_path, *changed_path;
	GtkTreeIter select_iter;
	gint select_row, changed_row;
	
	selection = gtk_tree_view_get_selection (priv->treeview);
	if (!gtk_tree_selection_get_selected (selection, NULL, &select_iter))
		return;

	select_path = gtk_tree_model_get_path (model, &select_iter);
	select_row = gtk_tree_path_get_indices (select_path)[0];
	gtk_tree_path_free (select_path);

	changed_path = gtk_tree_model_get_path (model, iter);
	changed_row = gtk_tree_path_get_indices (changed_path)[0];
	gtk_tree_path_free (changed_path);

	if (changed_row == select_row)	
#if USE_ITIP_VIEW
		set_itip_view (list, select_row);
#else
		set_data_and_props (list, select_row);
#endif
}

static void
invitation_row_deleted_cb (GtkTreeModel *model, GtkTreePath *path, 
				gpointer data)
{
	SunOneInvitationList *list = SUNONE_INVITATION_LIST (data);
	SunOneInvitationListPrivate *priv = list->priv;
	GtkTreeSelection *selection;

	selection = gtk_tree_view_get_selection (priv->treeview);
	if (!gtk_tree_selection_get_selected (selection, NULL, NULL))
#if USE_ITIP_VIEW
		set_itip_view (list, -1);
#else
		set_data_and_props (list, -1);
#endif
}

static void
invitation_cursor_changed_cb (GtkTreeView *treeview, gpointer data)
{
	SunOneInvitationList *list = SUNONE_INVITATION_LIST (data);
	GtkTreeSelection *selection;
	GtkTreeModel *model;
	GtkTreePath *path;
	GtkTreeIter iter;
	gint row;
	
	selection = gtk_tree_view_get_selection (treeview);
	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
		return;

	path = gtk_tree_model_get_path (model, &iter);
	row = gtk_tree_path_get_indices (path)[0];
	gtk_tree_path_free (path);

#if USE_ITIP_VIEW
	set_itip_view (list, row);
#else
	set_data_and_props (list, row);
#endif
}

#if 0
static gint
invitation_double_clicked_cb (ETable *et, int row, int col, GdkEvent *event, gpointer data)
{
	SunOneInvitationList *list = SUNONE_INVITATION_LIST (data);
	SunOneInvitationListPrivate *priv = list->priv;
	ECalComponent *comp, *clone;
	GNOME_Evolution_Calendar_CompEditorFactory factory;
	const char *uid;
	CORBA_Environment ev;

	comp = sunone_invitation_list_model_get_comp (priv->model, row);
	if (!comp)
		return FALSE;

	clone = e_cal_component_clone (comp);	
	sunone_util_mangle_uid (clone);
	e_cal_component_get_uid (clone, &uid);

	/* Get the factory */
	CORBA_exception_init (&ev);
	factory = bonobo_activation_activate_from_id (COMPEDITORFACTORY, 0, NULL, &ev);
	if (BONOBO_EX (&ev)) {
		g_message ("%s: Could not activate the component editor factory (%s)", G_GNUC_FUNCTION,
			   CORBA_exception_id (&ev));
		CORBA_exception_free (&ev);
		return FALSE;
	}

	GNOME_Evolution_Calendar_CompEditorFactory_editExisting (factory, priv->model->uri, (char *)uid, GNOME_Evolution_Calendar_CompEditorFactory_EDITOR_MODE_EVENT, &ev);

	if (BONOBO_EX (&ev)) {
		g_message ("%s: Execption while editing the component (%s)", G_GNUC_FUNCTION, 
			   CORBA_exception_id (&ev));
	}

	g_object_unref (G_OBJECT (clone));
	
	CORBA_exception_free (&ev);
	bonobo_object_release_unref (factory, NULL);	

	return FALSE;
}

#endif

static void
status_edited_cb (GtkCellRenderer *renderer, const gchar *path, const gchar *text, GtkTreeView *view)
{
	SunOneInvitationListModel *model = SUNONE_INVITATION_LIST_MODEL (gtk_tree_view_get_model (view));
	GtkTreePath *treepath = gtk_tree_path_new_from_string (path);
	int row = gtk_tree_path_get_indices (treepath)[0];
       
	sunone_invitation_list_model_set_value (model, STATUS_COL, row, text);
	gtk_tree_path_free (treepath);
}

static void
setup_invitation_list (SunOneInvitationList *list)
{
	SunOneInvitationListPrivate *priv = list->priv;
	GtkWidget *scrolled_window;
	GList *strings;
	GtkCellRenderer *renderer;
	GtkTreeViewColumn *column;
	gboolean editable;

	priv->treeview = (GtkTreeView *)gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->model));
	gtk_tree_view_set_headers_visible (priv->treeview, TRUE);
	gtk_tree_view_set_rules_hint (priv->treeview, TRUE);

	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (_("Starts"),
						renderer,
						"text",
						START_COL,
						NULL);
	gtk_tree_view_column_set_resizable (column, TRUE);
	gtk_tree_view_append_column (priv->treeview, column);

	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (_("Summary"),
						renderer,
						"text",
						SUMMARY_COL,
						NULL);
	gtk_tree_view_column_set_resizable (column, TRUE);
	gtk_tree_view_append_column (priv->treeview, column);

	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (_("Organizer"),
						renderer,
						"text",
						ORGANIZER_COL,
						NULL);
	gtk_tree_view_column_set_resizable (column, TRUE);
	gtk_tree_view_append_column (priv->treeview, column);

	strings = NULL;
	strings = g_list_append (strings, (char*) _("Needs Action"));
	strings = g_list_append (strings, (char*) _("Accepted"));
	strings = g_list_append (strings, (char*) _("Declined"));
	strings = g_list_append (strings, (char*) _("Tentative"));

	renderer = e_cell_renderer_combo_new ();
	editable = sunone_util_has_permissions (priv->model->props, 
					sunone_account_get_user (priv->model->account),
					SUNONE_ACE_CONTEXT_CALENDAR_COMPONENTS, 
					SUNONE_ACE_PERMISSION_WRITE | SUNONE_ACE_PERMISSION_REPLY) ||
				sunone_util_has_permissions (priv->model->props, 
					sunone_account_get_uid (priv->model->account),
					SUNONE_ACE_CONTEXT_CALENDAR_COMPONENTS, 
					SUNONE_ACE_PERMISSION_WRITE | SUNONE_ACE_PERMISSION_REPLY);
	g_object_set (G_OBJECT (renderer), "list", strings, "editable", editable, NULL);
	gtk_tree_view_insert_column_with_attributes (priv->treeview, 
						-1, 
						_("Status"), renderer,
						"text", 
						STATUS_COL,
						NULL);
	g_signal_connect (renderer, "edited", G_CALLBACK (status_edited_cb), priv->treeview);

	scrolled_window = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy ((GtkScrolledWindow *)scrolled_window, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type ((GtkScrolledWindow *)scrolled_window, GTK_SHADOW_IN);
	gtk_scrolled_window_add_with_viewport ((GtkScrolledWindow *)scrolled_window, (GtkWidget *)priv->treeview);

	gtk_paned_add1 (GTK_PANED (priv->pane), scrolled_window);
	g_signal_connect (priv->treeview, "cursor_changed",
			    G_CALLBACK (invitation_cursor_changed_cb), list);
	g_signal_connect (priv->model, "row_deleted",
			    G_CALLBACK (invitation_row_deleted_cb), list);
	g_signal_connect (priv->model, "row_changed",
			    G_CALLBACK (invitation_row_changed_cb), list);
#if 0
	g_signal_connect (G_OBJECT (et), "double_click",
			    G_CALLBACK (invitation_double_clicked_cb), list);
#endif
}

GtkWidget *
sunone_invitation_list_new (SunOneInvitationListModel *model)
{
	SunOneInvitationList *list;
	SunOneInvitationListPrivate *priv;

	list = g_object_new (SUNONE_INVITATION_LIST_TYPE, NULL);
	priv = list->priv;

	g_object_ref (G_OBJECT (model));
	priv->model = model;

	/* Display the list of invitations */
	setup_invitation_list (list);
	gtk_widget_show_all ((GtkWidget *)list);

#if USE_ITIP_VIEW
	gtk_widget_hide (priv->control);
#endif
	
	return (GtkWidget *)list;
}

