/* -*- 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-download-box.h"
#include "kz-entry.h"
#include "kz-statusbar.h"

enum {
	PROP_0,
	PROP_KZ_WINDOW
};

typedef struct _KzStatusbarPrivate	KzStatusbarPrivate;
struct _KzStatusbarPrivate
{
	KzWindow *kz;

	GtkWidget  *find_area;
	GtkWidget  *find_direction;
	GtkWidget  *statusbar;

	/* for find keyword */
	gboolean    did_find;
	GtkTooltips *find_tips[2];

	/* for popup & gesture */
	KzGesture  *gesture;
	gint start_x, start_y;
	gboolean is_gesture;

	/* statusbar id */
	guint status_link_message;
	guint status_gesture;
	guint status_search;
};
#define KZ_STATUSBAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KZ_TYPE_STATUSBAR, KzStatusbarPrivate))

#define STATUS_LINK_MESSAGE "INTERNAL_LINK_MESSAGE"
#define STATUS_GESTURE "INTERNAL_GESTURE"
#define STATUS_SEARCH "INTERNAL_SEARCH"

static GObject *constructor (GType type,
                             guint n_props,
                             GObjectConstructParam *props);

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 void     cb_find_keyword               (GtkWidget       *widget,
					       KzStatusbar     *bar);
static gboolean cb_find_key_press_event       (GtkWidget       *widget,
					       GdkEventKey     *event,
					       KzStatusbar     *bar);
static void     cb_find_changed               (GtkWidget       *widget,
					       KzStatusbar     *bar);
static void     cb_find_direction_toggle      (GtkToggleButton *button,
					       KzStatusbar     *bar);

G_DEFINE_TYPE (KzStatusbar, kz_statusbar, GTK_TYPE_HBOX)

static void
kz_statusbar_class_init (KzStatusbarClass *klass)
{
	GObjectClass *gobject_class;
	GtkWidgetClass *widget_class;

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

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

	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(KzStatusbarPrivate));
}


static void
kz_statusbar_init (KzStatusbar *bar)
{
	KzStatusbarPrivate *priv = KZ_STATUSBAR_GET_PRIVATE (bar);
	GtkWidget *vseparator, *arrow, *toggle, *widget;
	GtkTooltips *tips;

	/* widgets */
	priv->kz = NULL;
	
	priv->did_find     = FALSE;

	/* find direction toggle */
	priv->find_direction = gtk_toggle_button_new();

	tips = gtk_tooltips_new();

	toggle = priv->find_direction;
	arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
	gtk_container_add(GTK_CONTAINER(toggle), arrow);
	gtk_box_pack_start(GTK_BOX(bar), toggle, FALSE, FALSE, 0); 
	gtk_widget_show(arrow);
	gtk_widget_show(toggle);

	g_signal_connect(priv->find_direction, "toggled",
			 G_CALLBACK(cb_find_direction_toggle), bar);

	g_object_ref(G_OBJECT(tips));
	g_object_ref_sink(G_OBJECT(tips));
	gtk_tooltips_set_tip(tips, toggle,
			     _("Find direction"), NULL);
	priv->find_tips[0] = tips;

	/* find entry */
	priv->find_area = kz_entry_new_with_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
	tips = gtk_tooltips_new();

	gtk_box_pack_start(GTK_BOX(bar), priv->find_area,
			   FALSE, FALSE, 0); 
	kz_entry_set_backtext(KZ_ENTRY(priv->find_area),
			      _("Find in this page"));
	gtk_widget_show(priv->find_area);
	
	g_signal_connect(priv->find_area, "key-press-event",
			 G_CALLBACK(cb_find_key_press_event), bar);
	g_signal_connect(priv->find_area, "changed",
			 G_CALLBACK(cb_find_changed), bar);
	g_signal_connect(priv->find_area, "activate",
			 G_CALLBACK(cb_find_keyword), bar);

	g_object_ref(G_OBJECT(tips));
	g_object_ref_sink(G_OBJECT(tips));
	gtk_tooltips_set_tip(tips, priv->find_area,
			     _("Find a word or phrase in this page"), NULL);
	priv->find_tips[1] = tips;

	priv->statusbar = gtk_statusbar_new();
	priv->status_link_message =
		gtk_statusbar_get_context_id(GTK_STATUSBAR(priv->statusbar),
					     STATUS_LINK_MESSAGE);
	priv->status_gesture =
		gtk_statusbar_get_context_id(GTK_STATUSBAR(priv->statusbar),
					     STATUS_GESTURE);
	priv->status_search =
		gtk_statusbar_get_context_id(GTK_STATUSBAR(priv->statusbar),
					     STATUS_SEARCH);

	/* status bar */
	gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(priv->statusbar),
					  FALSE);
	gtk_box_pack_start(GTK_BOX(bar), priv->statusbar, TRUE, TRUE, 0);
	gtk_widget_show(priv->statusbar);
	vseparator = gtk_vseparator_new();
	gtk_box_pack_start(GTK_BOX(bar), vseparator, FALSE, FALSE, 0);
	gtk_widget_show(vseparator);

	/* resize grip */
	widget = gtk_statusbar_new();
	/* Why 18?
	   Because gtk_status_bar's max grip width is 18. */
	gtk_widget_set_size_request(widget, 18, -1);
	gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(widget), TRUE);
	gtk_box_pack_end(GTK_BOX(bar), widget, FALSE, FALSE, 0);
	gtk_widget_show(widget);
	vseparator = gtk_vseparator_new();
	gtk_box_pack_end(GTK_BOX(bar), vseparator, FALSE, FALSE, 0);
	gtk_widget_show(vseparator);

}


