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

/*
 *  Copyright (C) 2003 Hiroyuki Ikezoe
 *  Copyright (C) 2003 Takuro Ashie
 *
 *  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 <stdio.h>
#include <string.h>
#include "config.h"
#include "kz-home.h"
#include "gobject-utils.h"
#include "egg-toolbutton.h"
#include "intl.h"

static void kz_home_class_init (KzHomeClass *klass);
static void kz_home_init       (KzHome *kzhome);
static void kz_home_dispose    (GObject *object);

static GtkWidget *kz_home_menu_new   (KzHome *kzhome);
static void kz_home_file_load        (KzHome *kzhome);
static void kz_home_file_save        (KzHome *kzhome);
static void kz_home_menu_update      (KzHome *kzhome);

/* submenu */
static void kz_home_menu_submenu_popup(KzHomeList *kzhomelist, GdkEventButton *event);
static void kz_home_menu_submenu_delete(GtkMenuItem *menuite, KzHomeList *kzhomelist);

/* callbacks */
static gboolean kz_home_button_press_cb   (GtkWidget *widget,
					   GdkEventButton *event,
					   KzHome *kzhome);
static void     kz_home_menu_add_press_cb (GtkWidget *widget,
					   GdkEventButton *event,
					   KzHome *kzhome);
static void     kz_home_menu_press_cb     (GtkWidget *widget,
					   GdkEventButton *event,
					   KzHomeList *kzhomelist);
static void    proxy_weak_notify          (gpointer data, GObject *obj);

static GObjectClass *parent_class = NULL;

KZ_OBJECT_GET_TYPE(kz_home, "KzHome", KzHome,
		   kz_home_class_init, kz_home_init,
		   G_TYPE_OBJECT)

typedef enum {
	STATE_START   = (1 << 0),
	STATE_HOME    = (1 << 1),
	STATE_SITE    = (1 << 2),
	STATE_END     = (1 << 7),
} ParseState;

typedef struct _ParseContext ParseContext;
struct _ParseContext
{
	/* parser state information */
	ParseState state;

	KzHome *kzhome;
};

static void
start_element_handler (GMarkupParseContext *context,
		       const gchar         *element_name,
		       const gchar        **attribute_names,
		       const gchar        **attribute_values,
		       gpointer             user_data,
		       GError             **error)
{
	ParseContext *ctx = user_data;
	int i;

	switch (element_name[0])
	{
	 case 'h':
		if (ctx->state == STATE_START && !strcmp(element_name, "home"))
		{
			ctx->state = STATE_HOME;
		}
		break;
	 case 's':
		if ((ctx->state & STATE_HOME) && !strcmp(element_name, "site")) 
		{
			KzHomeList *list = NULL;

			list = g_new0(KzHomeList, 1);
			ctx->state = STATE_SITE;

			list->kzhome = ctx->kzhome;
			for (i = 0; attribute_names[i] != NULL; i++)
			{
				if (!strcmp(attribute_names[i], "uri"))
					list->uri = g_strdup(attribute_values[i]);
				else if (!strcmp(attribute_names[i], "title"))
					list->title = g_strdup(attribute_values[i]);
			}
			g_slist_append(ctx->kzhome->home_list, list);
		}
		break;
	}
}

static void
end_element_handler (GMarkupParseContext *context,
		     const gchar         *element_name,
		     gpointer             user_data,
		     GError             **error)
{
	ParseContext *ctx = user_data;
	
	switch (ctx->state)
	{
	 case STATE_START:
		g_warning(_("shouldn't get any end tags at this point"));
		/* should do a GError here */
		break;
	 case STATE_HOME:
		ctx->state = STATE_END;
		break;
	 case STATE_SITE:
		ctx->state = STATE_HOME;
		break;
	 case STATE_END:
		g_warning(_("shouldn't get any end tags at this point"));
		/* should do a GError here */
		break;
	}
}

static GMarkupParser ui_parser = {
	start_element_handler,
	end_element_handler,
	NULL,
	NULL,
	NULL
};

