/*
 *  Copyright (C) 2003  Ricardo Fernández Pascual
 *
 *  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, 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 details.
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <glib/gi18n.h>
#include "bookmarks-gtk-toolbar.h"
#include "bookmarks-tb-widget.h"
#include "gul-gobject-misc.h"
#include "bookmarks-dnd.h"
#include "galeon-dnd.h"
#include "galeon-marshal.h"
#include "gul-string.h"
#include "bookmarks-context-menu.h"
#include "bookmarks-gtk-menu-item.h"
#include "galeon-debug.h"

#include <gtk/gtkseparatortoolitem.h>
#include <gtk/gtkseparatormenuitem.h>
#include <gtk/gtktoolbutton.h>

/**
 * Private data
 */
struct _GbGtkToolbarPrivate {
	GbFolder *root;
	guint rebuild_timeout;

	GbLocationSource *location_source;
	GSList *toolbaritems;

	GtkToolItem *drag_item;
};

#define REBUILD_TIMEOUT 700

/**
 * Private functions, only availble from this file
 */
static void		gb_gtk_toolbar_class_init		(GbGtkToolbarClass *klass);
static void		gb_gtk_toolbar_init			(GbGtkToolbar *e);
static void		gb_gtk_toolbar_finalize_impl		(GObject *o);
static void		gb_gtk_toolbar_build			(GbGtkToolbar *gm);
static void		gb_gtk_toolbar_build_bookmark		(GbGtkToolbar *gm, GbBookmark *item, guint index);

static void		gb_gtk_toolbar_child_added_cb		(GbFolder *p, GbBookmark *c, 
								 gint position, GbGtkToolbar *gm);
static void		gb_gtk_toolbar_child_removed_cb	 	(GbFolder *p, GbBookmark *c, 
								 gint position, GbGtkToolbar *gm);
static void		gb_gtk_toolbar_rebuild			(GbGtkToolbar *gm);
static void		gb_gtk_toolbar_bookmark_activated_cb	(GObject *sender,
								 GbBookmarkEventActivated *ev,
								 GbGtkToolbar *gm);

static gpointer parent_class;

enum GbGtkToolbarSignalsEnum {
	GB_GTK_TOOLBAR_BOOKMARK_ACTIVATED,
	GB_GTK_TOOLBAR_LAST_SIGNAL
};
static gint GbGtkToolbarSignals[GB_GTK_TOOLBAR_LAST_SIGNAL];

/**
 * GtkToolbar object
 */

MAKE_GET_TYPE (gb_gtk_toolbar, "GbGtkToolbar", GbGtkToolbar, gb_gtk_toolbar_class_init,
	       gb_gtk_toolbar_init, GTK_TYPE_TOOLBAR);

static void
gb_gtk_toolbar_class_init (GbGtkToolbarClass *klass)
{
	G_OBJECT_CLASS (klass)->finalize = gb_gtk_toolbar_finalize_impl;
	parent_class = g_type_class_peek_parent (klass);

	GbGtkToolbarSignals[GB_GTK_TOOLBAR_BOOKMARK_ACTIVATED] = g_signal_new (
		"bookmark-activated", G_OBJECT_CLASS_TYPE (klass),  
		G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP,
                G_STRUCT_OFFSET (GbGtkToolbarClass, gb_gtk_toolbar_bookmark_activated), 
		NULL, NULL, 
		g_cclosure_marshal_VOID__POINTER,
		G_TYPE_NONE, 1, G_TYPE_POINTER);
}

static gboolean
gb_gtk_toolbar_button_press_cb (GtkWidget *wid, GdkEventButton *event, 
				GbGtkToolbar *gtb)
{
	GbGtkToolbarPrivate *p = gtb->priv;
	g_return_val_if_fail (GB_IS_BOOKMARK (p->root), FALSE);

	if (event->button == 3)
	{
		gb_context_menu_quick (GB_BOOKMARK (p->root), event, p->location_source, G_OBJECT (gtb));
		return TRUE;
	}
	/* TODO: maybe paste?
	else if (event->button == 2)
	{
		const gchar *url = GB_IS_SITE (p->bm) ? GB_SITE (p->bm)->url : NULL;
		gb_activated_event (w, p->bm, url, GB_BAF_NEW_TAB_OR_WINDOW, (GdkEvent *) event);
		return TRUE;
	}
	*/
	return FALSE;
}

