/*
 *  Copyright (C) 2003 Marco Pesenti Gritti
 *  Copyright (C) 2003 Christian Persch
 *  Copyright (C) 2003 Crispin Flowerday
 *
 *  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-encodings.h"
#include "eel-gconf-extensions.h"

#include <glib/gi18n.h>
#include <string.h>

struct GaleonEncodingsPrivate
{
	GList      *encodings;
	GHashTable *encodings_hash;
	GSList     *recent;

	/* List of auto-created encodings - needs freeing */
	GList      *auto_created;
};

#define RECENT_KEY	"/apps/galeon/Rendering/Language/recent_encodings"
#define RECENT_MAX	4

static GObjectClass *parent_class = NULL;

static GList *
galeon_encodings_get_all_encodings (GaleonEncodings *encodings)
{
	GaleonEncodingsClass *klass = GALEON_ENCODINGS_GET_CLASS (encodings);
        return klass->get_all_encodings (encodings);
}

static void
galeon_encodings_ensure_encodings (GaleonEncodings *encodings)
{
	GList *list, *l;
	GSList *slist, *li;

	g_return_if_fail (GALEON_IS_ENCODINGS (encodings));
	if (encodings->priv->encodings) return;

	encodings->priv->encodings_hash = g_hash_table_new (g_str_hash, g_str_equal);

	list = galeon_encodings_get_all_encodings (encodings);

	for (l = list; l != NULL; l = l->next)
	{
		GaleonEncodingInfo *info = (GaleonEncodingInfo*)(l->data);

		g_hash_table_insert (encodings->priv->encodings_hash,
				     (char*)info->encoding, info);

		encodings->priv->encodings = g_list_prepend (encodings->priv->encodings,
							     info);
	}

	g_list_free (list);

	/* get the list of recently used encodings */
	slist = eel_gconf_get_string_list (RECENT_KEY);

	/* make sure the list has no duplicates (GtkUIManager goes
	 * crazy otherwise), and only valid entries
	 */
	encodings->priv->recent = NULL;
	for (li = slist; li != NULL; li = li->next)
	{
		if (g_slist_find (encodings->priv->recent, li->data) == NULL
		    && g_slist_length (encodings->priv->recent) < RECENT_MAX
		    && galeon_encodings_get_encoding (encodings, li->data) != NULL)
		{
			encodings->priv->recent =
				g_slist_prepend (encodings->priv->recent,
						 li->data);
		}
		else
		{
			g_free (li->data);
		}
	}
	encodings->priv->recent = g_slist_reverse (encodings->priv->recent);
	g_slist_free (slist);
}

/**
 * Get the GaleonEncodingInfo structure for the specified encoding.
 * this structure must not be freed.
 */
const GaleonEncodingInfo*
galeon_encodings_get_encoding  (GaleonEncodings *encodings,
				const char *code)
{
	GaleonEncodingInfo* info;

	g_return_val_if_fail (GALEON_IS_ENCODINGS (encodings), NULL);
	g_return_val_if_fail (code != NULL, NULL);

	galeon_encodings_ensure_encodings (encodings);
	
	info = g_hash_table_lookup (encodings->priv->encodings_hash,
				   code);
	if (info) return info;

	/* No built-in encoding, so make one up */
        info = g_new0 (GaleonEncodingInfo, 1);
	info->encoding = g_strdup (code);

	/* translators: this is the title that an unknown encoding will
	 * be displayed as. */
	info->title = g_strdup_printf (N_("Unknown (%s)"), code);

	encodings->priv->auto_created = g_list_prepend (encodings->priv->auto_created, info);
	g_hash_table_insert (encodings->priv->encodings_hash,
			     (char*)info->encoding, info);
	encodings->priv->encodings = g_list_prepend (encodings->priv->encodings, info);
	return info;
}

/**
 * Get a list of encodings that match the specified group. Use
 * LG_ALL for all encodings. The list contains const GaleonEncodingInfo
 * structures, and the text is not translated.
 */
GList *
galeon_encodings_get_encodings (GaleonEncodings *encodings,
				GaleonLanguageGroup group_mask)
{
	GList *l, *list = NULL;
	galeon_encodings_ensure_encodings (encodings);	

	for(l = encodings->priv->encodings ; l ; l = l->next )
	{
		GaleonEncodingInfo *info = (GaleonEncodingInfo*)(l->data);
		if (group_mask == LG_ALL || (info->group & group_mask))
		{
			list = g_list_prepend (list, info);
		}
	}

	return list;
}

