/*
 *  Copyright (C) 2002  Ricardo Fernández Pascual
 *
 *  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 "bookmarks-util.h"
#include <string.h>
#include "gul-general.h"
#include "pixbuf-cache.h"
#include <gtk/gtkicontheme.h>
#include <gtk/gtkiconfactory.h>

/**
 * Returns a copy of the given list without the bookmarks that have
 * already an ascendant in the list.
 */
static gboolean
gb_util_remove_descendants_from_list_look_for_ancestor (GbBookmark *b, GSList *l)
{
	GSList *li;
	for (li = l; li; li = li->next)
	{
		if (GB_IS_FOLDER (li->data))
		{
			GbFolder *f = li->data;
			if (gb_folder_is_ancestor (f, b))
			{
				return TRUE;
			}
		}
	}
	return FALSE;
}

GSList *
gb_util_remove_descendants_from_list (GSList *l)
{
	GSList *ret = NULL;
	GSList *li;
	for (li = l; li; li = li->next)
	{
		GbBookmark *b = li->data;
		if (!gb_util_remove_descendants_from_list_look_for_ancestor (b, l))
		{
			ret = g_slist_prepend (ret, b);
		}
	}
	return g_slist_reverse (ret);
}

static GbFolder *
gb_util_merge_trees_match_folder (GbFolder *f, GbFolder *b)
{
	GbBookmark *bi;
	for (bi = f->child; bi; bi = bi->next)
	{
		if (GB_IS_FOLDER (bi))
		{
			if (!strcmp (bi->name, GB_BOOKMARK (b)->name))
			{
				return GB_FOLDER (bi);
			}
		}
	}
	return NULL;
}

static GbSite *
gb_util_merge_trees_match_site (GbFolder *f, GbSite *b)
{
	GbBookmark *bi;
	for (bi = f->child; bi; bi = bi->next)
	{
		if (GB_IS_SITE (bi))
		{
			if (!strcmp (GB_SITE (bi)->url, b->url))
			{
				return GB_SITE (bi);
			}
		}
	}
	return NULL;
}

static gboolean
gb_util_is_separator_at_index (GbFolder *f, int index)
{
	GbBookmark *bi;
	for (bi = f->child; bi ; bi = bi->next)
	{
		if (index-- == 0) break;
	}

	return (bi && GB_IS_SEPARATOR (bi)) ? TRUE : FALSE;
}


/**
 * Merges two trees of bookmarks. Modifies dest and does not touch orig.
 */
void
gb_util_merge_trees (GbFolder *dest, GbFolder *orig)
{
	gint current_index = 0;
	GbBookmark *boi;

	for (boi = orig->child; boi; boi = boi->next)
	{
		if (GB_IS_FOLDER (boi))
		{
			GbFolder *doi = gb_util_merge_trees_match_folder (dest, GB_FOLDER (boi));
			if (doi && !gb_bookmark_is_alias (doi))
			{
				gb_util_merge_trees (doi, GB_FOLDER (boi));
				current_index = gb_folder_get_child_index (GB_BOOKMARK (doi)->parent, 
									   GB_BOOKMARK (doi)) + 1;
			}
			else
			{
				GbBookmark *niu = gb_bookmark_copy (boi);
				gb_folder_add_child (dest, niu, current_index++);
				g_object_unref (niu);
			}
		}
		else if (GB_IS_SITE (boi))
		{
			GbSite *doi = gb_util_merge_trees_match_site (dest, GB_SITE (boi));
			if (doi)
			{
				/* nothing to be done in order to merge two matching sites
				   maybe add something to the notes field... check for smart sites... */
				current_index = gb_folder_get_child_index (GB_BOOKMARK (doi)->parent, 
									   GB_BOOKMARK (doi)) + 1;
			}
			else
			{
				GbBookmark *niu = gb_bookmark_copy (boi);
				gb_folder_add_child (dest, niu, current_index++);
				g_object_unref (niu);
			}
		}
		else if (GB_IS_SEPARATOR (boi))
		{
			if (gb_util_is_separator_at_index (dest, current_index))
			{
				/* Just increment the current index, there is already a
				 * separator in the destination tree */
				current_index++;
			}
			else
			{
				GbBookmark *niu = gb_bookmark_copy (boi);
				gb_folder_add_child (dest, niu, current_index++);
				g_object_unref (niu);
			}
		}
		else
		{
			GbBookmark *niu = gb_bookmark_copy (boi);
			gb_folder_add_child (dest, niu, current_index++);
			g_object_unref (niu);
		}
	}
}

