/*
 *  Copyright (C) 2000, 2001, 2002 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.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "galeon-embed-helper-list.h"
#include "galeon-embed-persist.h"
#include "gul-bonobo-extensions.h"
#include "gul-string.h"
#include "gul-general.h"

#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgnomevfs/gnome-vfs-mime-handlers.h>
#include <libgnomevfs/gnome-vfs-mime.h>
#include <libgnomevfs/gnome-vfs-mime-info.h>
#include <libgnomevfs/gnome-vfs-utils.h>
#include <glib/gi18n.h>
#include <gtk/gtkmenuitem.h>

static void
galeon_embed_helper_list_class_init (GaleonEmbedHelperListClass *klass);
static void
galeon_embed_helper_list_init (GaleonEmbedHelperList *window);
static void
galeon_embed_helper_list_finalize (GObject *object);
static void
galeon_embed_helper_list_get_property (GObject *object,
             	            	       guint prop_id,
                            	       GValue *value,
                            	       GParamSpec *pspec);
static void
galeon_embed_helper_list_set_property (GObject *object,
             	            	       guint prop_id,
                            	       const GValue *value,
                            	       GParamSpec *pspec);

enum
{
	PROP_0,
	PROP_GALEON_EMBED
};

struct GaleonEmbedHelperListPrivate
{
	GaleonEmbed *embed;
	GList *mime_types;
	char *uri;
};

typedef struct {
        GnomeVFSMimeApplication *application;
        char *url;
        GaleonEmbedHelperList *hl;
} ApplicationLaunchParameters;

static GObjectClass *parent_class = NULL;

GType 
galeon_embed_helper_list_get_type (void)
{
        static GType galeon_embed_helper_list_type = 0;

        if (galeon_embed_helper_list_type == 0)
        {
                static const GTypeInfo our_info =
                {
                        sizeof (GaleonEmbedHelperListClass),
                        NULL, /* base_init */
                        NULL, /* base_finalize */
                        (GClassInitFunc) galeon_embed_helper_list_class_init,
                        NULL,
                        NULL, /* class_data */
                        sizeof (GaleonEmbedHelperList),
                        0, /* n_preallocs */
                        (GInstanceInitFunc) galeon_embed_helper_list_init
                };

                galeon_embed_helper_list_type = g_type_register_static (G_TYPE_OBJECT,
                                                             	        "GaleonEmbedHelperList",
                                                             	        &our_info, 0);
        }

        return galeon_embed_helper_list_type;
}

static void
galeon_embed_helper_list_class_init (GaleonEmbedHelperListClass *klass)
{
        GObjectClass *object_class = G_OBJECT_CLASS (klass);

        parent_class = g_type_class_peek_parent (klass);

        object_class->finalize = galeon_embed_helper_list_finalize;
	object_class->set_property = galeon_embed_helper_list_set_property;
	object_class->get_property = galeon_embed_helper_list_get_property;
	
	g_object_class_install_property (object_class,
					 PROP_GALEON_EMBED,
                                         g_param_spec_object ("GaleonEmbed",
                                                              "GaleonEmbed",
                                                              "Galeon Embed",
                                                              G_TYPE_OBJECT,
                                                              G_PARAM_READWRITE));
}

static void
galeon_embed_helper_list_init (GaleonEmbedHelperList *dialog)
{
        dialog->priv = g_new0 (GaleonEmbedHelperListPrivate, 1);

	dialog->priv->embed = NULL;
	dialog->priv->uri = NULL;
}

static void
free_mime_types_list (GaleonEmbedHelperList *hl)
{
	g_list_foreach (hl->priv->mime_types, (GFunc) g_free, NULL);
	g_list_free (hl->priv->mime_types);
	hl->priv->mime_types = NULL;
}

