/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2006 Hiroyuki Ikezoe
 *
 *  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.
 */

#include <string.h>
#include <glib/gi18n.h>
#include <gdk/gdkkeysyms.h>

#include "gtk-utils.h"
#include "kz-tab-label.h"
#include "kz-notebook.h"
#include "kz-actions.h"

enum {
	PROP_0,
	PROP_KZ_WINDOW
};

enum {
	TARGET_KAZEHAKASE_TAB,
	TARGET_NETSCAPE_URL,
	TARGET_TEXT_URI_LIST,
	TARGET_TEXT_PLAIN,
	TARGET_STRING
};

static GtkTargetEntry url_drag_types [] =
{
        { "_KAZEHAKASE_TAB", 0, TARGET_KAZEHAKASE_TAB},
        { "_NETSCAPE_URL",   0, TARGET_NETSCAPE_URL},
	{ "text/uri-list",   0, TARGET_TEXT_URI_LIST},
	{ "text/plain",      0, TARGET_TEXT_PLAIN},
	{ "STRING",          0, TARGET_STRING}
};

typedef struct _KzNotebookPrivate	KzNotebookPrivate;
struct _KzNotebookPrivate
{
	/* Kazehakase */
	KzWindow *kz;
	/* tabs */
	GList *open_hist;
	GList *view_hist;
};

#define KZ_NOTEBOOK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KZ_TYPE_NOTEBOOK, KzNotebookPrivate))

static void     dispose      (GObject         *object);
static void     set_property (GObject         *object,
			      guint            prop_id,
			      const GValue    *value,
			      GParamSpec      *pspec);
static void     get_property (GObject         *object,
			      guint            prop_id,
			      GValue          *value,
			      GParamSpec      *pspec);

static gboolean scroll_event (GtkWidget       *widget,
			      GdkEventScroll  *event);

static void     drag_data_received
			     (GtkWidget *widget,
			      GdkDragContext *context,
			      gint x, gint y,
			      GtkSelectionData *data,
			      guint info,
			      guint time);

static void     switch_page  (GtkNotebook     *notebook,
			      GtkNotebookPage *page,
			      guint 	   page_num);

G_DEFINE_TYPE (KzNotebook, kz_notebook, GTK_TYPE_NOTEBOOK)

static void
kz_notebook_class_init (KzNotebookClass *klass)
{
	GObjectClass *gobject_class;
	GtkWidgetClass *widget_class;
	GtkNotebookClass *notebook_class;

	gobject_class  = (GObjectClass *)klass;
	widget_class   = (GtkWidgetClass *)klass;
	notebook_class = (GtkNotebookClass *)klass;

	/* GtkObject signals */
	gobject_class->dispose      = dispose;
	gobject_class->set_property = set_property;
	gobject_class->get_property = get_property;

	widget_class->scroll_event       = scroll_event;
	widget_class->drag_data_received = drag_data_received;

	notebook_class->switch_page = switch_page;

	g_object_class_install_property
		(gobject_class,
		 PROP_KZ_WINDOW,
		 g_param_spec_object("kz-window",
				     _("KzWindow"),
				     _("The parent kazehakase window"),
				     KZ_TYPE_WINDOW,
				     G_PARAM_READWRITE |
				     G_PARAM_CONSTRUCT_ONLY));

	g_type_class_add_private (gobject_class, sizeof(KzNotebookPrivate));
}


static void
kz_notebook_init (KzNotebook *notebook)
{
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(notebook);

	priv->kz        = NULL;
	priv->open_hist = NULL;
	priv->view_hist = NULL;

	gtk_drag_dest_set(GTK_WIDGET(notebook),
			  GTK_DEST_DEFAULT_ALL,
			  url_drag_types, G_N_ELEMENTS(url_drag_types),
			  GDK_ACTION_MOVE);
}