static void 
gb_gtk_toolbar_init (GbGtkToolbar *m)
{
	GbGtkToolbarPrivate *p = g_new0 (GbGtkToolbarPrivate, 1);
	m->priv = p;

	gtk_toolbar_set_show_arrow (GTK_TOOLBAR (m), TRUE);
	gtk_toolbar_set_style (GTK_TOOLBAR (m), GTK_TOOLBAR_TEXT);

	g_signal_connect (m, "button_press_event",
			  G_CALLBACK (gb_gtk_toolbar_button_press_cb), m);
}

static void
gb_gtk_toolbar_finalize_impl (GObject *o)
{
	GbGtkToolbar *gm = GB_GTK_TOOLBAR (o);
	GbGtkToolbarPrivate *p = gm->priv;
	GSList *li;

	LOG ("Finalizing GbGtkToolbar");

	gb_gtk_toolbar_set_location_source (gm, NULL);

	g_signal_handlers_disconnect_matched (p->root, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, gm);
	if (GB_BOOKMARK (p->root)->set)
	{
		g_signal_handlers_disconnect_matched (GB_BOOKMARK (p->root)->set, 
						      G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, gm);
	}

	if (p->rebuild_timeout)
	{
		g_source_remove (p->rebuild_timeout);
	}

	for (li = p->toolbaritems; li; li = li->next)
	{
		g_object_unref (li->data);
	}
	g_slist_free (p->toolbaritems);

	g_object_unref (G_OBJECT (p->root));
	g_free (p);

	G_OBJECT_CLASS (parent_class)->finalize (o);
}

typedef struct
{
	GbFolder *folder;
	int pos;
} GbGtkToolbarDragDatareceivedInfo;

static void
gb_gtk_toolbar_each_url_receive_data_binder (const char * url, const char * title, gpointer context)
{
	GbGtkToolbarDragDatareceivedInfo *info = context;
	GbSite *niu;

	g_return_if_fail (GB_IS_FOLDER (info->folder));

	if (!title) title = url;

	niu = gb_site_new (GB_BOOKMARK (info->folder)->set, title, url);

	gb_folder_add_child (info->folder, GB_BOOKMARK (niu), info->pos);
	g_object_unref (niu);

	info->pos++;
}



static void
gb_gtk_toolbar_drag_data_received_cb (GtkWidget *widget,
				      GdkDragContext *dc,
				      gint x, gint y,
				      GtkSelectionData *selection_data,
				      guint info,
				      guint t,
				      GbGtkToolbar *tb)
{
	GbGtkToolbarPrivate *p = tb->priv;
	GbGtkToolbarDragDatareceivedInfo aux;
	aux.pos = gtk_toolbar_get_drop_index (GTK_TOOLBAR (widget), x, y);
	aux.folder = p->root;
	
	galeon_dnd_drag_data_receive (widget, dc, x, y, selection_data,
				      info, t, &aux, gb_gtk_toolbar_each_url_receive_data_binder);

	/* Rebuild immediately to avoid nasty sliding artifacts */
	gb_gtk_toolbar_rebuild (GB_GTK_TOOLBAR (widget));
}
	

static gboolean
toolbar_drag_motion (GbGtkToolbar *toolbar,
		     GdkDragContext *context,
		     gint            x,
		     gint            y,
		     guint           time,
		     gpointer        null)
{
	gint index;
	LOG ("toolbar_drag_motion");

	if( !gtk_drag_dest_find_target (GTK_WIDGET (toolbar), context, NULL))
	{
		return FALSE;
	}

	if (!toolbar->priv->drag_item)
	{
		/* This item is just used to add some padding for visual 
		 * feedback */
		toolbar->priv->drag_item = gtk_tool_button_new (NULL, "m");
		g_object_ref (G_OBJECT (toolbar->priv->drag_item));
		gtk_object_sink (GTK_OBJECT (toolbar->priv->drag_item));
	}
  
	gdk_drag_status (context, context->suggested_action, time);

	index = gtk_toolbar_get_drop_index (GTK_TOOLBAR (toolbar), x, y);
	gtk_toolbar_set_drop_highlight_item (GTK_TOOLBAR (toolbar), 
					     toolbar->priv->drag_item, index);

	return TRUE;
}