static void
galeon_embed_helper_list_finalize (GObject *object)
{
        GaleonEmbedHelperList *dialog;

        g_return_if_fail (object != NULL);
        g_return_if_fail (IS_GALEON_EMBED_HELPER_LIST (object));

        dialog = GALEON_EMBED_HELPER_LIST (object);

        g_return_if_fail (dialog->priv != NULL);

	g_free (dialog->priv->uri);
	
        g_free (dialog->priv);

        G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
galeon_embed_helper_list_set_property (GObject *object,
                              	       guint prop_id,
                            	       const GValue *value,
                            	       GParamSpec *pspec)
{
        GaleonEmbedHelperList *d = GALEON_EMBED_HELPER_LIST (object);

        switch (prop_id)
        {
                case PROP_GALEON_EMBED:
                        galeon_embed_helper_list_set_embed (d, g_value_get_object (value));
                        break;
        }
}

static void
galeon_embed_helper_list_get_property (GObject *object,
             	            	       guint prop_id,
                            	       GValue *value,
                            	       GParamSpec *pspec)
{
        GaleonEmbedHelperList *d = GALEON_EMBED_HELPER_LIST (object);

        switch (prop_id)
        {
                case PROP_GALEON_EMBED:
                        g_value_set_object (value, d->priv->embed);
                        break;
        }
}

GaleonEmbedHelperList *
galeon_embed_helper_list_new (void)
{
	return GALEON_EMBED_HELPER_LIST (g_object_new (GALEON_EMBED_HELPER_LIST_TYPE, 
						       NULL));
}

void
galeon_embed_helper_list_set_embed (GaleonEmbedHelperList *hl,
			            GaleonEmbed *embed)
{
	hl->priv->embed = embed;
}

gboolean
galeon_embed_helper_list_set_uri (GaleonEmbedHelperList *hl,
				  const char *uri)
{
	char *mime = NULL;
	GnomeVFSURI *vfs_uri;
	
	if (hl->priv->uri) g_free (hl->priv->uri);
	hl->priv->uri = g_strdup (uri);

	free_mime_types_list (hl);

	vfs_uri = gnome_vfs_uri_new (uri);
	if (vfs_uri)
	{
		mime = g_strdup (gnome_vfs_get_mime_type_from_uri (vfs_uri));
		gnome_vfs_uri_unref (vfs_uri);
	}
	
	if (mime && strcmp (mime, "application/octet-stream") != 0)
	{
		galeon_embed_helper_list_add_mime_type (hl, mime);
	}
	else
	{
		return FALSE;
	}

	return TRUE;
}

void
galeon_embed_helper_list_add_mime_type (GaleonEmbedHelperList *hl,
					const char *mime_type)
{
	/* do not add duped mime types */
	if (g_list_find_custom (hl->priv->mime_types,
                                mime_type,
                                gul_strcasecmp_compare_func))
	{
		return;
	}

	hl->priv->mime_types = g_list_append (hl->priv->mime_types,
					      g_strdup (mime_type));
}

static ApplicationLaunchParameters *
application_launch_parameters_new (GnomeVFSMimeApplication *application,
				   const char *url,
				   GaleonEmbedHelperList *hl)
{
        ApplicationLaunchParameters *result;

        result = g_new0 (ApplicationLaunchParameters, 1);
        result->application = gnome_vfs_mime_application_copy (application);
	result->hl = hl;
	g_object_ref (result->hl);
	result->url = url ? g_strdup (url) : NULL;

        return result;
}

static void
application_launch_parameters_free (ApplicationLaunchParameters *parameters)
{
	gnome_vfs_mime_application_free (parameters->application);
	g_free (parameters->url);
	g_object_unref (parameters->hl);
	g_free (parameters);
}         

static char *
generate_temp_file_name (const char *ext)
{
	char *base, *result;
	
	base = g_build_filename (g_get_tmp_dir (),
				 "galeon-openwith-XXXXXX", NULL);
	result = gul_general_tmp_filename (base, ext);
	g_free (base);
	
	return result;
}

static void
open_with_temp (GaleonEmbedHelperList *hl,
		const char *location,
		GnomeVFSMimeApplication *app)
{
	GaleonEmbedPersist *persist;
	const char *mime;
	GList *ext;
	char *final_filename;
	
	g_assert (hl->priv->mime_types != NULL);
	mime = (const char *)hl->priv->mime_types->data;

	ext = gnome_vfs_mime_get_extensions_list (mime);
	final_filename = generate_temp_file_name 
		(ext ? (char *)ext->data : NULL);
	gnome_vfs_mime_extensions_list_free (ext);
		
	persist = galeon_embed_persist_new (hl->priv->embed);
	galeon_embed_persist_set_source (persist, location);
	galeon_embed_persist_set_dest (persist, final_filename);
	galeon_embed_persist_set_handler (persist, app->command, 
					  app->requires_terminal);
	galeon_embed_persist_save (persist);
	g_object_unref (G_OBJECT (persist));

	g_free (final_filename);
}

static gboolean
application_supports_uri_scheme (GnomeVFSMimeApplication *application,
                                 const char *uri_scheme)
{
        /* The default supported uri scheme is "file" */
        if (application->supported_uri_schemes == NULL
            && g_ascii_strcasecmp (uri_scheme, "file") == 0) {
                return TRUE;
        }
        return g_list_find_custom (application->supported_uri_schemes,
                                   uri_scheme,
                                   gul_strcasecmp_compare_func) != NULL;
}

static gboolean
open_directly (const char *location,
	       GnomeVFSMimeApplication *application)
{
	const char *scheme = NULL;
	GnomeVFSURI *uri;
	char *final_location;
	gboolean result = FALSE;
	
	uri = gnome_vfs_uri_new (location);
	if (uri)
	{
		scheme = gnome_vfs_uri_get_scheme (uri);
	}
	
	final_location = gnome_vfs_get_local_path_from_uri (location);
	if (!final_location)
	{
		final_location = g_strdup (location);
	}
	
	if (scheme && application_supports_uri_scheme (application, scheme))
	{
		gul_general_launch_application (application->command,
						final_location,
						application->requires_terminal);
		result = TRUE;
	}

	g_free (final_location);
	if (uri) gnome_vfs_uri_unref (uri);
		
	return result;
}

static void
bonobo_launch_application_callback (BonoboUIComponent *component, gpointer callback_data, const char *path)
{
        ApplicationLaunchParameters *launch_parameters; 
	
	launch_parameters = (ApplicationLaunchParameters *) callback_data;	
	
	if (!open_directly (launch_parameters->url, 
			    launch_parameters->application))
	{	
		open_with_temp (launch_parameters->hl,
				launch_parameters->url,
				launch_parameters->application);
	}
}       

static void
add_numbered_menu_item (BonoboUIComponent *ui,
                        const char *parent_path,
                        const char *label,
                        const char *tip,
                        int index,
                        GdkPixbuf *pixbuf,
                        gpointer callback,
                        gpointer callback_data,
                        GDestroyNotify destroy_notify)
{
        char *escaped_parent_path, *escaped_label, *verb_name, *item_path;
        
        escaped_parent_path = gul_string_double_underscores (parent_path);

        escaped_label = gul_string_double_underscores (label);
        gul_bonobo_add_numbered_menu_item 
                (ui, 
                 escaped_parent_path,
                 index,
                 escaped_label, 
                 pixbuf);
        g_free (escaped_label);
        item_path = gul_bonobo_get_numbered_menu_item_path
                (ui, escaped_parent_path, index);
        gul_bonobo_set_tip (ui, item_path, tip);
        g_free (item_path);

        verb_name = gul_bonobo_get_numbered_menu_item_command 
                (ui, escaped_parent_path, index);       
        bonobo_ui_component_add_verb_full (ui, verb_name,
                                           g_cclosure_new (callback, callback_data,
                                                           (GClosureNotify) destroy_notify));      
        g_free (verb_name);

        g_free (escaped_parent_path);
}

static void
add_application_to_bonobo_menu (GaleonEmbedHelperList *hl,
				BonoboUIComponent *ui_component,
                                GnomeVFSMimeApplication *application, 
				const char *path,
				const char *url,
				int index)
{
        ApplicationLaunchParameters *launch_parameters;
        char *tip;

        launch_parameters = application_launch_parameters_new 
                (application, url, hl);
        tip = g_strdup_printf (_("Use \"%s\" to open the selected item"), application->name);

        add_numbered_menu_item (ui_component, 
                                path,
                                application->name,
                                tip,
                                index,
                                NULL,
                                bonobo_launch_application_callback,
                                launch_parameters,
                                (GDestroyNotify) application_launch_parameters_free);
        g_free (tip);
}

static void
activate_cb (GtkMenuItem *mi, ApplicationLaunchParameters *launch_parameters)
{
	if (!open_directly (launch_parameters->url, 
			    launch_parameters->application))
	{	
		open_with_temp (launch_parameters->hl,
				launch_parameters->url,
				launch_parameters->application);
	}
}       

static void
add_application_to_gtk_menu (GaleonEmbedHelperList *hl,
			     GtkMenuShell *ms,
			     GnomeVFSMimeApplication *application, 
			     const char *url)
{
        ApplicationLaunchParameters *launch_parameters;
	GtkWidget *mi;

        launch_parameters = application_launch_parameters_new (application, url, hl);

	mi = gtk_menu_item_new_with_label (application->name);
	gtk_widget_show (mi);
	gtk_menu_shell_append (ms, mi);
	g_signal_connect_data (mi, "activate", 
			       G_CALLBACK (activate_cb), 
			       launch_parameters, (GClosureNotify) application_launch_parameters_free,
			       (GConnectFlags) 0);
}

static void
setup_openwith_list_bonoboui (GaleonEmbedHelperList *hl,
			      BonoboUIComponent *ui_component,
			      const char *path)
{
	int index = 0;
	GList *apps;
	GList *tl;
	
	for (tl = hl->priv->mime_types; tl != NULL; tl = tl->next)
	{
		GList *l;
		
		apps = gnome_vfs_mime_get_short_list_applications 
			((char *)tl->data);
		
		for (l = apps; l != NULL; l = l->next)
		{
			add_application_to_bonobo_menu 
				(hl, ui_component, 
				 (GnomeVFSMimeApplication *)l->data,
				 path, hl->priv->uri, index);
			index ++;
		}
		
		gnome_vfs_mime_application_list_free (apps);
	}
}
	
void
galeon_embed_helper_list_add_to_bonoboui_menu (GaleonEmbedHelperList *hl,
					       BonoboUIComponent *ui_component,
					       const char *path)
{
	setup_openwith_list_bonoboui (hl, ui_component, path);
}

void
galeon_embed_helper_list_add_to_gtk_menu (GaleonEmbedHelperList *hl,
					  GtkMenuShell *ms)
{
	GList *tl;
	
	for (tl = hl->priv->mime_types; tl != NULL; tl = tl->next)
	{
		GList *apps = gnome_vfs_mime_get_short_list_applications (tl->data);
		GList *l;
		
		for (l = apps; l != NULL; l = l->next)
		{
			add_application_to_gtk_menu (hl, ms, l->data, hl->priv->uri);
		}
		
		gnome_vfs_mime_application_list_free (apps);
	}
}

