/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2004 Takuro Ashie
 *  Copyright (C) 2004 Hiroyuki Ikezoe
 *
 *  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.
 *
 *  $Id: kz-smart-bookmark.c 2213 2005-07-11 03:05:50Z ikezoe $
 */

#include "kz-smart-bookmark.h"

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

#include "gobject-utils.h"
#include "eggregex.h"
#include "utils.h"
#include "kz-bookmark-file.h"

typedef struct _KzSmartBookmarkPrivate	KzSmartBookmarkPrivate;
struct _KzSmartBookmarkPrivate
{
	KzBookmark *history;
};

#define KZ_SMART_BOOKMARK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KZ_TYPE_SMART_BOOKMARK, KzSmartBookmarkPrivate))

enum {
	PROP_0,
	PROP_SMART_LIST,
	PROP_SMART_HISTORY
};

static void     kz_smart_bookmark_class_init  (KzSmartBookmarkClass *klass);
static void     kz_smart_bookmark_init        (KzSmartBookmark *bookmark);
static void     kz_smart_bookmark_dispose     (GObject *object);
static void     kz_smart_bookmark_set_property (GObject         *object,
				                guint            prop_id,
				                const GValue    *value,
				                GParamSpec      *pspec);
static void     kz_smart_bookmark_get_property (GObject         *object,
				                guint            prop_id,
				                GValue          *value,
				                GParamSpec      *pspec);


static KzBookmarkClass *parent_class;


KZ_OBJECT_GET_TYPE(kz_smart_bookmark, "KzSmartBookmark", KzSmartBookmark,
		   kz_smart_bookmark_class_init, kz_smart_bookmark_init,
		   KZ_TYPE_BOOKMARK)

static GQuark smart_list_quark  = 0;