static void
set_property (GObject *object, guint prop_id,
              const GValue *value, GParamSpec *pspec)
{
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(object);
  
	switch (prop_id)
	{
	case PROP_KZ_WINDOW:
		priv->kz = g_object_ref(g_value_get_object(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}

static void
get_property (GObject *object, guint prop_id,
              GValue *value, GParamSpec *pspec)
{
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(object);

	switch (prop_id)
	{
	case PROP_KZ_WINDOW:
		g_value_set_object(value, priv->kz);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


GtkWidget *
kz_notebook_new (KzWindow *kz)
{
	KzNotebook *notebook;

	notebook = g_object_new(KZ_TYPE_NOTEBOOK,
				"kz-window", kz,
				"scrollable", TRUE,
				"show-tabs", TRUE,
				NULL);
	
	return GTK_WIDGET(notebook);
}

static void
dispose (GObject *object)
{
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(object);

	if (priv->kz)
	{
		g_object_unref(priv->kz);
		priv->kz = NULL;
	}

	if (priv->open_hist)
	{
		g_list_free(priv->open_hist);
		priv->open_hist = NULL;
	}
	if (priv->view_hist)
	{
		g_list_free(priv->view_hist);
		priv->view_hist = NULL;
	}

	if (G_OBJECT_CLASS(kz_notebook_parent_class)->dispose)
		G_OBJECT_CLASS(kz_notebook_parent_class)->dispose(object);
}

static gboolean
scroll_event (GtkWidget *widget, GdkEventScroll *event)
{
	GtkWidget* originator;
	GtkWidget *current;
	
	current = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widget),
					    gtk_notebook_get_current_page(GTK_NOTEBOOK(widget)));
	originator = gtk_get_event_widget ((GdkEvent *)event);

	/* ignore scroll events from the content of the page */
	if (!originator || gtk_widget_is_ancestor (originator, current) || originator == current)
		return FALSE;

	return kz_notebook_scroll_tab(KZ_NOTEBOOK(widget), event->direction);
}

static void
drag_data_received (GtkWidget *widget,
                    GdkDragContext *context,
                    gint x, gint y,
                    GtkSelectionData *data,
                    guint info,
                    guint time)
{
	KzWindow *kz;
	g_return_if_fail(KZ_IS_WINDOW(widget));

	kz = KZ_NOTEBOOK_GET_PRIVATE(widget)->kz;
	switch (info)
	{
	case TARGET_KAZEHAKASE_TAB:
	{
		GtkWidget *src_widget;
		KzWindow *src_kz;
		KzTabLabel *src_kztab;

		src_widget = gtk_drag_get_source_widget(context);

		if (!KZ_IS_TAB_LABEL(src_widget)) return;
		src_kztab = KZ_TAB_LABEL(src_widget);

		src_kz = KZ_WINDOW(KZ_GET_WINDOW_FROM_TAB(GTK_WIDGET(src_kztab->kzembed)));
		if (kz == src_kz)
		{
			gint n;

			n = gtk_notebook_get_n_pages(GTK_NOTEBOOK(kz->notebook));
			kz_window_reorder_tab(kz, GTK_WIDGET(src_kztab->kzembed),
					      n - 1);
			kz_actions_set_sensitive
				(kz, KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz)));
			kz_actions_set_tab_sensitive
				(kz, KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz)));
		}
		else
		{
			kz_window_move_tab(kz, GTK_WIDGET(src_kztab->kzembed));
		}

		break;
	}

	case TARGET_NETSCAPE_URL:
	case TARGET_TEXT_URI_LIST:
	case TARGET_TEXT_PLAIN:
	case TARGET_STRING:
	{
		gchar *string, **strings;

		if (data->length < 0) return;
		string = g_alloca (data->length + 1);

		memcpy (string, data->data, data->length);
		string[data->length] = '\0';

		strings = g_strsplit(string, "\n", 2);
		kz_window_open_new_tab(kz, strings[0]);
		g_strfreev(strings);
		break;

	}
	default:
		break;
	}
}

static gboolean
idle_focus_location_entry(gpointer data)
{
	g_return_val_if_fail(KZ_IS_WINDOW(data), FALSE);

	kz_window_activate_action(KZ_WINDOW(data), "FocusLocationEntry");

	return FALSE;
}

