/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* e-folder-type-registry.c
 *
 * Copyright (C) 2000, 2001 Ximian, Inc.
 *
 * 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 of the
 * License, 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.
 *
 * Author: Ettore Perazzoli
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <glib.h>
#include <gtk/gtktypeutils.h>

#include <gal/util/e-util.h>

#include "e-shell-utils.h"

#include "e-folder-type-registry.h"


#define PARENT_TYPE GTK_TYPE_OBJECT
static GtkObjectClass *parent_class = NULL;

struct _FolderType {
	char *name;
	char *icon_name;

	char *display_name;
	char *description;

	gboolean user_creatable;

	GList *exported_dnd_types; /* char * */
	GList *accepted_dnd_types; /* char * */

	EvolutionShellComponentClient *handler;

	/* The icon, standard (48x48) and mini (16x16) versions.  */
	GdkPixbuf *icon_pixbuf;
	GdkPixbuf *mini_icon_pixbuf;
};
typedef struct _FolderType FolderType;

struct _EFolderTypeRegistryPrivate {
	GHashTable *name_to_type;
};


/* FolderType handling.  */

static FolderType *
folder_type_new (const char *name,
		 const char *icon_name,
		 const char *display_name,
		 const char *description,
		 gboolean user_creatable,
		 int num_exported_dnd_types,
		 const char **exported_dnd_types,
		 int num_accepted_dnd_types,
		 const char **accepted_dnd_types)
{
	FolderType *new;
	char *icon_path;
	int i;

	new = g_new (FolderType, 1);

	new->name           = g_strdup (name);
	new->icon_name      = g_strdup (icon_name);
	new->display_name   = g_strdup (display_name);
	new->description    = g_strdup (description);

	new->user_creatable = user_creatable;

	new->exported_dnd_types = NULL;
	for (i = 0; i < num_exported_dnd_types; i++)
		new->exported_dnd_types = g_list_prepend (new->exported_dnd_types,
							  g_strdup (exported_dnd_types[i]));
	new->exported_dnd_types = g_list_reverse (new->exported_dnd_types);

	new->accepted_dnd_types = NULL;
	for (i = 0; i < num_accepted_dnd_types; i++)
		new->accepted_dnd_types = g_list_prepend (new->accepted_dnd_types,
							  g_strdup (accepted_dnd_types[i]));
	new->accepted_dnd_types = g_list_reverse (new->accepted_dnd_types);

	new->handler = NULL;

	icon_path = e_shell_get_icon_path (icon_name, FALSE);
	if (icon_path == NULL)
		new->icon_pixbuf = NULL;
	else
		new->icon_pixbuf = gdk_pixbuf_new_from_file (icon_path);

	g_free (icon_path);

	icon_path = e_shell_get_icon_path (icon_name, TRUE);
	if (icon_path != NULL) {
		new->mini_icon_pixbuf = gdk_pixbuf_new_from_file (icon_path);
	} else {
		if (new->icon_pixbuf != NULL)
			new->mini_icon_pixbuf = gdk_pixbuf_ref (new->icon_pixbuf);
		else
			new->mini_icon_pixbuf = NULL;
	}

	g_free (icon_path);

	return new;
}

static void
folder_type_free (FolderType *folder_type)
{
	g_free (folder_type->name);
	g_free (folder_type->icon_name);
	g_free (folder_type->display_name);
	g_free (folder_type->description);

	if (folder_type->icon_pixbuf != NULL)
		gdk_pixbuf_unref (folder_type->icon_pixbuf);
	if (folder_type->mini_icon_pixbuf != NULL)
		gdk_pixbuf_unref (folder_type->mini_icon_pixbuf);

	if (folder_type->handler != NULL)
		bonobo_object_unref (BONOBO_OBJECT (folder_type->handler));

	g_free (folder_type);
}

static FolderType *
get_folder_type (EFolderTypeRegistry *folder_type_registry,
		 const char *type_name)
{
	EFolderTypeRegistryPrivate *priv;

	priv = folder_type_registry->priv;

	return g_hash_table_lookup (priv->name_to_type, type_name);
}