static void
toolbar_drag_leave (GbGtkToolbar   *toolbar,
		    GdkDragContext *context,
		    guint           time,
		    gpointer	    null)
{
	LOG ("toolbar_drag_leave");
	if (toolbar->priv->drag_item)
	{
		g_object_unref (G_OBJECT (toolbar->priv->drag_item));
		toolbar->priv->drag_item = NULL;
	}
	
	gtk_toolbar_set_drop_highlight_item (GTK_TOOLBAR (toolbar), NULL, 0);
}

GbGtkToolbar *
gb_gtk_toolbar_new (GbFolder *root)
{
	GbGtkToolbar *ret = g_object_new (GB_TYPE_GTK_TOOLBAR, NULL);
	GbGtkToolbarPrivate *p = ret->priv;

	p->root = root;
	g_object_ref (root);

	g_signal_connect (root, "child-added", 
			  G_CALLBACK (gb_gtk_toolbar_child_added_cb), ret);
	g_signal_connect (root, "child-removed", 
			  G_CALLBACK (gb_gtk_toolbar_child_removed_cb), ret);

	p->toolbaritems = NULL;
	gb_gtk_toolbar_rebuild (ret);

	galeon_dnd_url_drag_dest_set_with_flags (GTK_WIDGET (ret), GTK_DEST_DEFAULT_DROP);

	g_signal_connect (ret, "drag_data_received",
			  G_CALLBACK (gb_gtk_toolbar_drag_data_received_cb), ret);

	g_signal_connect (ret, "drag_motion",
			  G_CALLBACK (toolbar_drag_motion), ret);
	g_signal_connect (ret, "drag_leave", 
			  G_CALLBACK (toolbar_drag_leave), ret);

	return ret;
}

static gboolean
gb_gtk_toolbar_create_menu_proxy_cb (GtkToolItem *item, GbGtkToolbar *gtb)
{
	GbGtkToolbarPrivate *p = gtb->priv;
	GtkWidget *proxy;
	if (GTK_IS_SEPARATOR_TOOL_ITEM (item))
	{
		proxy = gtk_separator_menu_item_new ();
	}
	else
	{
		GbTbWidget *tbw = GB_TB_WIDGET (GTK_BIN (item)->child);
		GbBookmark *b = gb_tb_widget_get_bookmark (tbw);
		proxy = GTK_WIDGET (gb_gtk_menu_item_new (b));
		gb_gtk_menu_item_set_location_source (GB_GTK_MENU_ITEM (proxy), p->location_source);
		g_signal_connect (proxy, "bookmark-activated", 
				  G_CALLBACK (gb_gtk_toolbar_bookmark_activated_cb), gtb);
	}
	gtk_tool_item_set_proxy_menu_item (item, "bookmark-menu-item-proxy", GTK_WIDGET (proxy));
	return TRUE;
}

static void
gb_gtk_toolbar_build_separator (GbGtkToolbar *gm, guint index)
{
	GbGtkToolbarPrivate *p = gm->priv;
	GtkToolItem *sep = gtk_separator_tool_item_new ();
	g_signal_connect (sep, "create_menu_proxy",
			  G_CALLBACK (gb_gtk_toolbar_create_menu_proxy_cb), gm);
	gtk_widget_show (GTK_WIDGET (sep));
	p->toolbaritems = g_slist_prepend (p->toolbaritems, g_object_ref (sep));
	gtk_toolbar_insert (GTK_TOOLBAR (gm), sep, index);
}


static void
gb_gtk_toolbar_build_bookmark (GbGtkToolbar *gm, GbBookmark *item, guint index)
{
	GbGtkToolbarPrivate *p = gm->priv;
	GbTbWidget *w = gb_bookmark_create_toolbar_widget (item);
	GtkToolItem *ti = gtk_tool_item_new ();
	gb_tb_widget_set_location_source (w, p->location_source);
	g_signal_connect (w, "bookmark-activated", 
			  G_CALLBACK (gb_gtk_toolbar_bookmark_activated_cb), gm);

	p->toolbaritems = g_slist_prepend (p->toolbaritems, g_object_ref (w));

	gtk_widget_show (GTK_WIDGET (w));
	
	gtk_container_add (GTK_CONTAINER (ti), GTK_WIDGET (w));
	gtk_widget_show (GTK_WIDGET (ti));

	g_signal_connect (ti, "create_menu_proxy",
			  G_CALLBACK (gb_gtk_toolbar_create_menu_proxy_cb), gm);

	gtk_toolbar_insert (GTK_TOOLBAR (gm), ti, index);
}