static void
switch_page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num)
{
	KzTabLabel *tab;
	gchar *title;
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(notebook);
	KzEmbed *kzembed = KZ_EMBED(gtk_notebook_get_nth_page(notebook, page_num));

	title = kz_embed_ensure_title(kzembed);
	if (title)
	{
		gtk_window_set_title(GTK_WINDOW(priv->kz), title);
		g_free(title);
	}
	else
	{
		gtk_window_set_title(GTK_WINDOW(priv->kz), _("Kazehakase"));
	}

	priv->view_hist = g_list_remove(priv->view_hist, kzembed);
	priv->view_hist = g_list_prepend(priv->view_hist, kzembed);

	tab = KZ_TAB_LABEL(gtk_notebook_get_tab_label(notebook, GTK_WIDGET(kzembed)));
	g_return_if_fail(tab);

	if (kz_tab_label_get_state(tab) == KZ_TAB_LABEL_STATE_LOADED)
	{
		const gchar *location;
		gboolean focus;
		kz_tab_label_set_state(tab, KZ_TAB_LABEL_STATE_NORMAL);
		location = kz_embed_get_location(kzembed);
		if((!location || *location == 0) &&
		   KZ_CONF_GET("Tab", "focus_loc_ent_new", focus, BOOL) &&
		   focus)
		{
			g_idle_add(idle_focus_location_entry, priv->kz);
		}
	}	

	if (GTK_NOTEBOOK_CLASS(kz_notebook_parent_class)->switch_page)
		GTK_NOTEBOOK_CLASS(kz_notebook_parent_class)->switch_page(notebook, page, page_num);
}

static gint 
get_insert_tab_position (KzNotebook *notebook)
{
	gchar pos_str[256];
	gint pos = -1;

	KZ_CONF_GET("Tab", "new_tab_position", pos_str, STRING);
	
	if (!pos_str || !strcasecmp(pos_str, "last"))
	{
		pos = -1;
	}
	else if (!strcasecmp(pos_str, "first"))
	{
		pos = 0;
	}
	else if (!strcasecmp(pos_str, "left"))
	{
		pos = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
	}
	else if (!strcasecmp(pos_str, "right"))
	{
		pos = 1 + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
	}
	else if (!strcasecmp(pos_str, "unread_right"))
	{
		gint i = 1 + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
		gint num = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook));
		
		if (i > num)
		{
			pos = -1;
		}
		else
		{
			while (i < num)
			{
				GtkWidget *kzembed;
				GtkWidget *label;
				KzTabLabelState state;

				kzembed = GTK_WIDGET(gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), i));
				label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook),
								   kzembed);
				state = kz_tab_label_get_state(KZ_TAB_LABEL(label));
				if (state == KZ_TAB_LABEL_STATE_NORMAL)
					break;
				i++;
			}
			pos = i;
		}
	}

	return pos;
}

gint
kz_notebook_open_new_tab_at_pos (KzNotebook *notebook, KzEmbed *embed, KzTabLabel *label, gint pos)
{
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(notebook);
	gint inserted_pos;

	inserted_pos = gtk_notebook_insert_page(GTK_NOTEBOOK(notebook),
						GTK_WIDGET(embed),
						GTK_WIDGET(label),
						pos);
	/* add to this notebook's history */
	priv->open_hist = g_list_prepend(priv->open_hist, embed);

	return inserted_pos;
}

gint
kz_notebook_open_new_tab (KzNotebook *notebook, KzEmbed *embed, KzTabLabel *label)
{
	gint pos;

	pos = get_insert_tab_position(notebook);

	return kz_notebook_open_new_tab_at_pos(notebook, embed, label, pos);
}

