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

/*
 *  Copyright (C) 2002 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.
 */

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>

#include "kazehakase.h"
#include "kz-window.h"
#include "kz-ext.h"
#include "kz-actions-tab.h"
#include "utils.h"
#ifdef WITH_GECKO
#include "mozilla.h"
#endif

KzApp	       *kz_app		  = NULL;

static gchar** action_array;
static gchar* geometry_string;
static gboolean show_version;

static GOptionEntry entries[] = {
	{ "action", 'a', 0, G_OPTION_ARG_STRING_ARRAY, &action_array, "Execute action", NULL},
	{ "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry_string, " Position the window on the screen", NULL },
	{ "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, "Show this version", NULL },
	{ NULL }
};

static const gchar parameter_string[] = "URI ...";

static GOptionContext *context;
static GOptionGroup *main_group;

static void activate_action(const gchar *text);

/******************************************************************************
 *                                                                            *
 *                       arguments parser                                     *
 *                                                                            *
 ******************************************************************************/
/* split_string is taken from glib/tests/option-test.c */

static gchar **
split_string (const char *str, int *argc)
{
	gchar **argv;
	int len;
  
	argv = g_strsplit (str, "\n", -1);

	for (len = 0; argv[len] != NULL; len++);
	
	if (argc)
		*argc = len;
	
	return argv;
}

static gboolean
pre_parse_cb (GOptionContext *context,
		GOptionGroup   *group,
		gpointer	       data,
		GError        **error)
{
#if 0
	if(action_array)
	{
                /* free action_array before using */
		g_strfreev (action_array);
		action_array = NULL;
	}
#endif
	return TRUE;
}

static gboolean
post_parse_cb (GOptionContext *context,
		 GOptionGroup   *group,
		 gpointer	  data,
		 GError        **error)
{
        /* XXX: Is there anything to do? */
	return TRUE;
}

static void
parse_error_cb (GOptionContext *context,
		 GOptionGroup   *group,
		 gpointer	  data,
		 GError        **error)
{
	fprintf (stderr, "%s\n", (*error)->message);
	g_error_free (*error);
	return;
}

static void
free_goption()
{
	g_free (geometry_string);
	g_strfreev (action_array);
	g_option_context_free (context);
}

/* XXX: This function has side effect! */
static void
process_args(int argc, char** argv)
{
	int i;
	static int count = 0;
	const GList* list;
	KzWindow *kz;

#warning This function has side effect!

	if (!kz_app) return;

	list = KZ_GET_WINDOW_LIST;
	if (!list || !list->data || !KZ_IS_WINDOW(list->data))
		return;

	if(action_array)
	{
                /* show actions */
		for(; action_array[count] != NULL; count++)
		{
			activate_action(action_array[count]);
		}
	}
	
	kz = KZ_WINDOW(list->data);
	for(i = 1; i < argc; i++)
	{
		gchar *uri = complement_scheme(argv[i]);
		gdk_window_raise(GTK_WIDGET(kz)->window);

		kz_window_open_new_tab(kz, uri);
		g_free(uri);
	}
}

/******************************************************************************
 *                                                                            *
 *                       Kazehakase process checker                           *
 *                                                                            *
 ******************************************************************************/
/*
 *  Based on Text maid 2.3.0
 *  copyright (c) 1998-2003 Kazuki IWAMOTO http://www.maid.org/ iwm@maid.org
 */
enum {
	TARGET_STRING
};

static GtkTargetEntry targets[]={
	{"STRING", 0, TARGET_STRING},
};
static GdkAtom  atom_kz0;  /* for checking process  */
static GdkAtom  atom_kz1;  /* for sending arguments */
static gchar   *arg_text = NULL;
static gint     instance = -1;


static void
cb_selection_get(GtkWidget *widget,
		 GtkSelectionData *data,
		 guint info,
		 guint time,
		 GtkWidget *window)
{
	gchar *text = NULL;
	gint length = 0;

	if (data->selection == atom_kz0)
	{
		text = "Kazehakase Selection";
		length = strlen(text);
		gtk_selection_convert(window, atom_kz1,
				      GDK_SELECTION_TYPE_STRING,
				      GDK_CURRENT_TIME);
	}
	else if (data->selection == atom_kz1 && arg_text != NULL)
	{
		text = arg_text;
		arg_text = NULL;
		length = strlen(text);
	}

	if (text != NULL)
	{
		gtk_selection_data_set_text(data, text, length);
		if (data->selection == atom_kz1)
			g_free(text);
	}
}


static void
activate_action(const gchar *text)
{
	KzWindow *kz;
	KzTabLabel *kztab;
	const GList *window_list;
	GtkWidget *widget;
	GtkAction *action;
	gchar *action_name;
	gint action_type;
	enum {
		WINDOW_ACTION,
		TAB_ACTION
	};
	gint window_id = 0, tab_id = -1 /* -1 is current tab */;

	window_list = KZ_GET_WINDOW_LIST;
	
	if (g_str_has_prefix(text, "KzWindowTabPopup"))
	{
		gchar *p1, *p2, *p3;
		action_type = TAB_ACTION;
		
		p1 = (gchar *)(text + 17);

		p2 = strchr(p1, ' ');
		if (!p2)
		{
			window_id = 1;
			action_name = g_strdup(p1);
		}
		else
		{
			window_id = atoi(p2 + 1);
			action_name = g_strndup(p1, p2 - p1);
			p3 = strchr(p2 + 1, ' ');
			if (p3)
				tab_id = atoi(p3 + 1);
		}
		tab_id = !tab_id ? -1 : tab_id;
	}
	else /*if (g_str_has_prefix(action, "KzWindow"))*/
	{
		gchar *p1, *p2;
		action_type = WINDOW_ACTION;

		p1 = strchr(text, '/');
		if (!p1)
			p1 = (gchar*)text;
		else
			p1++;

		p2 = strchr(p1, ' ');
		if (!p2)
		{
			window_id = 1;
			action_name = g_strdup(p1);
		}
		else
		{
			window_id = atoi(p2 + 1);
			action_name = g_strndup(p1, p2 - p1);
		}
	}
	window_id = !window_id ? 1 : window_id;

	kz = g_list_nth_data((GList *)window_list, window_id - 1);	
	/* if not exists indicated window, use first one  */
	if (!kz)
		kz = window_list->data;

	/* activate actions */
	switch (action_type)
	{
	 case TAB_ACTION:
		if (tab_id == -1)
			widget = KZ_WINDOW_CURRENT_PAGE(kz);
		else
			widget = KZ_WINDOW_NTH_PAGE(kz, tab_id - 1);
		if (!widget)
			widget = KZ_WINDOW_CURRENT_PAGE(kz);
		if (!widget)
			break;
		kztab = KZ_TAB_LABEL(gtk_notebook_get_tab_label(GTK_NOTEBOOK(kz->notebook),
								widget));

		action = gtk_action_group_get_action(kz->tabpop_actions,
						     action_name);
		if (action)
			kz_actions_tab_activate_action(kztab, action);
		break;
	 case WINDOW_ACTION:
	 default:
		action = gtk_action_group_get_action(kz->actions,
						     action_name);
		if (action)
			gtk_action_activate(action);
		break;
	}

	g_free(action_name);
}

static void
cb_selection_received(GtkWidget *widget,
		      GtkSelectionData *data,
		      guint time,
		      gpointer user_data)
{
	if (data->selection == atom_kz0)
	{
		instance = MAX(data->length, 0);
	}
	else if (data->selection == atom_kz1 && data->length > 0) 
	{
		gint argc;
		gchar **argv;
		gboolean retval;
		GError *error = NULL;


		/* open URL */
		argv = split_string((const char*)data->data, &argc);

		retval = g_option_context_parse (context, &argc, &argv, &error);
		if(retval)
			process_args(argc, argv);

		g_strfreev(argv);
	}
}


static GtkWidget *
check_kazehakase_process (int argc, char *argv[])
{
	GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gint i, length = 0;

	gtk_widget_realize(window);

	atom_kz0 = gdk_atom_intern("Kazehakase InterProcess communication 0",
				   FALSE);
	atom_kz1 = gdk_atom_intern("Kazehakase InterProcess communication 1",
				   FALSE);

	gtk_selection_add_targets(window, atom_kz0, targets, 1);
	gtk_selection_add_targets(window, atom_kz1, targets, 1);
	g_signal_connect (window, "selection-get",
			  G_CALLBACK(cb_selection_get), window);
	g_signal_connect (window, "selection-received",
			  G_CALLBACK(cb_selection_received), window);

	for (i = 0; i < argc; i++)
	{
		gint len;

		len = strlen(argv[i]) * sizeof(gchar);
		arg_text = g_realloc(arg_text,
				     length + len + sizeof(gchar));
		g_memmove(arg_text + length, argv[i], len);
		length += len;
		arg_text[length++] = '\n';
	}
	if (length > 0)
	{
		arg_text[length - 1] = '\0';
		gtk_selection_owner_set(window, atom_kz1, GDK_CURRENT_TIME);
	}
	gtk_selection_convert(window,atom_kz0,
			      GDK_SELECTION_TYPE_STRING,
			      GDK_CURRENT_TIME);
	while (instance < 0)
		while (gtk_events_pending())
			gtk_main_iteration();
	if (instance > 0)
	{
		/* Kazehakase process is already exists */
		while (arg_text != NULL)
			while (gtk_events_pending())
				gtk_main_iteration();
		gtk_widget_destroy(window);
		return NULL;
	}
	g_free(arg_text);
	arg_text = NULL;
	gtk_selection_owner_set(window, atom_kz0, GDK_CURRENT_TIME);

	return window;
}


/******************************************************************************
 *                                                                            *
 *                                  Main                                      *
 *                                                                            *
 ******************************************************************************/
int
main (int argc, char *argv[])
{
	int init_address;
	GtkWidget *window = NULL;
	GtkWidget *dupl_check_server = NULL;
	gchar **argv_copy;
	gint argc_copy;
	gboolean retval;
	GError *error = NULL;
	gboolean restore = FALSE;

        /* initialize */
	bindtextdomain(PACKAGE, LOCALEDIR);
	bind_textdomain_codeset(PACKAGE, "UTF-8");
	textdomain(PACKAGE);

        /* initialize goption */
	context = g_option_context_new (parameter_string);
	g_option_context_add_main_entries (context, entries, NULL);
	main_group = g_option_context_get_main_group (context);

        /* set hooks */
	g_option_group_set_parse_hooks (main_group,
					pre_parse_cb, post_parse_cb);
	g_option_group_set_error_hook (main_group,
				       parse_error_cb);
        /* set to ignore unknown options */
	g_option_context_set_ignore_unknown_options (context, TRUE);

	gtk_init(&argc, &argv);

        /* copy argv (ant argc) so as to preserve original arguments */
	argv_copy = g_strdupv(argv);
	argc_copy = argc;

        /* g_option_context_parse changes argc_copy and argv_copy so as to
	   process remaining arguments */
	retval = g_option_context_parse (context, &argc_copy, &argv_copy, &error);
	if(retval && show_version)
	{
	        /* show version number and exit */
		g_print("%s %s\n", PACKAGE, VERSION);
		free_goption();
		g_strfreev(argv_copy);
		exit(0);
	}

	/* find kazehakase process */
	dupl_check_server = check_kazehakase_process(argc, argv);
	if (!dupl_check_server)
	{
		free_goption();
		g_strfreev(argv_copy);
		/* FIXME! show message */
		return 0;   /* found kazehakase process */
	}

	kz_app = kz_app_new(argc, argv);
	kz_ext_init(kz_app, &init_address);

#ifdef WITH_GECKO
#warning FIXME! Initializaton of mozilla should be called somewhere else.
        mozilla_init(KZ_GET_GLOBAL_PROFILE);
#endif

	/* create window */	
	KZ_CONF_GET("Session", "restore", restore, BOOL);
	if (restore)
	{
		window = kz_app_restore_session(kz_app);
		if (!window)
			window = kz_app_create_new_window(kz_app, NULL);
	}
	else
		window = kz_app_create_new_window(kz_app, NULL);

       	/* 
	 * gtk_window_pargse_geometry() needs to be called 
	 * before gtk_widget_show().
	 */ 
	if(geometry_string)
	{
		gtk_window_parse_geometry(GTK_WINDOW(window),
					  geometry_string);
	}
	gtk_widget_show(window);

	/* process arguments */
	process_args(argc_copy, argv_copy);
	g_strfreev (argv_copy);

	/* event loop */
	gtk_main();

	/* exit */
	gtk_widget_destroy(dupl_check_server);
	dupl_check_server = NULL;

	kz_ext_exit();
#ifdef WITH_GECKO
        mozilla_exit();
#endif
	g_object_unref(kz_app);

	return 0;
}