static void
gb_gtk_toolbar_build (GbGtkToolbar *gm)
{
	GbGtkToolbarPrivate *p = gm->priv;
	guint index = 0;
	GSList *items;
	GSList *li;

	items = gb_folder_list_children (p->root);

	for (li = items; li != NULL; li = li->next)
	{
		GbBookmark *i = li->data;
		
		if  (GB_IS_FOLDER (i) || GB_IS_SITE (i))
		{
			gb_gtk_toolbar_build_bookmark (gm, i, index);
		}
		else if (GB_IS_SEPARATOR (i))
		{
			gb_gtk_toolbar_build_separator (gm, index);
		}
		else
		{
			g_warning ("something skipped when building bookmarks toolbar");
		}
		index++;
	}

	g_slist_free (items);
}

void
gb_gtk_toolbar_set_location_source (GbGtkToolbar *gm, GbLocationSource *src)
{
	GbGtkToolbarPrivate *p = gm->priv;
	GSList *li;

	if (p->location_source)
	{
		g_object_remove_weak_pointer (G_OBJECT (p->location_source),
					      (gpointer *) &p->location_source);
	}

	p->location_source = src;

	if (p->location_source)
	{
		g_object_add_weak_pointer (G_OBJECT (p->location_source), 
					   (gpointer *) &p->location_source);
	}

	for (li = p->toolbaritems; li; li = li->next)
	{
		if (GB_IS_TB_WIDGET (li->data))
		{
			gb_tb_widget_set_location_source (li->data, src);
		}
	}
}

static gboolean
gb_gtk_toolbar_rebuild_idle_cb (gpointer data)
{
	GbGtkToolbar *gm = data;
	GbGtkToolbarPrivate *p = gm->priv;
	LOG ("GbGtkToolbar rebuild idle");

	p->rebuild_timeout = 0;

	gb_gtk_toolbar_rebuild (data);
	return FALSE;
}

static void 
gb_gtk_toolbar_rebuild_when_idle (GbGtkToolbar *gm) 
{
	GbGtkToolbarPrivate *p = gm->priv;

	if (p->rebuild_timeout)
	{
		g_source_remove (p->rebuild_timeout);
	}
	
	p->rebuild_timeout = g_idle_add (gb_gtk_toolbar_rebuild_idle_cb, gm);
}

static void
gb_gtk_toolbar_rebuild (GbGtkToolbar *gm)
{
	GbGtkToolbarPrivate *p = gm->priv;
	GSList *sli;

	LOG ("gb_gtk_toolbar_rebuild called");
	
	/* this could be more fine grained. For now, just destroy everything and 
	   build it again */
	
	/* remove already created toolbaritems*/
	for (sli = p->toolbaritems; sli; sli = sli->next)
	{
		GtkWidget *toolitem = GTK_WIDGET (sli->data);

		/* this is a bit ugly... */
		if (!GTK_IS_SEPARATOR_TOOL_ITEM (sli->data))
		{
			toolitem = toolitem->parent;
		}
		gtk_container_remove (GTK_CONTAINER (gm), toolitem);
	}

	/* free the list of toolbaritems */
	for (sli = p->toolbaritems; sli; sli = sli->next)
	{
		g_object_unref (sli->data);
	}
	g_slist_free (p->toolbaritems);
	p->toolbaritems = NULL;

	gb_gtk_toolbar_build (gm);
}

static void
gb_gtk_toolbar_child_added_cb (GbFolder *p, GbBookmark *c, gint position, GbGtkToolbar *gm)
{
	gb_gtk_toolbar_rebuild_when_idle (gm);
}

static void
gb_gtk_toolbar_child_removed_cb (GbFolder *p, GbBookmark *c, gint position, GbGtkToolbar *gm)
{
	gb_gtk_toolbar_rebuild_when_idle (gm);
}

static void
gb_gtk_toolbar_bookmark_activated_cb (GObject *sender,
				   GbBookmarkEventActivated *ev,
				   GbGtkToolbar *gm)
{
	g_signal_emit (gm, GbGtkToolbarSignals[GB_GTK_TOOLBAR_BOOKMARK_ACTIVATED], 0, ev);
}