gboolean
kz_notebook_close_tab (KzNotebook *notebook, GtkWidget *widget)
{
	gchar *ret_page = NULL;
	KzTabLabel *kztab = NULL;
	GtkWidget *next = NULL;
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(notebook);
	KzWindow *kz = priv->kz;
	GtkWidget *current = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook),
						       gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)));
	kztab = KZ_TAB_LABEL(gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook),
							widget));

	if (kztab && kz_tab_label_get_lock(kztab))
	{
		/* kztab is locked, so return without closing tab */
		return FALSE;
	}
	else if (current != widget)
	{
		/* close tab which is not current , so there is nothing to consider */
		goto CLOSE_TAB;
	}

	ret_page = KZ_CONF_GET_STR("Tab", "page_to_return_when_close");
	if (ret_page)
	{
		if (!strcmp(ret_page, "last_shown"))
		{
			GList *node;

			if (priv->view_hist &&
					(node = g_list_next(priv->view_hist)))
			{
				next = node->data;
			}
		}
		else if (!strcmp(ret_page, "last_created"))
		{
			GList *node = priv->open_hist;

			for (; node && !next; node = g_list_next(node))
			{
				if (current != node->data)
					next = node->data;
			}
		}
		else if (!strcmp(ret_page, "prev_tab"))
		{
			gtk_notebook_prev_page(GTK_NOTEBOOK(notebook));
		}
		else if (!strcmp(ret_page, "next_tab"))
		{
			gtk_notebook_next_page(GTK_NOTEBOOK(notebook));
		}
	}
	if (next)
	{
		gint num;

		num = gtk_notebook_page_num
			(GTK_NOTEBOOK(notebook),
			 GTK_WIDGET(next));
		gtk_notebook_set_current_page
			(GTK_NOTEBOOK(notebook), num);
	}
	g_free(ret_page);

CLOSE_TAB:
	priv->view_hist = g_list_remove(priv->view_hist, widget);
	gtk_widget_destroy(widget);

	if (kz && gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)) == 0)
	{
		gtk_window_set_title(GTK_WINDOW(kz), _("Kazehakase"));
		kz_window_set_location_entry_text(kz, "");
	}
	return TRUE;
}

gboolean
kz_notebook_close_tabs (KzNotebook *notebook, KzNotebookCloseCondition condition, gint pos)
{
	gint num, i;

	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), FALSE);

	num = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook));

	if (num < 2 || pos < 0) return FALSE;

	for (i = num - 1; i >= 0; i--)
	{
		GtkWidget *widget = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), i);

		if (i == pos) continue;
		if (i > pos && condition == KZ_NOTEBOOK_CLOSE_BACKWARD) continue;
		if (i < pos && condition == KZ_NOTEBOOK_CLOSE_FORWARD) continue;

		kz_notebook_close_tab(notebook, widget);
	}
	return TRUE;
}

gboolean
kz_notebook_close_all_tab (KzNotebook *notebook)
{
	gint n_pages, i;

	n_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook));
	for (i = n_pages - 1; i >= 0; i--)
	{
		GtkWidget *widget = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), i);
		gtk_widget_destroy(widget);
	}
	return TRUE;
}

gboolean
kz_notebook_move_tab (KzNotebook *src_notebook, KzNotebook *dest_notebook, GtkWidget *widget)
{
	KzTabLabel *new_kztab;
	KzNotebookPrivate *dest_priv, *src_priv;

	/* create new tab label */
	dest_priv = KZ_NOTEBOOK_GET_PRIVATE(dest_notebook);
	new_kztab = KZ_TAB_LABEL(kz_tab_label_new(dest_priv->kz, KZ_EMBED(widget)));

	/* move the page to this window */
	g_object_ref(widget);
	gtk_container_remove(GTK_CONTAINER(src_notebook), widget);
	gtk_notebook_prepend_page(GTK_NOTEBOOK(dest_notebook),
				  widget,
				  GTK_WIDGET(new_kztab));
	g_object_unref(widget);

	src_priv = KZ_NOTEBOOK_GET_PRIVATE(src_notebook);
	/* remove view_hist */
	src_priv->view_hist = g_list_remove(src_priv->view_hist,
						widget);
	/* move open_hist */
	src_priv->open_hist = g_list_remove(src_priv->open_hist,
						widget);
	dest_priv->open_hist = g_list_prepend(dest_priv->open_hist,
					      widget);
	return TRUE;
}

GtkWidget *
kz_notebook_reorder_tab (KzNotebook *notebook, GtkWidget *widget, gint pos)
{
	gint n_pages, cur_pos, sib_pos = pos;
	GtkWidget *sibtab = NULL;

	n_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook));
	g_return_val_if_fail(pos >= 0 && pos < n_pages, NULL);

	cur_pos = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), widget);
	if (cur_pos == pos)
		return NULL;

	if (cur_pos < pos)
		sib_pos = sib_pos + 1;

	if (sib_pos < n_pages)
	{
		sibtab = gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook),
					gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), sib_pos));
	}

	gtk_notebook_reorder_child (GTK_NOTEBOOK(notebook),
				    widget, pos);

	return sibtab;
}