static GObject*
constructor (GType                  type,
             guint                  n_props,
             GObjectConstructParam *props)
{
	KzStatusbar *bar;
	KzStatusbarPrivate *priv;
	GObject *object;
	GObjectClass *klass = G_OBJECT_CLASS(kz_statusbar_parent_class);
	GtkWidget *widget;

	object = klass->constructor(type, n_props, props);

	bar = KZ_STATUSBAR(object);
	priv = KZ_STATUSBAR_GET_PRIVATE(object);

	/* download box */
	widget = kz_download_box_new(priv->kz);
	gtk_widget_set_size_request(widget, 200, -1);
	gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(widget),
					  FALSE);
	gtk_statusbar_push(GTK_STATUSBAR(widget), 0,
			   _("Drop link to download"));
	gtk_box_pack_end(GTK_BOX(bar), widget,
			 FALSE, FALSE, 0);
	gtk_widget_show(widget);

	return object;
}


static void
set_property (GObject *object, guint prop_id,
              const GValue *value, GParamSpec *pspec)
{
	KzStatusbarPrivate *priv = KZ_STATUSBAR_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)
{
	KzStatusbarPrivate *priv = KZ_STATUSBAR_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_statusbar_new (KzWindow *kz)
{
	KzStatusbar *bar;

	bar = g_object_new(KZ_TYPE_STATUSBAR,
			   "kz-window", kz,
			   NULL);
	
	return GTK_WIDGET(bar);
}

static void
dispose (GObject *object)
{
	KzStatusbarPrivate *priv = KZ_STATUSBAR_GET_PRIVATE(object);

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

	if (priv->find_tips[0]) 
	{
		g_object_unref(priv->find_tips[0]);
		priv->find_tips[0] = NULL;
	}

	if (priv->find_tips[1])
	{
		g_object_unref(priv->find_tips[1]);
		priv->find_tips[1] = NULL;
	}

	if (G_OBJECT_CLASS(kz_statusbar_parent_class)->dispose)
		(*G_OBJECT_CLASS(kz_statusbar_parent_class)->dispose) (object);
}


void
kz_statusbar_set_text (KzStatusbar *bar, const gchar *text, guint id)
{
	KzStatusbarPrivate *priv;

	g_return_if_fail(KZ_IS_STATUSBAR(bar));

	priv = KZ_STATUSBAR_GET_PRIVATE (bar);

	gtk_statusbar_pop(GTK_STATUSBAR(priv->statusbar), id);
	if (text)
		gtk_statusbar_push(GTK_STATUSBAR(priv->statusbar), id, text);
}

void
kz_statusbar_set_text_with_name (KzStatusbar *bar, const gchar *text,
				 const gchar *name)
{
	KzStatusbarPrivate *priv;
	guint id;

	g_return_if_fail(KZ_IS_STATUSBAR(bar));

	priv = KZ_STATUSBAR_GET_PRIVATE (bar);
	id = gtk_statusbar_get_context_id (GTK_STATUSBAR(priv->statusbar),
					   name);
	kz_statusbar_set_text (bar, text, id);
}

void
kz_statusbar_set_link_text (KzStatusbar *bar, const gchar *text)
{
	KzStatusbarPrivate *priv;

	g_return_if_fail(KZ_IS_STATUSBAR(bar));
	
	priv = KZ_STATUSBAR_GET_PRIVATE (bar);

	kz_statusbar_set_text(bar, text, priv->status_link_message);
}


void
kz_statusbar_set_gesture_text (KzStatusbar *bar, const gchar *text)
{
	KzStatusbarPrivate *priv;

	g_return_if_fail(KZ_IS_STATUSBAR(bar));
	
	priv = KZ_STATUSBAR_GET_PRIVATE (bar);

	kz_statusbar_set_text(bar, text, priv->status_gesture);
}


void
kz_statusbar_set_focus_to_find_area (KzStatusbar *bar)
{
	KzStatusbarPrivate *priv;

	g_return_if_fail(KZ_IS_STATUSBAR(bar));
	
	priv = KZ_STATUSBAR_GET_PRIVATE (bar);

	gtk_widget_grab_focus (priv->find_area);
}


static void
search_found(GtkWidget* widget, KzStatusbar *bar)
{
	static const GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
	static const GdkColor black = { 0, 0x0000, 0x0000, 0x0000 };

	KzStatusbarPrivate *priv = KZ_STATUSBAR_GET_PRIVATE (bar);
	gtk_statusbar_pop(GTK_STATUSBAR(priv->statusbar),
			  priv->status_search);

	gtk_widget_modify_base(widget, GTK_STATE_NORMAL, &white);
	gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &black);
}

