/*
 *  Copyright (C) 2000 Marco Pesenti Gritti
 *
 *  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.
 */

/* Galeon includes */
#include "galeon.h"
#include "spinner.h"
#include "eel-gconf-extensions.h"
#include "window.h"
#include "misc.h"
#include "embed.h"
#include "themes.h"

#include <string.h>
#include <gtk/gtkbox.h>

/* GNOME includes */
#include <libgnome/gnome-util.h>
#include <libgnomeui/gnome-animator.h>
#include <libgnomeui/gnome-preferences.h>
#include <libgnomevfs/gnome-vfs.h>
#include <libgnomevfs/gnome-vfs-mime.h>
#include <gdk-pixbuf/gdk-pixbuf.h>

/* local function prototypes */
static void free_spinner_list (void);
static void spinner_fill_iconlist_from_dir (GnomeIconList *icon_list,
					    gchar *base);
static void
spinner_button_released_cb (GtkButton *button, GdkEventButton *event,
			    GaleonWindow *window);

/**
 * spinner_create: create a spinner, note this DOESN'T pack it anywhere,
 * so the user has to take the return value and pack it somewhere
 * FIXME cleanup
 */
GtkWidget *
spinner_create (GaleonWindow *window)
{
	gint i, width, height, target_height, icon_height;
	GtkWidget *spinner_button;
	GtkWidget *spinner;
	ToolbarStyle style;
	GdkPixbuf *pixbuf;
	gchar *anim_dir;
	gchar *filename;
	gboolean relief = gnome_preferences_get_toolbar_relief_btn ();

	/* check window */
	return_val_if_not_window (window, NULL);

	/* initially no spinner */
	window->spinner = NULL;

	/* get the directory of the animations from configuration space,
 	 * default to installed share directory */
	anim_dir = eel_gconf_get_string (CONF_TOOLBAR_SPINNER_DIR);
	if (anim_dir == NULL || strlen (anim_dir) == 0)
	{
		anim_dir = g_strdup (SHARE_DIR);
	}
	
	/* try to load the first file to find the size */
	filename = g_strdup_printf ("%s/rest.png", anim_dir);
	pixbuf = gdk_pixbuf_new_from_file (filename);
	if (pixbuf == NULL)
	{
		g_warning ("unable to load first spinner frame: %s\n",
			   filename);
		g_free (filename);
		return NULL;
	}

	width = gdk_pixbuf_get_width (pixbuf);
	height = gdk_pixbuf_get_height (pixbuf);
	gdk_pixbuf_unref (pixbuf);
	g_free (filename);

	/* set the size of the pixmap appropriately to whether 
	 * text is turned on in the toolbars, so it resizes okay */
	style = eel_gconf_get_integer (CONF_TOOLBAR_STYLE);

	icon_height = get_theme_pixmap_height ();

	/* downsize the image if it would bloat the toolbar */
	target_height = 0;
	switch (style)
	{
	case TOOLBAR_STYLE_TEXT_ONLY:
		target_height = 22;
		break;

	case TOOLBAR_STYLE_ICONS_ONLY:
	case TOOLBAR_STYLE_HORIZONTAL:
		target_height = icon_height;
		break;

	case TOOLBAR_STYLE_VERTICAL:
		target_height = icon_height + 16;
		break;
	}
	g_assert (target_height != 0);
	target_height = CLAMP (target_height, 24, height); 
	if (height > target_height)
	{
		/* preserve aspect ratio */
		width = (width * target_height) / height;
		height = target_height;
	}

	/* build a button to hold the spinner */
	spinner_button = gtk_button_new ();	
	gtk_button_set_relief (GTK_BUTTON (spinner_button), relief ?
			       GTK_RELIEF_NORMAL : GTK_RELIEF_NONE);
	GTK_WIDGET_UNSET_FLAGS (spinner_button, GTK_CAN_FOCUS);

	/* build the spinner and insert it into the button */
	spinner = gnome_animator_new_with_size (width, height);
	gtk_container_add (GTK_CONTAINER(spinner_button), spinner);

	/* should always loop continously when loading */
	gnome_animator_set_loop_type (GNOME_ANIMATOR (spinner),
				      GNOME_ANIMATOR_LOOP_RESTART);

	/* 999 is an arbitrary upper limit, in case something
	 * goes wrong with testing the existence of the files */
	for (i = 0; i < 1000; i++)
	{
		/* build the filename */
		if (i == 0)
		{
			filename = g_strdup_printf("%s/rest.png", anim_dir);
		}
		else
		{
			filename = g_strdup_printf("%s/%03d.png", anim_dir, i);
		}

		if ((g_file_exists(filename) & (G_FILE_TEST_ISFILE |
						G_FILE_TEST_ISLINK)) == 0)
		{
			/* free the string */
			g_free(filename);

			/* check if this is the first one and warn */
			if (i == 0)
			{
				g_warning("no frames of animation found in "
					  "directory `%s'", anim_dir);
				return NULL;
			}

			/* jump out of loading */
			break;
		}

		/* otherwise add it in */
		gnome_animator_append_frame_from_file_at_size
			(GNOME_ANIMATOR(spinner),
			 filename,       /* filename */
			 0, 0,           /* x_offset, y_offset */
			 70,            /* interval */
			 width, height); /* x_size, y_size */
 
		/* free allocated filename */
		g_free(filename);
	}

	/* attach click signal */
	gtk_signal_connect (GTK_OBJECT (spinner_button), "button-release-event",
			    spinner_button_released_cb, window);

	/* setup context menu */
	gtk_signal_connect (GTK_OBJECT (spinner_button), "button-press-event",
			    GTK_SIGNAL_FUNC
			    (window_generic_button_press_event_cb), window);

	/* show the spinner */
	gtk_widget_show (spinner_button);
	gtk_widget_show (spinner);

	/* free directory string */
	g_free (anim_dir);

	/* set in window */
	window->spinner = spinner;

	/* return button */
	return spinner_button;
}