void
kz_notebook_next_tab (KzNotebook *notebook)
{
	gboolean circulation = FALSE;
	GtkNotebook *gtk_notebook;

	g_return_if_fail(KZ_IS_NOTEBOOK(notebook));

	gtk_notebook = GTK_NOTEBOOK(notebook);
	KZ_CONF_GET("Tab", "wheel_circulation", circulation, BOOL);
	
	if (circulation && 
	    gtk_notebook_get_current_page(gtk_notebook) == 
	    gtk_notebook_get_n_pages(gtk_notebook) - 1)
	{
		gtk_notebook_set_current_page(gtk_notebook, 0);
	}
	else
	{
		gtk_notebook_next_page(gtk_notebook);
	}
}

void
kz_notebook_prev_tab (KzNotebook *notebook)
{
	gboolean circulation = FALSE;
	GtkNotebook *gtk_notebook;

	g_return_if_fail(KZ_IS_NOTEBOOK(notebook));

	gtk_notebook = GTK_NOTEBOOK(notebook);
	KZ_CONF_GET("Tab", "wheel_circulation", circulation, BOOL);

	if (circulation &&
	    gtk_notebook_get_current_page(gtk_notebook) == 0)
	{
		gtk_notebook_set_current_page
			(gtk_notebook, gtk_notebook_get_n_pages(gtk_notebook) - 1);
	}
	else
	{
		gtk_notebook_prev_page(gtk_notebook);
	}
}

gboolean
kz_notebook_scroll_tab (KzNotebook *notebook, GdkScrollDirection direction)
{
	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), FALSE);

	switch (direction) {
	case GDK_SCROLL_UP:
	case GDK_SCROLL_LEFT:
		kz_notebook_prev_tab(notebook);
		return TRUE;
		break;
	case GDK_SCROLL_DOWN:
	case GDK_SCROLL_RIGHT:
		kz_notebook_next_tab(notebook);
		return TRUE;
		break;
	default:
		g_warning ("Invalid scroll direction!");
		break;
	}

	return FALSE;
}

GtkWidget *
kz_notebook_get_nth_page (KzNotebook *notebook, gint page_num)
{
	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), NULL);

	return gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num);
}

gint
kz_notebook_page_num (KzNotebook *notebook, GtkWidget *child)
{
	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), -1);

	return gtk_notebook_page_num(GTK_NOTEBOOK(notebook), child);
}

gint
kz_notebook_get_current_page (KzNotebook *notebook)
{
	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), -1);

	return gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
}

void
kz_notebook_set_current_page (KzNotebook *notebook, gint page_num)
{
	g_return_if_fail(KZ_IS_NOTEBOOK(notebook));

	gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), page_num);
}

gint
kz_notebook_get_n_pages (KzNotebook *notebook)
{
	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), 0);

	return gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook));
}

KzEmbed *
kz_notebook_get_sibling_embed (KzNotebook *notebook, KzEmbed *embed)
{
	gint pos;
	GtkWidget *sibembed;

	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), NULL);
	g_return_val_if_fail(KZ_IS_EMBED(embed), NULL);

	pos = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), GTK_WIDGET(embed));
	if (pos < 0)
		return NULL;
	pos++;
	if (pos >= gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)))
		return NULL;

	sibembed = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), pos);

	if (sibembed)
		return KZ_EMBED(sibembed);
	else
		return NULL;
}

KzTabLabel *
kz_notebook_get_sibling_tab_label (KzNotebook *notebook, KzTabLabel *label)
{
	KzEmbed *sibembed;
	GtkWidget *widget;

	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), NULL);
	g_return_val_if_fail(KZ_IS_TAB_LABEL(label), NULL);
	g_return_val_if_fail(KZ_IS_EMBED(label->kzembed), NULL);

	sibembed = kz_notebook_get_sibling_embed(notebook, label->kzembed);
	if (!sibembed)
		return NULL;

	widget = gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook), GTK_WIDGET(sibembed));
	if (widget)
		return KZ_TAB_LABEL(widget);
	else
		return NULL;
}