static void
kz_smart_bookmark_class_init (KzSmartBookmarkClass *klass)
{
	GObjectClass *object_class;

	parent_class = g_type_class_peek_parent(klass);
	object_class = (GObjectClass *) klass;

	object_class->dispose      = kz_smart_bookmark_dispose;
	object_class->set_property = kz_smart_bookmark_set_property;
	object_class->get_property = kz_smart_bookmark_get_property;
	
	g_object_class_install_property(
		object_class,
		 PROP_SMART_LIST,
		 g_param_spec_pointer(
			 "smart-list",
			 _("Smart List"),
			 _("The List for the Smart Bookmark"),
			 G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		 PROP_SMART_HISTORY,
		 g_param_spec_object(
			 "smart-history",
			 _("History bookmark for smart bookmark"),
			 _("The history bookmark for the Smart Bookmark"),
			 KZ_TYPE_BOOKMARK,
			 G_PARAM_READWRITE));

	smart_list_quark  = g_quark_from_string("KzBookmark::SmartBookmarkList");

	g_type_class_add_private (object_class, sizeof(KzSmartBookmarkPrivate));
}


static void
kz_smart_bookmark_init (KzSmartBookmark *smart)
{
	KzSmartBookmarkPrivate *priv = KZ_SMART_BOOKMARK_GET_PRIVATE (smart);

	priv->history = NULL;
}


static void
free_smart_list (GList *list)
{
	GList *node;

	if (!list) return;

	for (node = list; node; node = g_list_next(node))
	{
		KzSmartBookmarkProperty *prop = node->data;
		if (!prop) continue;
	
		if (prop->regex)
			g_free(prop->regex);
		if (prop->uri)
			g_free(prop->uri);
		if (prop->encode)
			g_free(prop->encode);
		g_free(prop);
	}
	g_list_free(list);
	list = NULL;
}

static void
kz_smart_bookmark_dispose (GObject *object)
{
	KzSmartBookmark *smart = KZ_SMART_BOOKMARK(object);
	GList *smart_list;
	KzSmartBookmarkPrivate *priv = KZ_SMART_BOOKMARK_GET_PRIVATE (smart);

	/* smart property */
	smart_list = g_object_get_qdata(object, smart_list_quark);
	free_smart_list(smart_list);
	g_object_set_qdata(object, smart_list_quark, NULL);

	if (priv->history)
		g_object_unref (priv->history);

	priv->history = NULL;

	if (G_OBJECT_CLASS (parent_class)->dispose)
		G_OBJECT_CLASS (parent_class)->dispose(object);
}


static void
kz_smart_bookmark_set_property (GObject *object,
			        guint prop_id,
			        const GValue *value,
			        GParamSpec *pspec)
{
	KzSmartBookmarkPrivate *priv = KZ_SMART_BOOKMARK_GET_PRIVATE (object);

	switch (prop_id)
       	{
	case PROP_SMART_LIST:
		g_object_set_qdata(object, smart_list_quark, 
				   g_value_get_pointer(value));
		break;
	case PROP_SMART_HISTORY:
		priv->history = g_object_ref(g_value_get_object(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


static void
kz_smart_bookmark_get_property (GObject *object,
		                guint prop_id,
		                GValue *value,
		                GParamSpec *pspec)
{
	KzSmartBookmarkPrivate *priv = KZ_SMART_BOOKMARK_GET_PRIVATE (object);

	switch (prop_id)
       	{
	case PROP_SMART_LIST:
		g_value_set_pointer(value, 
				    g_object_get_qdata(object, smart_list_quark));
		break;
	case PROP_SMART_HISTORY:
		g_value_set_object(value, priv->history);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


KzBookmark *
kz_smart_bookmark_new (void)
{
	KzSmartBookmark *smart;

	smart = g_object_new(KZ_TYPE_SMART_BOOKMARK, NULL);

	return KZ_BOOKMARK(smart);
}


void
kz_smart_bookmark_append_property (KzSmartBookmark *bookmark,
				         const gchar *regex,
				         const gchar *uri,
				         const gchar *encode,
				         gboolean urlencode)
{
	GList *smart_list;
	KzSmartBookmarkProperty *prop;

	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));

	prop = g_new0(KzSmartBookmarkProperty, 1);
	if (regex)
		prop->regex = g_strdup(regex);
	if (uri)
		prop->uri   = g_strdup(uri);
	if (encode)
		prop->encode = g_strdup(encode);
	prop->urlencode = urlencode;

	smart_list = g_object_get_qdata(G_OBJECT(bookmark),
					smart_list_quark);
	smart_list = g_list_append(smart_list, prop);

	g_object_set(G_OBJECT(bookmark), "smart-list", smart_list, NULL);
}

void
kz_smart_bookmark_remove_property (KzSmartBookmark *bookmark)
{
}

#define MAX_GROUPSTRING 9
gchar *
kz_smart_bookmark_get_smart_uri (KzSmartBookmark *bookmark, const gchar *text)
{
	gchar *smart_uri;
	GList *smart_list, *node;
	KzSmartBookmarkProperty *match = NULL;
	gint n_match = 0;
	EggRegex *egg_regex;
	GError *error = NULL;

	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);

	if (!text) 
		return g_strdup("");

	if (!KZ_IS_SMART_BOOKMARK(bookmark))
	{
		const gchar *link;
		link = kz_bookmark_get_link(KZ_BOOKMARK(bookmark));
		if (link)
			return g_strdup(link);
		else 
			return g_strdup(text);
	}

	smart_list = g_object_get_qdata(G_OBJECT(bookmark),
					smart_list_quark);

	for (node = smart_list; node; node = g_list_next(node))
	{
		gchar *regex;
		KzSmartBookmarkProperty *prop;
		
		prop = node->data;	
		regex = prop->regex;

		egg_regex = egg_regex_new(regex, 0, 0, &error);

		if (error)
		{
			g_warning("KzBookmark: Regular Expression Error");
			g_error_free(error);
			return g_strdup(text);
		}

		n_match = egg_regex_match(egg_regex, text, -1, 0);

		if (n_match > 0)		
		{
			match = prop;
			break;
		}
		egg_regex_free(egg_regex);
	}

	if(match)
	{
		gboolean urlencode = match->urlencode;
		gchar *uri = match->uri;
		gchar *encode = match->encode;
		gchar **strings = NULL;
		gchar *url_strings[MAX_GROUPSTRING];
		guint i, n = 0;
		GString *str;

		strings = egg_regex_fetch_all(egg_regex, text);
	
		if (strings)
		{
			while (strings[n] && n < MAX_GROUPSTRING)
			{
				url_strings[n] = ensure_encode_string(strings[n], 
						     encode,
						     urlencode);
				n++;
			}
		}
		g_strfreev(strings);	

		/* %s is placed whole of the text */
		str = g_string_new_len(uri, strlen(uri));
		if (strstr(uri, "%s"))
		{
			gchar *url;
			url = ensure_encode_string(text, encode,
						   urlencode);
			g_string_printf(str, uri, url);
			g_free(url);
		}

		/* \1, \2 ... is replaced matching sub pattern */
		for (i = 0; i < n; i++)
		{
			gchar *pos;
			gchar *index;
			index = g_strdup_printf("\\%d", i);
			pos = strstr(str->str, index); 
			while (pos)
			{
				gssize p = pos - str->str;
				str = g_string_erase(str, p, strlen(index));
				str = g_string_insert(str, p, url_strings[i]); 
				pos = strstr(str->str, index);
			}
			g_free(index);
		}

		smart_uri = g_strndup(str->str, str->len);	
		g_string_free(str, TRUE);

		egg_regex_free(egg_regex);
	}
	else
	{
                /* match nothing so nothing to do... */
		smart_uri = g_strdup(text);
	}

	return smart_uri;
}

void
kz_smart_bookmark_set_smart_list (KzSmartBookmark *bookmark, GList *list)
{
	GList *old_list;
	g_return_if_fail(KZ_IS_SMART_BOOKMARK(bookmark));
	
	old_list = g_object_get_qdata(G_OBJECT(bookmark),
				      smart_list_quark);
	free_smart_list(old_list);

	g_object_set(G_OBJECT(bookmark), "smart-list", list, NULL);
}


const GList *
kz_smart_bookmark_get_smart_list (KzSmartBookmark *bookmark)
{
	const GList *list;

	g_return_val_if_fail(KZ_IS_SMART_BOOKMARK(bookmark), NULL);
	
	list = g_object_get_qdata(G_OBJECT(bookmark),
				  smart_list_quark);

	return list;
}

KzBookmark *
kz_smart_bookmark_get_history (KzSmartBookmark *bookmark)
{
	KzSmartBookmarkPrivate *priv;

	g_return_val_if_fail(KZ_IS_SMART_BOOKMARK(bookmark), NULL);
	
	priv = KZ_SMART_BOOKMARK_GET_PRIVATE (bookmark);

	return priv->history;
}

void 
kz_smart_bookmark_set_history (KzSmartBookmark *bookmark, 
			       KzBookmark *history)
{
	g_return_if_fail(KZ_IS_SMART_BOOKMARK(bookmark));
	g_return_if_fail(KZ_IS_BOOKMARK(history));
	
	g_object_set(G_OBJECT(bookmark), "smart-history", history, NULL);
}

