/*
 * 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)
 *
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include <glib/gi18n.h>
#include "mail-conv-view.h"
#include <gdk/gdkkeysyms.h>


G_DEFINE_TYPE (MailConvView, mail_conv_view, GTK_TYPE_VBOX)

enum {
	 MESSAGE_SHOWN, 
	VIEW_CLOSE,
	LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };
	
struct  _MailConvViewPrivate {
	GtkWidget *viewport;
	GList *child;
	GList  *selected_child;
	MailMessageView **load_chain;
	gint position;
	gboolean once;
	MailMessageView *last_child;
	char *folder_uri;
	GtkWidget *tab_label;
};

static void
mail_conv_view_init (MailConvView  *shell)
{
	shell->priv = g_new0(MailConvViewPrivate, 1);
	shell->uri = "message://";
	shell->type = -1;
	shell->priv->once = FALSE;
	shell->priv->load_chain = NULL;
	shell->priv->position = -1;
		 
}

static void
mail_conv_view_finalize (GObject *object)
{
	MailConvView *shell = (MailConvView *)object;
	MailConvViewPrivate *priv = shell->priv;

	g_free (priv->folder_uri);
	g_free (priv);
	
	G_OBJECT_CLASS (mail_conv_view_parent_class)->finalize (object);
}

static gboolean
mcv_focus (GtkWidget        *widget,
		     GtkDirectionType  direction)
{
	MailConvView *shell = (MailConvView *)widget;
	MailConvViewPrivate *priv = shell->priv;
	
	if (priv->child)
		gtk_widget_grab_focus (priv->child->data);
	return TRUE;
}

static gboolean
mcv_key_press (GtkWidget  *widget, GdkEventKey *event)
{
	MailConvView *shell = (MailConvView *)widget;
	MailConvViewPrivate *priv = shell->priv;

	switch (event->keyval) {
/*	case GDK_KP_Right:
	case GDK_Right:
	case GDK_Left:
	case GDK_KP_Left:
	case GDK_KP_Enter:
	{
		gboolean ret;
		g_signal_emit_by_name (priv->selected_child->data, "key-press-event", event, &ret);
		return TRUE;
	}*/
	case GDK_Page_Up:
	case GDK_KP_Page_Up:
		while (priv->selected_child->prev)
			priv->selected_child = priv->selected_child->prev;
		break;		
	case GDK_Page_Down:
	case GDK_KP_Page_Down:
		while (priv->selected_child->next)
			priv->selected_child = priv->selected_child->next;
		break;
	case GDK_Down:
	case GDK_KP_Down:
		if (!(event->state & GDK_SHIFT_MASK)) {
			priv->selected_child = priv->selected_child->next ?
				priv->selected_child->next : priv->selected_child;
		} else {
			// Scroll by small steps
			GtkAdjustment *adj = gtk_viewport_get_vadjustment ((GtkViewport *)shell->priv->viewport);
			gdouble step = gtk_adjustment_get_step_increment (adj);
			gdouble new = gtk_adjustment_get_value (adj) + step;
			gdouble max = gtk_adjustment_get_upper (adj);
			gtk_adjustment_set_value (adj, new > max ? max : new);
		}
		break;
	case GDK_Up:
	case GDK_KP_Up:
		if (!(event->state & GDK_SHIFT_MASK)) {
			priv->selected_child = priv->selected_child->prev ?
				priv->selected_child->prev : priv->selected_child;
		} else {
			// Scroll by small steps
			GtkAdjustment *adj = gtk_viewport_get_vadjustment ((GtkViewport *)shell->priv->viewport);
			gdouble step = gtk_adjustment_get_step_increment (adj);
			gdouble new = gtk_adjustment_get_value (adj) - step;
			gdouble min = gtk_adjustment_get_lower (adj);
			gtk_adjustment_set_value (adj, new < min ? min : new);
		}
		break;
	case GDK_space:
	case GDK_KP_Space:
	{
		GtkAdjustment *adj = gtk_viewport_get_vadjustment ((GtkViewport *)shell->priv->viewport);
		gdouble page_inc  = gtk_adjustment_get_page_increment (adj);
		gdouble page_size = gtk_adjustment_get_upper (adj);
		gdouble val = gtk_adjustment_get_value(adj);
		gdouble size = gtk_adjustment_get_page_size (adj);

		if (!(event->state & GDK_SHIFT_MASK)) {
			if (val+page_inc  > page_size-size)
				gtk_adjustment_set_value (adj, page_size-size);
			else
				gtk_adjustment_set_value (adj, val + page_inc);
		} else {
			if (val - page_inc < adj->lower)
				gtk_adjustment_set_value (adj, adj->lower);
			else
				gtk_adjustment_set_value (adj, val - page_inc);			
		}
		
		return TRUE;
	}
	default:
		return FALSE;
	};

	gtk_widget_grab_focus (priv->selected_child->data);
	return TRUE;
}