static gboolean
register_folder_type (EFolderTypeRegistry *folder_type_registry,
		      const char *name,
		      const char *icon_name,
		      const char *display_name,
		      const char *description,
		      gboolean user_creatable,
		      int num_exported_dnd_types,
		      const char **exported_dnd_types,
		      int num_accepted_dnd_types,
		      const char **accepted_dnd_types)
{
	EFolderTypeRegistryPrivate *priv;
	FolderType *folder_type;

	priv = folder_type_registry->priv;

	/* Make sure we don't add the same type twice.  */
	if (get_folder_type (folder_type_registry, name) != NULL)
		return FALSE;

	folder_type = folder_type_new (name, icon_name,
				       display_name, description,
				       user_creatable,
				       num_exported_dnd_types, exported_dnd_types,
				       num_accepted_dnd_types, accepted_dnd_types);
	g_hash_table_insert (priv->name_to_type, folder_type->name, folder_type);

	return TRUE;
}

static gboolean
set_handler (EFolderTypeRegistry *folder_type_registry,
	     const char *name,
	     EvolutionShellComponentClient *handler)
{
	EFolderTypeRegistryPrivate *priv;
	FolderType *folder_type;

	priv = folder_type_registry->priv;

	folder_type = get_folder_type (folder_type_registry, name);
	if (folder_type == NULL)
		return FALSE;
	if (folder_type->handler != NULL) {
		g_warning ("Folder type already has a handler -- %s",
			   folder_type->name);
		return FALSE;
	}

	bonobo_object_ref (BONOBO_OBJECT (handler));
	folder_type->handler = handler;

	return TRUE;
}


/* GtkObject methods.  */

static void
hash_forall_free_folder_type (gpointer key,
			      gpointer value,
			      gpointer data)
{
	FolderType *folder_type;

	folder_type = (FolderType *) value;
	folder_type_free (folder_type);
}

static void
destroy (GtkObject *object)
{
	EFolderTypeRegistry *folder_type_registry;
	EFolderTypeRegistryPrivate *priv;

	folder_type_registry = E_FOLDER_TYPE_REGISTRY (object);
	priv = folder_type_registry->priv;

	g_hash_table_foreach (priv->name_to_type,
			      hash_forall_free_folder_type, NULL);
	g_hash_table_destroy (priv->name_to_type);

	g_free (priv);

	(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}


static void
class_init (EFolderTypeRegistryClass *class)
{
	GtkObjectClass *object_class;

	object_class = GTK_OBJECT_CLASS (class);
	object_class->destroy = destroy;

	parent_class = gtk_type_class (gtk_object_get_type ());
}

static void
init (EFolderTypeRegistry *folder_type_registry)
{
	EFolderTypeRegistryPrivate *priv;

	priv = g_new (EFolderTypeRegistryPrivate, 1);
	priv->name_to_type = g_hash_table_new (g_str_hash, g_str_equal);

	folder_type_registry->priv = priv;
}


void
e_folder_type_registry_construct (EFolderTypeRegistry *folder_type_registry)
{
	g_return_if_fail (folder_type_registry != NULL);
	g_return_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry));

	GTK_OBJECT_UNSET_FLAGS (GTK_OBJECT (folder_type_registry), GTK_FLOATING);
}

EFolderTypeRegistry *
e_folder_type_registry_new (void)
{
	EFolderTypeRegistry *new;

	new = gtk_type_new (e_folder_type_registry_get_type ());

	e_folder_type_registry_construct (new);

	return new;
}


gboolean
e_folder_type_registry_register_type (EFolderTypeRegistry *folder_type_registry,
				      const char *type_name,
				      const char *icon_name,
				      const char *display_name,
				      const char *description,
				      gboolean user_creatable,
				      int num_exported_dnd_types,
				      const char **exported_dnd_types,
				      int num_accepted_dnd_types,
				      const char **accepted_dnd_types)
{
	g_return_val_if_fail (folder_type_registry != NULL, FALSE);
	g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), FALSE);
	g_return_val_if_fail (type_name != NULL, FALSE);
	g_return_val_if_fail (icon_name != NULL, FALSE);

	return register_folder_type (folder_type_registry, type_name, icon_name,
				     display_name, description, user_creatable,
				     num_exported_dnd_types, exported_dnd_types,
				     num_accepted_dnd_types, accepted_dnd_types);
}