static gchar *
gb_util_options_skip_spaces (const gchar *str)
{
	const gchar *ret = str;
	while (*ret && g_ascii_isspace (*ret))
	{
		++ret;
	}
	return (gchar *) ret;
}

static gchar *
gb_util_options_find_value_end (const gchar *value_start)
{
	const gchar *value_end;
	if (*value_start == '"')
	{
		for (value_end = value_start + 1; 
		     *value_end && (*value_end != '"' || *(value_end - 1) == '\\');
		     ++value_end) ;
		
	}
	else
	{
		for (value_end = value_start; 
		     *value_end && ! (g_ascii_isspace (*value_end)
				      || *value_end == ','
				      || *value_end == ';');
		     ++value_end) ;
	}
	return (gchar *) value_end;
}

static gchar *
gb_util_options_find_next_option (const gchar *current)
{
	const gchar *value_start;
	const gchar *value_end;
	const gchar *ret;
	value_start = strchr (current, '=');
	if (!value_start) return NULL;
	value_start = gb_util_options_skip_spaces (value_start + 1);
	value_end = gb_util_options_find_value_end (value_start);
	if (! (*value_end)) return NULL;
	for (ret = value_end + 1;
	     *ret && (g_ascii_isspace (*ret)
		      || *ret == ','
		      || *ret == ';');
	     ++ret);
	return (char *) ret;
}

/**
 * Very simple parser for option strings in the 
 * form a=b,c=d,e="f g",...
 */
gchar *
gb_util_options_get (const gchar *options, const gchar *option)
{
	gchar *ret = NULL;
	gsize optionlen = strlen (option);
	const gchar *current = gb_util_options_skip_spaces (options);
	
	while (current)
	{
		if (!strncmp (option, current, optionlen))
		{
			if (g_ascii_isspace (*(current + optionlen)) || *(current + optionlen) == '=')
			{
				const gchar *value_start;
				const gchar *value_end;
				value_start = strchr (current + optionlen, '=');
				if (!value_start) continue;
				value_start = gb_util_options_skip_spaces (value_start + 1);
				value_end = gb_util_options_find_value_end (value_start);
				if (*value_start == '"') value_start++;
				if (value_end >= value_start)
				{
					ret = g_strndup (value_start, value_end - value_start);
					break;
				}
			}
		}
		current = gb_util_options_find_next_option (current);
	}

	return ret;
}

gchar *
gb_util_options_set (const gchar *options, 
		     const gchar *option, const gchar *value)
{
	GString *str = g_string_sized_new (16);
	gchar *ret;
	gsize optionlen = strlen (option);
	const gchar *current = gb_util_options_skip_spaces (options);
	const gchar *next;
	gboolean done = FALSE;

	while (current)
	{
		next = gb_util_options_find_next_option (current);
		if (!strncmp (option, current, optionlen)
		    && (g_ascii_isspace (*(current + optionlen)) || *(current + optionlen) == '='))
		{
			g_string_append (str, option);
			g_string_append (str, "=\"");
			g_string_append (str, value);
			g_string_append (str, "\"");
			if (next && *next)
			{
				g_string_append (str, ",");
			}
			done = TRUE;
		}
		else
		{
			if (next)
			{
				g_string_append_len (str, current, next - current);
			}
			else
			{
				g_string_append (str, current);
			}
		}
		
		current = next;
	}

	if (!done)
	{
		if (str->len > 0) 
		{
			g_string_append (str, ",");
		}
		g_string_append (str, option);
		g_string_append (str, "=\"");
		g_string_append (str, value);
		g_string_append (str, "\"");
	}

	ret = str->str;
	g_string_free (str, FALSE);
	return ret;
}