static void
mail_conv_view_class_init (MailConvViewClass *klass)
{
	GObjectClass * object_class = G_OBJECT_CLASS (klass);
	GtkWidgetClass *  widget_class = (GtkWidgetClass *) klass;

	signals[VIEW_CLOSE] =
		g_signal_new ("view-close",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (MailConvViewClass , view_close),
			      NULL, NULL,
			      g_cclosure_marshal_VOID__VOID,
			      G_TYPE_NONE, 0);
	signals[MESSAGE_SHOWN] =
		g_signal_new ("message-shown",
			      G_OBJECT_CLASS_TYPE (object_class),
			      G_SIGNAL_RUN_FIRST,
			      G_STRUCT_OFFSET (MailConvViewClass , message_shown),
			      NULL, NULL,
			      g_cclosure_marshal_VOID__VOID,
			      G_TYPE_NONE, 0);
	
	mail_conv_view_parent_class = g_type_class_peek_parent (klass);
	object_class->finalize = mail_conv_view_finalize;
	widget_class->focus = mcv_focus;
	widget_class->key_press_event = mcv_key_press;
}

static int
mcv_sh_color_expose (GtkWidget *w, GdkEventExpose *event, const char *color)
{
	cairo_t *cr = gdk_cairo_create (w->window);
	int wid = w->allocation.width;
	int heig = w->allocation.height;
	GdkColor colr;

	gdk_color_parse ( color, &colr);
	gdk_cairo_set_source_color (cr,  &colr);
	cairo_rectangle (cr, 0, 0, wid, heig);
	cairo_fill (cr);
	
	cairo_destroy (cr);

	return FALSE;
}

static void
mcv_adjust_scroll(MailConvView *mcv, MailMessageView *mmv)
{
	int x, y, height, ret;
	GtkAdjustment *adj = gtk_viewport_get_vadjustment((GtkViewport *)mcv->priv->viewport);
	if (!((GtkContainer *)mcv->child_box)->focus_child)
		return;
	ret = gtk_widget_translate_coordinates (mcv->priv->selected_child->data, mcv->child_box, 0, 0, &x, &y);
	height = ((GtkContainer *)mcv->child_box)->focus_child->allocation.height;
	gtk_adjustment_clamp_page (adj, y + height, y+height+gtk_adjustment_get_page_increment(adj)-36);
}

static void
mcv_scroll_to_mmv (MailMessageView *mmv, MailConvView *mcv)
{
	mcv_adjust_scroll (mcv, mmv);
	g_signal_handlers_disconnect_by_func(mmv, mcv_scroll_to_mmv, mcv);
}

static int
mcv_sh_focus_expose (GtkWidget *w, GdkEventExpose *event, MailConvView *mcv)
{
	int x, y, height, ret;
	GtkAdjustment *adj = gtk_viewport_get_vadjustment((GtkViewport *)mcv->priv->viewport);

	if (!mcv->priv->once) {
		mcv->priv->once = TRUE;
		ret = gtk_widget_translate_coordinates (mcv->priv->selected_child->data, mcv->child_box, 0, 0, &x, &y);
		if (!((GtkContainer *)mcv->child_box)->focus_child)
			return FALSE;
		height = ((GtkContainer *)mcv->child_box)->focus_child->allocation.height;
		gtk_adjustment_clamp_page (adj, y, y+height);
	}

	return FALSE;	
}

static void
mcv_message_selected (MailMessageView *mmv, MailConvView *mcv)
{
	GtkWidget *focus = mail_message_view_get_focus_widget (mmv);
	GList *tmp = mcv->priv->child;

	while (tmp) {
		if (tmp->data == focus)
			break;
		tmp = tmp->next;
	}

	if (tmp)
		mcv->priv->selected_child = tmp;
	else
		g_warning ("Unable to find the selected mail in conversation\n");
}