static gboolean
kz_home_parse_from_string (KzHome *kzhome, gpointer user_data,
			   const gchar *buffer, guint length,
			   GError **error)
{
	ParseContext ctx = { 0 };
	GMarkupParseContext *context;
	gboolean res = TRUE;

	g_return_val_if_fail(buffer != NULL, FALSE);

	ctx.state = STATE_START;
	ctx.kzhome = kzhome;

	context = g_markup_parse_context_new(&ui_parser, 0, &ctx, NULL);
	if (length < 0)
		length = strlen(buffer);

	if (g_markup_parse_context_parse(context, buffer, length, error))
	{
		if (!g_markup_parse_context_end_parse(context, error))
			res = FALSE;
	}
	else
		res = FALSE;

	g_markup_parse_context_free (context);

	return res;
}

static gboolean
kz_home_parse_from_file (KzHome *kzhome, gpointer user_data,
			 const gchar *filename, GError **error)
{
	gchar *buffer;
	gint length;
	gboolean res;

	if (!g_file_get_contents (filename, &buffer, &length, error))
		return FALSE;

	res = kz_home_parse_from_string(kzhome, user_data, buffer, length, error);
	g_free(buffer);

	return res;
}

static void
kz_home_class_init (KzHomeClass *klass)
{
	GObjectClass *gobject_class;

	parent_class = g_type_class_peek_parent (klass);

	gobject_class = (GObjectClass *) klass;

	/* GtkObject signals */
	gobject_class->dispose = kz_home_dispose;
}

static void
kz_home_init (KzHome *kzhome)
{
	/* widgets */
	kzhome->kz = NULL;
	kzhome->popup_menu = NULL;

	/* widgets to connect signal */
	kzhome->widget_list = NULL;
	
	/* lairs list  */
	kzhome->home_list = g_slist_alloc();
}

/* create new home object */
KzHome *
kz_home_new (KzWindow *kz)
{
	KzHome *kzhome = KZ_HOME(g_object_new(kz_home_get_type(), NULL));

	kzhome->kz = kz;

	/* load lairs list file */
	kz_home_file_load(kzhome);
	
	/* create lairs menu */
	kzhome->popup_menu = kz_home_menu_new(kzhome);
	
	return kzhome;
}