/**
 * spinner_destroy: destroy the spinner of a window
 */
void
spinner_destroy (GaleonWindow *window)
{
	if (window->spinner != NULL)
	{
		gnome_animator_stop (GNOME_ANIMATOR (window->spinner));
		gtk_widget_destroy (window->spinner->parent);
		window->spinner = NULL;
	}
}

/**
 * spinner_start: start the spinner in a window, the spinner runs as
 * long as there are any active embeds within the window
 */
void
spinner_start (GaleonWindow *window)
{
	/* start the spinner animation */
	if (window->spinner && !window->spinner_running)
	{
		gnome_animator_start (GNOME_ANIMATOR (window->spinner));
		window->spinner_running = TRUE;
	}
}

/**
 * spinner_stop: stop the spinner (unless there are still active embeds)
 */
void
spinner_stop (GaleonWindow *window)
{
	/* stop the spinner animation and go to the initial frame */
	if (window->spinner && window->spinner_running)
	{
		gnome_animator_stop (GNOME_ANIMATOR (window->spinner));
		gnome_animator_goto_frame (GNOME_ANIMATOR (
					   window->spinner), 0);
		window->spinner_running = FALSE;
	}
}

static GList *spinner_list = NULL;

/**
 * Free any existing spinner list.
 */
static void
free_spinner_list (void)
{
	GList *node;

	for (node = spinner_list; node; node = node->next)
		g_free(node->data);
	g_list_free(spinner_list);
	spinner_list = NULL;
}

/**
 * spinner_fill_iconlist: fill a gnome icon list with icons of available spinners
 */
void
spinner_fill_iconlist (GnomeIconList *icon_list)
{
	gchar *home_spinner_dir;

	/* clear spinner list */
	free_spinner_list ();
	gnome_icon_list_clear (GNOME_ICON_LIST (icon_list));
	
	/* load all the galeon spinners */
	spinner_fill_iconlist_from_dir (icon_list, SHARE_DIR "/spinners");

	home_spinner_dir = g_strconcat (g_get_home_dir (),
					"/.galeon/spinners", NULL);
	spinner_fill_iconlist_from_dir (icon_list, home_spinner_dir);
	g_free (home_spinner_dir);

	/* load all the nautilus spinners */
	/* NB: we assume nautilus is installed with the same prefix as galeon */
	spinner_fill_iconlist_from_dir (icon_list, 
					SHARE_DIR "/../pixmaps/nautilus");
}

static void
spinner_fill_iconlist_from_dir (GnomeIconList *icon_list, gchar *base)
{
	int index;
	gchar *icon;
	gchar *pref_spinner_path; 	
	GList *node;
	GList *list = NULL;
	GnomeVFSResult rc;

	pref_spinner_path = 
		eel_gconf_get_string(CONF_TOOLBAR_SPINNER_DIR);	

	rc = gnome_vfs_directory_list_load
		(&list, base, (GNOME_VFS_FILE_INFO_GET_MIME_TYPE |
			       GNOME_VFS_FILE_INFO_FORCE_FAST_MIME_TYPE |
			       GNOME_VFS_FILE_INFO_FOLLOW_LINKS), NULL);
	
	index = icon_list->icons;
	for (node = list; node; node = node->next)
	{
		GnomeVFSFileInfo *info;
		gchar *path;
		gchar *ext_path;

		info = node->data;
		if (info->name[0] == '.')
			continue;
		if (info->type != GNOME_VFS_FILE_TYPE_DIRECTORY)
			continue;

		path = g_concat_dir_and_file (base, info->name);
		ext_path = g_concat_dir_and_file (path, "throbber");
		if (g_file_exists (ext_path))
		{
			g_free (path);
			path = ext_path;
		}
		else
		{
			g_free (ext_path);
		}

		/* Check to see if there is a rest.png */
		icon = g_concat_dir_and_file (path, "rest.png");
		if (g_file_exists (icon))
		{
			spinner_list = g_list_append (spinner_list, 
						      g_strdup (path));

			/* Name defaults to the directory name */
			gnome_icon_list_append (icon_list, icon, info->name);

			/* Select the icon configured in prefs */
			if (pref_spinner_path && 
			    strcmp (pref_spinner_path, path) == 0)
			{
				gnome_icon_list_select_icon (icon_list, index);
			}
			index++;
		}

		g_free (path);
		g_free (icon);
	}

	gnome_vfs_file_info_list_free(list);
	g_free(pref_spinner_path);
}

/**
 * spinner_get_path_from_index: used in prefs_callbacks.c to get the 
 * path of selected icon
 */
const gchar *
spinner_get_path_from_index (gint index)
{
	gchar *path;

	path = g_list_nth_data (spinner_list, index);

	return path;
}

/**
 * spinner_button_released_cb: spinner clicked
 */
static void
spinner_button_released_cb (GtkButton *button,
			    GdkEventButton *event,
			    GaleonWindow *window)
{
	return_if_not_window (window);

	if (GTK_IS_BUTTON (button) && event->button == 1 && 
            !GTK_BUTTON (button)->button_down) 
        	return; 
	if (event->button > 3) 
		return; 
	if (GTK_WIDGET (button)->window != event->window) 
		return;

	embed_activate_link (window->active_embed, NULL, GALEON_HOMEPAGE_URL,
		mouse_state_to_link_state (event->button, event->state));
}