static void
mcv_free_node (MailConvView *mcv, MailMessageView *mmv, GList *node)
{
	 if (mcv->priv->selected_child == node) {
		  GList *tmp = mcv->priv->selected_child;

		  if (tmp->next)
			   mcv->priv->selected_child = tmp->next;
		  else if (tmp->prev)
			   mcv->priv->selected_child = tmp->prev;
		  else
			   mcv->priv->selected_child = NULL; //Free mcv

		  if (mcv->priv->selected_child)
			   gtk_widget_grab_focus (mcv->priv->selected_child->data);
		  
	 }
	 gtk_widget_destroy ((GtkWidget *)mmv);
	 g_list_free_1 (node);
}

static void
mcv_message_removed (MailMessageView *mmv, MailConvView *mcv)
{
	GtkWidget *focus = mail_message_view_get_focus_widget (mmv);
	GList *tmp = mcv->priv->child, *prev;

	prev = tmp;
	if (tmp->data == focus) {
		 /* remove first node; */
		 mcv->priv->child = mcv->priv->child->next;
		 if (mcv->priv->child)
			  mcv->priv->child->prev = NULL;
		 /* Free view */
		 mcv_free_node (mcv, mmv, tmp);
		 return;
	}
	tmp = tmp->next;
	while (tmp) {
		 if (tmp->data == focus) {
			  prev->next = tmp->next;
			  if (tmp->next)
				   tmp->next->prev = prev;
			  mcv_free_node (mcv, mmv, tmp);
			  return;
		 }
		tmp = tmp->next;
	}

	if (!tmp)
		g_warning ("Unable to find the deleted  mail in conversation\n");
}


static void
mcv_message_reply (MailMessageView *mmv, MailConvView *mcv)
{
	int x, y, ret;
	GtkAdjustment *adj = gtk_viewport_get_vadjustment((GtkViewport *)mcv->priv->viewport);
	GtkWidget *focus = mail_message_view_get_focus_widget (mmv);

	if (!(mcv->priv->selected_child && mcv->priv->selected_child->data == focus))
		return;
	ret = gtk_widget_translate_coordinates (mail_message_view_get_focus_widget (mmv), mmv->comp_view, 0, 0, &x, &y);
	gtk_adjustment_clamp_page (adj, y, y+((GtkWidget *)mmv)->allocation.height);
	gtk_adjustment_set_value (adj,  y+((GtkWidget *)mmv)->allocation.height);
}

static gboolean
render_msg (MailConvView *mcv)
{
	 MailMessageView *child = mcv->priv->load_chain[mcv->priv->position];

	 mail_message_view_show_message ((MailMessageView *)child, child == mcv->priv->last_child);	 
	 return FALSE;
}
static void
mcv_msg_load_chain (MailMessageView *mmv, MailConvView *mcv)
{
	 g_signal_handlers_disconnect_by_func(mmv, mcv_msg_load_chain, mcv);

	 mcv->priv->position++;
	 if (mcv->priv->load_chain[mcv->priv->position]) {
		  MailMessageView *child = mcv->priv->load_chain[mcv->priv->position];
		  //  g_signal_connect (child, "message-rendered", G_CALLBACK(mcv_msg_load_chain), mcv);
		  //  mail_message_view_show_message ((MailMessageView *)child, FALSE);
		  g_signal_connect (child, "message-rendered", G_CALLBACK(mcv_msg_load_chain), mcv);
		  g_timeout_add (20, (GSourceFunc) render_msg, mcv);
	 }
}