gboolean
e_folder_type_registry_set_handler_for_type  (EFolderTypeRegistry *folder_type_registry,
					      const char *type_name,
					      EvolutionShellComponentClient *handler)
{
	g_return_val_if_fail (folder_type_registry != NULL, FALSE);
	g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), FALSE);
	g_return_val_if_fail (handler != NULL, FALSE);
	g_return_val_if_fail (BONOBO_IS_OBJECT_CLIENT (handler), FALSE);

	return set_handler (folder_type_registry, type_name, handler);
}


gboolean
e_folder_type_register_type_registered  (EFolderTypeRegistry *folder_type_registry,
					 const char *type_name)
{
	EFolderTypeRegistryPrivate *priv;

	g_return_val_if_fail (folder_type_registry != NULL, FALSE);
	g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), FALSE);
	g_return_val_if_fail (type_name != NULL, FALSE);

	priv = folder_type_registry->priv;

	if (get_folder_type (folder_type_registry, type_name) == NULL)
		return FALSE;

	return TRUE;
}

void
e_folder_type_register_unregister_type (EFolderTypeRegistry *folder_type_registry,
					const char *type_name)
{
	EFolderTypeRegistryPrivate *priv;
	FolderType *folder_type;

	g_return_if_fail (folder_type_registry != NULL);
	g_return_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry));
	g_return_if_fail (type_name != NULL);

	priv = folder_type_registry->priv;

	folder_type = get_folder_type (folder_type_registry, type_name);
	if (folder_type == NULL) {
		g_warning ("e_folder_type_register_unregister_type(): cannot find type `%s'\n",
			   type_name);
		return;
	}

	g_hash_table_remove (priv->name_to_type, folder_type->name);
	folder_type_free (folder_type);
}


static void
get_type_names_hash_forall (void *key,
			    void *value,
			    void *data)
{
	GList **type_name_list;

	type_name_list = (GList **) data;

	*type_name_list = g_list_prepend (*type_name_list, g_strdup ((const char *) key));
}

GList *
e_folder_type_registry_get_type_names (EFolderTypeRegistry *folder_type_registry)
{
	GList *type_name_list;
	EFolderTypeRegistryPrivate *priv;

	g_return_val_if_fail (folder_type_registry != NULL, NULL);
	g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), NULL);

	priv = folder_type_registry->priv;

	type_name_list = NULL;
	g_hash_table_foreach (priv->name_to_type, get_type_names_hash_forall, &type_name_list);

	return type_name_list;
}


const char *
e_folder_type_registry_get_icon_name_for_type (EFolderTypeRegistry *folder_type_registry,
					       const char *type_name)
{
	const FolderType *folder_type;

	g_return_val_if_fail (folder_type_registry != NULL, NULL);
	g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), NULL);
	g_return_val_if_fail (type_name != NULL, NULL);

	folder_type = get_folder_type (folder_type_registry, type_name);
	if (folder_type == NULL) {
		g_warning ("e_folder_type_registry_get_icon_name_for_type() -- Unknown type `%s'", type_name);
		return NULL;
	}

	return folder_type->icon_name;
}

GdkPixbuf *
e_folder_type_registry_get_icon_for_type (EFolderTypeRegistry *folder_type_registry,
					  const char *type_name,
					  gboolean mini)
{
	const FolderType *folder_type;

	g_return_val_if_fail (folder_type_registry != NULL, NULL);
	g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), NULL);
	g_return_val_if_fail (type_name != NULL, NULL);

	folder_type = get_folder_type (folder_type_registry, type_name);
	if (folder_type == NULL) {
		g_warning ("e_folder_type_registry_get_icon_for_type() -- Unknown type `%s'", type_name);
		return NULL;
	}

	if (mini)
		return folder_type->mini_icon_pixbuf;
	else
		return folder_type->icon_pixbuf;
}