static void
search_not_found(GtkWidget* widget, KzStatusbar *bar)
{
	static const GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
	static const GdkColor red = { 0, 0xffff, 0x6666, 0x6666 };
	const gchar *search_word;
	gchar *message;
	KzStatusbarPrivate *priv = KZ_STATUSBAR_GET_PRIVATE (bar);

	search_word = gtk_entry_get_text(GTK_ENTRY(widget));
	message = g_strdup_printf(_("%s not found"), search_word);
	if(priv->statusbar) 
	{
		gtk_statusbar_pop(GTK_STATUSBAR(priv->statusbar),
				  priv->status_search);
		gtk_statusbar_push(GTK_STATUSBAR(priv->statusbar),
				   priv->status_search,
				   message);
	}
	g_free(message);

	gtk_widget_modify_base(widget, GTK_STATE_NORMAL, &red);
	gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &white);
}

static void
cb_find_keyword (GtkWidget *widget, KzStatusbar *bar)
{
	KzStatusbarPrivate *priv = KZ_STATUSBAR_GET_PRIVATE (bar);
	KzEmbed *embed = KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(priv->kz));
	GtkToggleButton *toggle = GTK_TOGGLE_BUTTON(priv->find_direction);
	const gchar *text = gtk_entry_get_text(GTK_ENTRY(widget));
	gboolean back = gtk_toggle_button_get_active(toggle);
	gint x, y;
	GdkModifierType modifier;

	if (!embed) return;

	if (!text || !(*text))
	{
		search_found(widget, bar);
		return;
	}

	gdk_window_get_pointer(NULL, &x, &y, &modifier);
	if (modifier & GDK_SHIFT_MASK)
		back = !back;
	priv->did_find = kz_embed_find(embed, text, back);
	if(priv->did_find)
		search_found(widget, bar);
	else
		search_not_found(widget, bar);
}

static gboolean
cb_find_key_press_event (GtkWidget *widget, GdkEventKey *event, KzStatusbar *bar)
{
	if ((event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) &&
	    event->state & GDK_SHIFT_MASK)
	{
		gtk_widget_activate(widget);
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

static void
cb_find_changed (GtkWidget *widget, KzStatusbar *bar)
{
	KzEmbed *embed;
	GtkToggleButton *toggle;
	const gchar *text;
	KzStatusbarPrivate *priv;
	gboolean back;

	priv = KZ_STATUSBAR_GET_PRIVATE(bar);
	embed = KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(priv->kz));
	if (!embed) return;

	toggle = GTK_TOGGLE_BUTTON(priv->find_direction);
	text = gtk_entry_get_text(GTK_ENTRY(widget));
	if (!text || !(*text))
	{
		search_found(widget, bar);
		return;
	}

	back = gtk_toggle_button_get_active(toggle);
	priv->did_find = kz_embed_incremental_search(embed, text, back);

	if(priv->did_find)
		search_found(widget, bar);
	else
		search_not_found(widget, bar);
	return;
}


static void
cb_find_direction_toggle (GtkToggleButton *button, KzStatusbar *bar)
{
	GtkArrow *arrow;
	gboolean active;

	arrow = GTK_ARROW(GTK_BIN(button)->child);
	g_return_if_fail(GTK_IS_ARROW(arrow));

	active = gtk_toggle_button_get_active(button);
	if (active)
		gtk_arrow_set(arrow, GTK_ARROW_UP, GTK_SHADOW_NONE);
	else
		gtk_arrow_set(arrow, GTK_ARROW_DOWN, GTK_SHADOW_NONE);
}