void
mail_conv_view_set_thread (MailConvView *mcv, CamelFolder *folder, const char *uri, GPtrArray *array)
{
	GtkWidget *tmp = NULL;
	int i, j;
	GList *focus = NULL;
	extern char *scolor_bg_norm;
	GtkWidget *unread = NULL;
	gboolean last_mail_shown = FALSE;
	
	mcv->priv->folder_uri = g_strdup(uri);

	if (mcv->child_box) {
		tmp = mcv->child_box;
		gtk_widget_hide (tmp);
		gtk_container_remove ((GtkContainer *)mcv->priv->viewport, tmp);
	}
	mcv->child_box = gtk_vbox_new (FALSE, 0);
	mcv->priv->once = FALSE;
	gtk_container_set_focus_vadjustment ((GtkContainer *)mcv->child_box, gtk_viewport_get_vadjustment ((GtkViewport *)mcv->priv->viewport));
	gtk_widget_show (mcv->child_box);
	g_signal_connect (mcv->child_box, "expose-event",
					  G_CALLBACK (mcv_sh_color_expose),
					  scolor_bg_norm);

	gtk_container_add ((GtkContainer *)mcv->priv->viewport, mcv->child_box);
	mcv->priv->position = 0;
	mcv->priv->load_chain = g_malloc0(sizeof (gpointer) * (array->len+1));
	if (array->len) {
		CamelMessageInfo *mi = camel_folder_summary_uid (folder->summary, array->pdata[0]);
		gtk_label_set_text ((GtkLabel *)mcv->priv->tab_label, camel_message_info_subject(mi));
		gtk_label_set_max_width_chars ((GtkLabel *)mcv->priv->tab_label, 15);
		gtk_label_set_ellipsize ((GtkLabel *)mcv->priv->tab_label, PANGO_ELLIPSIZE_END);
		gtk_widget_set_tooltip_text (mcv->priv->tab_label, camel_message_info_subject(mi));
		camel_message_info_free (mi);
	}
	for (i=0, j=0; i<array->len; i++) {
		GtkWidget *child;
		
		child = (GtkWidget *)mail_message_view_new ();
		gtk_box_pack_start ((GtkBox *)mcv->child_box, child, FALSE, FALSE, 4);
		gtk_widget_show (child);
		
		mail_message_view_set_message ((MailMessageView *)child, folder, uri, array->pdata[i], i == (array->len -1));
		g_signal_connect (child, "message-selected", G_CALLBACK(mcv_message_selected), mcv);
		g_signal_connect (child, "message-removed", G_CALLBACK(mcv_message_removed), mcv);
		g_signal_connect (child, "message-reply", G_CALLBACK(mcv_message_reply), mcv);
		focus = g_list_prepend (focus, (gpointer)mail_message_view_get_focus_widget((MailMessageView *)child));
		
		if (mail_message_view_get_unread ((MailMessageView *)child)) {
			 mcv->priv->load_chain[j] = (MailMessageView *)child;
			 j++;
		}
		if (!unread) {
			if (mail_message_view_get_unread ((MailMessageView *)child)) {
				unread = mail_message_view_get_focus_widget((MailMessageView *)child);
				g_signal_connect (child, "message-loaded", G_CALLBACK(mcv_scroll_to_mmv), mcv);
			}
		}

		if (i == array->len -1) {
			mcv->priv->last_child = (MailMessageView *)child;
			 if (!unread) {
				  unread = mail_message_view_get_focus_widget((MailMessageView *)child);
				  g_signal_connect (child, "message-loaded", G_CALLBACK(mcv_scroll_to_mmv), mcv);
				  last_mail_shown = TRUE;
				  mail_message_view_show_message ((MailMessageView *)child, TRUE);
			 } else {
			 	if (!mail_message_view_get_unread ((MailMessageView *)child)) {
					mcv->priv->load_chain[j] = (MailMessageView *)child;
			 		j++;
			 	}
				mcv->priv->load_chain[j] = NULL;
			 }
		}
	}
	
	focus = g_list_reverse (focus);
	mcv->priv->child = focus;

	/* Select first child by default */
	mcv->priv->selected_child = focus;
	if (unread) {
		g_signal_connect (mcv->child_box, "expose-event",
					  G_CALLBACK (mcv_sh_focus_expose),
					  mcv);
		mcv->priv->selected_child = g_list_find (focus, unread);
		gtk_widget_grab_focus (unread);
	} else				
		gtk_widget_grab_focus (mcv->priv->child->data);

	//g_signal_emit (mcv, signals[MESSAGE_SHOWN], 0);

	/* Start showing the message */
	if (!last_mail_shown) {
		 mcv->priv->position = 0;
		 g_signal_connect (mcv->priv->load_chain[mcv->priv->position], "message-rendered", G_CALLBACK(mcv_msg_load_chain), mcv);
		 mail_message_view_show_message ((MailMessageView *)mcv->priv->load_chain[mcv->priv->position], FALSE);
	} else {
		 g_free (mcv->priv->load_chain);
		 mcv->priv->load_chain = NULL;
		 mcv->priv->position = -1;
	}	
}

static gboolean
mcv_scroll_key_press (GtkWidget *w, GdkEventKey *event, MailConvView *mcv)
{
	switch (event->keyval) {
	case GDK_Page_Up:
	case GDK_KP_Page_Up:
	case GDK_Page_Down:
	case GDK_KP_Page_Down:
	case GDK_Home:
	case GDK_KP_Home:
	case GDK_End:	
	case GDK_KP_End:	
		mcv_key_press ((GtkWidget *)mcv, event);
		return TRUE;
	default:
		break;
	}
	return FALSE;
}