EvolutionShellComponentClient *
e_folder_type_registry_get_handler_for_type (EFolderTypeRegistry *folder_type_registry,
					     const char *type_name)
{
	const FolderType *folder_type;

	g_return_val_if_fail (folder_type_registry != NULL, NULL);
	g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), NULL);
	g_return_val_if_fail (type_name != NULL, NULL);

	folder_type = get_folder_type (folder_type_registry, type_name);
	if (folder_type == NULL) {
		g_warning ("e_folder_type_registry_get_handler_for_type() -- Unknown type `%s'", type_name);
		return NULL;
	}

	return folder_type->handler;
}

gboolean
e_folder_type_registry_type_is_user_creatable  (EFolderTypeRegistry *folder_type_registry,
						const char          *type_name)
{
	const FolderType *folder_type;

	g_return_val_if_fail (folder_type_registry != NULL, FALSE);
	g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), FALSE);
	g_return_val_if_fail (type_name != NULL, FALSE);

	folder_type = get_folder_type (folder_type_registry, type_name);
	if (folder_type == NULL) {
		g_warning ("e_folder_type_registry_type_is_user_creatable() -- Unknown type `%s'", type_name);
		return FALSE;
	}

	return folder_type->user_creatable;
}

const char *
e_folder_type_registry_get_display_name_for_type (EFolderTypeRegistry *folder_type_registry,
						  const char *type_name)
{
	const FolderType *folder_type;

	g_return_val_if_fail (folder_type_registry != NULL, NULL);
	g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), NULL);
	g_return_val_if_fail (type_name != NULL, NULL);

	folder_type = get_folder_type (folder_type_registry, type_name);
	if (folder_type == NULL) {
		g_warning ("e_folder_type_registry_type_get_display_name_for_type() -- Unknown type `%s'", type_name);
		return FALSE;
	}

	return folder_type->display_name;
}

const char *
e_folder_type_registry_get_description_for_type (EFolderTypeRegistry *folder_type_registry,
						 const char *type_name)
{
	const FolderType *folder_type;

	g_return_val_if_fail (folder_type_registry != NULL, NULL);
	g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), NULL);
	g_return_val_if_fail (type_name != NULL, NULL);

	folder_type = get_folder_type (folder_type_registry, type_name);
	if (folder_type == NULL) {
		g_warning ("e_folder_type_registry_get_description_for_type() -- Unknown type `%s'", type_name);
		return FALSE;
	}

	return folder_type->description;
}


GList *
e_folder_type_registry_get_exported_dnd_types_for_type (EFolderTypeRegistry *folder_type_registry,
							const char *type_name)
{
	const FolderType *folder_type;

	g_return_val_if_fail (folder_type_registry != NULL, NULL);
	g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), NULL);
	g_return_val_if_fail (type_name != NULL, NULL);

	folder_type = get_folder_type (folder_type_registry, type_name);
	if (folder_type == NULL) {
		g_warning ("e_folder_type_registry_get_exported_dnd_types_for_type() -- Unknown type `%s'", type_name);
		return NULL;
	}

	return folder_type->exported_dnd_types;
}

GList *
e_folder_type_registry_get_accepted_dnd_types_for_type (EFolderTypeRegistry *folder_type_registry,
							const char *type_name)
{
	const FolderType *folder_type;

	g_return_val_if_fail (folder_type_registry != NULL, NULL);
	g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), NULL);
	g_return_val_if_fail (type_name != NULL, NULL);

	folder_type = get_folder_type (folder_type_registry, type_name);
	if (folder_type == NULL) {
		g_warning ("e_folder_type_registry_get_accepted_dnd_types_for_type() -- Unknown type `%s'", type_name);
		return NULL;
	}

	return folder_type->accepted_dnd_types;
}


E_MAKE_TYPE (e_folder_type_registry, "EFolderTypeRegistry", EFolderTypeRegistry,
	     class_init, init, PARENT_TYPE)