static void
kz_home_dispose (GObject *object)
{
	KzHome *kzhome = KZ_HOME(object);

	if (kzhome->home_list) {
		g_slist_free(kzhome->home_list);
		kzhome->home_list = NULL;
	}

	if (kzhome->widget_list) {
		GSList *node = kzhome->widget_list;
		for (; node; node = g_slist_next(node)) {
			GtkWidget *proxy = node->data;

			if (EGG_IS_TOOL_BUTTON(proxy))
				proxy = EGG_TOOL_BUTTON(proxy)->button;
			g_signal_handlers_disconnect_by_func
				(G_OBJECT(proxy),
				 G_CALLBACK(kz_home_button_press_cb),
				 kzhome);
			g_object_weak_unref(node->data, proxy_weak_notify, kzhome);
		}
		g_slist_free(kzhome->widget_list);
		kzhome->widget_list = NULL;
	}

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

static void 
kz_home_file_load (KzHome *kzhome)
{
        gchar *homefile;

        homefile = g_strdup_printf("%s/.%s/home.xml", g_get_home_dir(), PACKAGE);
	kz_home_parse_from_file(kzhome, NULL, homefile, NULL);
	g_free(homefile);
}

static void
kz_home_file_save (KzHome *kzhome)
{
	gchar *homefile;
	GString *xml;
	FILE *fp;

	KzHomeList *kzhomelist;
	GSList *list;
	guint list_num, n;

	xml = g_string_new(NULL);

	g_string_append(xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
	g_string_append(xml, "<home>\n");

	/* add sites */
	list = kzhome->home_list;
	list_num = g_slist_length(kzhome->home_list);
	for (n = 1; n < list_num; n++)
	{
		kzhomelist = (KzHomeList*) g_slist_nth_data(list, n);
		gchar *escaped_text[2];

		escaped_text[0] = g_markup_escape_text(kzhomelist->uri,-1);
		escaped_text[1] = g_markup_escape_text(kzhomelist->title,-1);
		g_string_append_printf(xml, "  <site uri=\"%s\" title=\"%s\" />\n",
				       escaped_text[0], escaped_text[1]);
		g_free(escaped_text[0]);
		g_free(escaped_text[1]);
	}

	xml = g_string_append(xml, "</home>");

        homefile = g_strdup_printf("%s/.%s/home.xml", g_get_home_dir(), PACKAGE);
	fp = fopen(homefile, "w");
	if (fp != NULL)
	{
		fwrite(xml->str, xml->len, 1, fp);
		fclose(fp);
	}
	g_free(homefile);

	g_string_free(xml, TRUE);
}

/* create lairs menu */
static GtkWidget *
kz_home_menu_new (KzHome *kzhome)
{
	GtkWidget *menu, *menu_item;
	GSList *list;
	KzHomeList *kzhomelist;
	guint list_num, n;
	
	menu = gtk_menu_new();	
	/* Add lairs */
	menu_item = gtk_menu_item_new_with_label(_("Add lairs"));
	gtk_menu_shell_append(GTK_MENU_SHELL(menu),
			      menu_item);
	g_signal_connect(G_OBJECT(menu_item), "button_press_event",
			 G_CALLBACK(kz_home_menu_add_press_cb),
			 kzhome);
	gtk_menu_shell_append(GTK_MENU_SHELL(menu),
			      gtk_separator_menu_item_new());
	
	/* create lairs menu */
	list = kzhome->home_list;
	list_num = g_slist_length(kzhome->home_list);
	for (n = 1; n < list_num; n++)
	{
		kzhomelist = (KzHomeList*) g_slist_nth_data(list, n);

		menu_item = gtk_menu_item_new_with_label(kzhomelist->title);
		gtk_menu_shell_append(GTK_MENU_SHELL(menu),
				      menu_item);
		g_signal_connect(G_OBJECT(menu_item), "button_press_event",
				 G_CALLBACK(kz_home_menu_press_cb),
				 kzhomelist);
	}
	gtk_widget_show_all(menu);
	return menu;
}

static void
kz_home_menu_submenu_popup (KzHomeList *kzhomelist, GdkEventButton *event)
{
	GtkWidget *submenu, *menu_item;
	
	submenu = gtk_menu_new();

	menu_item = gtk_menu_item_new_with_label(_("Remove from list"));
	gtk_menu_shell_append(GTK_MENU_SHELL(submenu),
			      menu_item);
	g_signal_connect(G_OBJECT(menu_item), "activate",
			 G_CALLBACK(kz_home_menu_submenu_delete),
			 kzhomelist);
	gtk_widget_show_all(submenu);
	gtk_menu_popup(GTK_MENU(submenu), NULL, NULL, NULL,
		       NULL, event->button, event->time);
}

static void
kz_home_menu_submenu_delete (GtkMenuItem *menuitem, KzHomeList *kzhomelist)
{
	g_slist_remove(kzhomelist->kzhome->home_list, kzhomelist);
	
	/* save home.xml */
	kz_home_file_save(kzhomelist->kzhome);
	
	kz_home_menu_update(kzhomelist->kzhome);
	
	g_free(kzhomelist);
}

static void
kz_home_menu_update (KzHome *kzhome)
{
	/* update lairs menu */
	if (kzhome->popup_menu != NULL)
	{	
		gtk_widget_destroy(kzhome->popup_menu);
		g_free(kzhome->popup_menu);
	}
	kzhome->popup_menu = kz_home_menu_new(kzhome);
}

static gboolean
kz_home_button_press_cb (GtkWidget *widget, GdkEventButton *event,
			 KzHome *kzhome)
{
	KzWindow *kz;

	kz = kzhome->kz;
	if (!KZ_IS_WINDOW(kz)) return FALSE;

	switch ( event->button ) {
	case 2: /* open in new tab */
		if (g_slist_length(kzhome->home_list) > 1) {
			KzHomeList *list = g_slist_nth_data(kzhome->home_list, 1);
			kz_window_open_new_tab(kz, list->uri);
		}
		return TRUE;
		break;
	case 3: /* popup secret menu */
		gtk_menu_popup(GTK_MENU(kzhome->popup_menu), NULL, NULL, NULL,
			       NULL, event->button, event->time);
		return TRUE;
		break;
	}

	return FALSE;
}

static void
kz_home_menu_add_press_cb (GtkWidget *widget, GdkEventButton *event,
			   KzHome *kzhome)
{
	GSList *list;
	KzWindow *kz;
	KzHomeList *kzhomelist = NULL;
	const gchar *title, *uri;

	kz = kzhome->kz;
	if (!KZ_IS_WINDOW(kz)) return;

	kzhomelist = g_new0(KzHomeList, 1);
	
	kzhomelist->kzhome = kzhome;

	title = kz_window_get_title(kz);
	uri   = kz_window_get_uri(kz);
	if (title) kzhomelist->title = g_strdup(title);
	if (uri)   kzhomelist->uri = g_strdup(uri);

	/* append new site */
	list = g_slist_last(kzhome->home_list);
	g_slist_append(list, kzhomelist);
	
	/* save home.xml */
	kz_home_file_save(kzhome);
	
	kz_home_menu_update(kzhome);
}

static void
kz_home_menu_press_cb (GtkWidget *widget, GdkEventButton *event,
		       KzHomeList *kzhomelist)
{
	KzWindow *kz;

	kz = kzhomelist->kzhome->kz;
	if (!KZ_IS_WINDOW(kz)) return;

	if (kzhomelist->uri)
	{
		switch ( event->button ) {
		 case 1:
			kz_window_load_url(kz, kzhomelist->uri);
			break;
		 case 2:
			kz_window_open_new_tab(kz, kzhomelist->uri);
			break;
		 case 3: /* popup menu(remove etc.) */
			kz_home_menu_submenu_popup(kzhomelist, event);
			break;
		}
	}
}

static void
proxy_weak_notify (gpointer data, GObject *obj)
{
	KzHome *kzhome = KZ_HOME(data);

	kzhome->widget_list = g_slist_remove(kzhome->widget_list, obj);
}

gboolean
kz_home_set_widget (KzHome *kzhome, GtkWidget *widget)
{
	GtkWidget *proxy = NULL;

	g_return_val_if_fail(KZ_IS_HOME(kzhome), FALSE);

	if (g_slist_find(kzhome->widget_list, widget)) return FALSE;

	if (GTK_IS_MENU_ITEM (widget) || GTK_IS_BUTTON (widget))
		proxy = widget;
	else if (EGG_IS_TOOL_BUTTON (widget))
		proxy = EGG_TOOL_BUTTON(widget)->button;

	if (proxy) {
		g_object_weak_ref(G_OBJECT(widget), proxy_weak_notify, kzhome);
		g_signal_connect(G_OBJECT(proxy), "button_press_event",
				 G_CALLBACK(kz_home_button_press_cb), kzhome);
		kzhome->widget_list = g_slist_append(kzhome->widget_list, widget);
		return TRUE;
	}

	return FALSE;
}

void
kz_home_load_first_home (KzHome *kzhome)
{
	KzWindow *kz;

	g_return_if_fail(KZ_IS_HOME(kzhome));

	kz = kzhome->kz;
	if (!KZ_IS_WINDOW(kz)) return;

	if (g_slist_length(kzhome->home_list) > 1) {
		KzHomeList *list = g_slist_nth_data(kzhome->home_list, 1);

		kz_window_load_url(kz, list->uri);
	}
}