void
mail_conv_view_construct (MailConvView *mcview)
{
	MailConvViewPrivate *priv = mcview->priv;
	
	mcview->child_box = NULL;

	mcview->header = gtk_hbox_new (FALSE, 0);
	mcview->scroller = gtk_scrolled_window_new (NULL, NULL);
	g_signal_connect (mcview->scroller, "key-press-event", G_CALLBACK(mcv_scroll_key_press), mcview);
	
	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (mcview->scroller),
			GTK_SHADOW_IN);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (mcview->scroller),
			GTK_POLICY_AUTOMATIC,
			GTK_POLICY_AUTOMATIC);
	priv->viewport = gtk_viewport_new (gtk_scrolled_window_get_hadjustment((GtkScrolledWindow *)mcview->scroller), gtk_scrolled_window_get_vadjustment((GtkScrolledWindow *)mcview->scroller));
	gtk_viewport_set_shadow_type ((GtkViewport *)priv->viewport, GTK_SHADOW_NONE);
	gtk_container_add ((GtkContainer *)mcview->scroller, priv->viewport);
	gtk_widget_show (priv->viewport);
	gtk_box_pack_start ((GtkBox *)mcview, mcview->scroller, TRUE, TRUE, 0);
	gtk_widget_show (mcview->scroller);

		
	/* Set this to the name of the sender */
}

MailConvView *
mail_conv_view_new ()
{
	MailConvView *mcview = g_object_new (MAIL_CONV_VIEW_TYPE, NULL);
	mail_conv_view_construct (mcview);

	return mcview;
}

void
mail_conv_view_activate (MailConvView *mcv, GtkWidget *tree, GtkWidget *folder_tree, GtkWidget *check_mail, GtkWidget *sort_by, gboolean act)
{
	 if (!folder_tree || !check_mail || !sort_by)
		  return;
	 //gtk_widget_hide (folder_tree);
	 //g_signal_emit (mcv, signals[MESSAGE_SHOWN], 0);
	 gtk_widget_set_sensitive (check_mail, act);
	 gtk_widget_set_sensitive (sort_by, act);
}

static gboolean
mcv_btn_expose (GtkWidget *w, GdkEventExpose *event, MailConvView *mfv)
{
	GdkPixbuf *img = g_object_get_data ((GObject *)w, "pbuf");
	cairo_t *cr = gdk_cairo_create (w->window);
	cairo_save (cr);
	gdk_cairo_set_source_pixbuf (cr, img, event->area.x-5, event->area.y-4);
	cairo_paint(cr);
	cairo_restore(cr);
	cairo_destroy (cr);

	return TRUE;
}

static void
mcv_close (GtkButton *w, MailConvView *mfv)
{
	g_signal_emit (mfv, signals[VIEW_CLOSE], 0);			
}

GtkWidget *
mail_conv_view_get_tab_widget(MailConvView *mcv)
{
	GdkPixbuf *pbuf = gtk_widget_render_icon ((GtkWidget *)mcv, "gtk-close", GTK_ICON_SIZE_MENU, NULL);

	GtkWidget *tool, *box, *img;
	int w=-1, h=-1;
	GtkWidget *tab_label;

	img = gtk_image_new_from_pixbuf (pbuf);
	g_object_set_data ((GObject *)img, "pbuf", pbuf);
	g_signal_connect (img, "expose-event", G_CALLBACK(mcv_btn_expose), mcv);
	
	tool = gtk_button_new ();
	gtk_button_set_relief((GtkButton *)tool, GTK_RELIEF_NONE);
	gtk_button_set_focus_on_click ((GtkButton *)tool, FALSE);
	gtk_widget_set_tooltip_text (tool, _("Close Tab"));
	g_signal_connect (tool, "clicked", G_CALLBACK(mcv_close), mcv);
	
	box = gtk_hbox_new (FALSE, 0);
	gtk_box_pack_start ((GtkBox *)box, img, FALSE, FALSE, 0);
	gtk_container_add ((GtkContainer *)tool, box);
	gtk_widget_show_all (tool);
	gtk_icon_size_lookup_for_settings (gtk_widget_get_settings(tool) , GTK_ICON_SIZE_MENU, &w, &h);
	gtk_widget_set_size_request (tool, w+2, h+2);

	box = gtk_label_new (_("Message"));
	tab_label = gtk_hbox_new (FALSE, 0);
	gtk_box_pack_start ((GtkBox *)tab_label, box, FALSE, FALSE, 0);
	gtk_box_pack_start ((GtkBox *)tab_label, tool, FALSE, FALSE, 0);
	gtk_widget_show_all (tab_label);
	mcv->priv->tab_label = box;
	return tab_label;
	
}