/**
 *  Add an encoding to the list of recently used encodings
 */
void
galeon_encodings_add_recent (GaleonEncodings *encodings,
			     const char *code)
{
	GSList *element;
	galeon_encodings_ensure_encodings (encodings);

	g_return_if_fail (GALEON_IS_ENCODINGS (encodings));
	g_return_if_fail (code != NULL);
	g_return_if_fail (galeon_encodings_get_encoding (encodings, code) != NULL);

	/* keep the list elements unique */
	element = g_slist_find_custom (encodings->priv->recent, code,
				       (GCompareFunc) strcmp);
	if (element != NULL)
	{
		g_free (element->data);
		encodings->priv->recent =
			g_slist_remove_link (encodings->priv->recent, element);
	}

	/* add the new code upfront */
	encodings->priv->recent =
		g_slist_prepend (encodings->priv->recent, g_strdup (code));

	/* truncate the list if necessary; it's at most 1 element too much */
	if (g_slist_length (encodings->priv->recent) > RECENT_MAX)
	{
		GSList *tail;

		tail = g_slist_last (encodings->priv->recent);
		g_free (tail->data);
		encodings->priv->recent =
			g_slist_remove_link (encodings->priv->recent, tail);
	}

	/* persist the list */
	eel_gconf_set_string_list (RECENT_KEY, encodings->priv->recent);
}

/**
 * Get a list of the recent encodings that have been used. The
 * list contains pointer to GaleonEncodingInfo structures. The
 * text in the GaleonEncodingInfo is NOT translated, and the 
 * structures must NOT be freed
 */
GList *
galeon_encodings_get_recent (GaleonEncodings *encodings)
{
	GSList *l;
	GList *list = NULL;

	for (l = encodings->priv->recent; l != NULL; l = l->next)
	{
		const GaleonEncodingInfo *info;

		info = galeon_encodings_get_encoding (encodings, (char *) l->data);
		g_return_val_if_fail (info, NULL);

		list = g_list_prepend (list, (GaleonEncodingInfo *)info);
	}
	return g_list_reverse (list);
}

static void
galeon_encodings_init (GaleonEncodings *encodings)
{
        encodings->priv = g_new0 (GaleonEncodingsPrivate, 1);
}


static void
free_encoding_info (GaleonEncodingInfo *info)
{
	g_free ((char*)info->title);
	g_free ((char*)info->encoding);
	g_free (info);
}

static void
galeon_encodings_finalize (GObject *object)
{
	GaleonEncodings *encodings = GALEON_ENCODINGS (object);

	g_hash_table_destroy (encodings->priv->encodings_hash);
	g_list_free (encodings->priv->encodings);

	g_slist_foreach (encodings->priv->recent, (GFunc) g_free, NULL);
	g_slist_free (encodings->priv->recent);

	g_list_foreach (encodings->priv->auto_created, (GFunc)free_encoding_info, NULL);
	g_list_free (encodings->priv->auto_created);

	g_free (encodings->priv);

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

static void
galeon_encodings_class_init (GaleonEncodingsClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	parent_class = g_type_class_peek_parent (klass);

	object_class->finalize = galeon_encodings_finalize;
}

GType
galeon_encodings_get_type (void)
{
	static GType galeon_encodings_type = 0;

	if (galeon_encodings_type == 0)
	{
		static const GTypeInfo our_info =
		{
			sizeof (GaleonEncodingsClass),
			NULL,
			NULL,
			(GClassInitFunc) galeon_encodings_class_init,
			NULL,
			NULL,
			sizeof (GaleonEncodings),
			0,
			(GInstanceInitFunc) galeon_encodings_init
		};

		galeon_encodings_type = g_type_register_static (G_TYPE_OBJECT,
							      "GaleonEncodings",
							       &our_info, 0);
	}

	return galeon_encodings_type;
}

void
galeon_encoding_page_info_free (GaleonEncodingPageInfo *info)
{
	if (info)
	{
		g_free (info->encoding);
		g_free (info);
	}
}
