/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 *  Copyright (C) 2000-2002 Ximian Inc.
 *
 *  Author: Not Zed <notzed@lostzed.mmc.com.au>
 *          Jeffrey Stedfast <fejj@ximian.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * 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 <gtk/gtk.h>
#include <glade/glade.h>
#include <libgnome/gnome-i18n.h>
#include <libgnomeui/gnome-dialog.h>
#include <libgnomeui/gnome-dialog-util.h>

#include "vfolder-context.h"
#include "vfolder-rule.h"
#include "shell/evolution-shell-client.h"

#define d(x) x

static int validate(FilterRule *);
static int vfolder_eq(FilterRule *fr, FilterRule *cm);
static xmlNodePtr xml_encode(FilterRule *);
static int xml_decode(FilterRule *, xmlNodePtr, RuleContext *f);
static void rule_copy (FilterRule *dest, FilterRule *src);
/*static void build_code(FilterRule *, GString *out);*/
static GtkWidget *get_widget(FilterRule *fr, RuleContext *f);

extern EvolutionShellClient *global_shell_client;

static void vfolder_rule_class_init (VfolderRuleClass *klass);
static void vfolder_rule_init (VfolderRule *vr);
static void vfolder_rule_finalise (GObject *obj);


static FilterRuleClass *parent_class = NULL;


GType
vfolder_rule_get_type (void)
{
	static GType type = 0;
	
	if (!type) {
		static const GTypeInfo info = {
			sizeof (VfolderRuleClass),
			NULL, /* base_class_init */
			NULL, /* base_class_finalize */
			(GClassInitFunc) vfolder_rule_class_init,
			NULL, /* class_finalize */
			NULL, /* class_data */
			sizeof (VfolderRule),
			0,    /* n_preallocs */
			(GInstanceInitFunc) vfolder_rule_init,
		};
		
		type = g_type_register_static (FILTER_TYPE_RULE, "VfolderRule", &info, 0);
	}
	
	return type;
}

static void
vfolder_rule_class_init (VfolderRuleClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	FilterRuleClass *fr_class = (FilterRuleClass *) klass;
	
	parent_class = g_type_class_ref (FILTER_TYPE_RULE);
	
	object_class->finalize = vfolder_rule_finalise;
	
	/* override methods */
	fr_class->validate   = validate;
	fr_class->eq = vfolder_eq;
	fr_class->xml_encode = xml_encode;
	fr_class->xml_decode = xml_decode;
	fr_class->copy = rule_copy;
	/*fr_class->build_code = build_code;*/
	fr_class->get_widget = get_widget;
}

static void
vfolder_rule_init (VfolderRule *vr)
{
	;
}

static void
vfolder_rule_finalise (GObject *obj)
{
	VfolderRule *vr = (VfolderRule *) obj;
	
	g_list_foreach (vr->sources, (GFunc) g_free, NULL);
	g_list_free (vr->sources);
	
        G_OBJECT_CLASS (parent_class)->finalize (obj);
}

/**
 * vfolder_rule_new:
 *
 * Create a new VfolderRule object.
 * 
 * Return value: A new #VfolderRule object.
 **/
VfolderRule *
vfolder_rule_new (void)
{
	return (VfolderRule *) g_object_new (VFOLDER_TYPE_RULE, NULL, NULL);
}

void
vfolder_rule_add_source (VfolderRule *vr, const char *uri)
{
	g_assert (IS_VFOLDER_RULE (vr));
	
	vr->sources = g_list_append (vr->sources, g_strdup (uri));
	
	filter_rule_emit_changed ((FilterRule *) vr);
}

const char *
vfolder_rule_find_source (VfolderRule *vr, const char *uri)
{
	GList *l;
	
	g_assert (IS_VFOLDER_RULE (vr));
	
	/* only does a simple string or address comparison, should
	   probably do a decoded url comparison */
	l = vr->sources;
	while (l) {
		if (l->data == uri || !strcmp (l->data, uri))
			return l->data;
		l = l->next;
	}
	
	return NULL;
}

void
vfolder_rule_remove_source (VfolderRule *vr, const char *uri)
{
	char *found;
	
	g_assert (IS_VFOLDER_RULE (vr));
	
	found = (char *) vfolder_rule_find_source (vr, uri);
	if (found) {
		vr->sources = g_list_remove (vr->sources, found);
		g_free (found);
		filter_rule_emit_changed ((FilterRule *) vr);
	}
}

const char *
vfolder_rule_next_source (VfolderRule *vr, const char *last)
{
	GList *node;
	
	if (last == NULL) {
		node = vr->sources;
	} else {
		node = g_list_find (vr->sources, (char *) last);
		if (node == NULL)
			node = vr->sources;
		else
			node = g_list_next (node);
	}
	
	if (node)
		return (const char *) node->data;
	
	return NULL;
}

static int
validate (FilterRule *fr)
{
	GtkWidget *dialog;
	
	g_return_val_if_fail (fr != NULL, FALSE);
	
	if (!fr->name || !*fr->name) {
		dialog = gnome_ok_dialog (_("You must name this vfolder."));
		gnome_dialog_run_and_close (GNOME_DIALOG (dialog));
		
		return 0;
	}
	
	/* We have to have at least one source set in the "specific" case.
	   Do not translate this string! */
	if (fr->source && !strcmp (fr->source, "specific") && VFOLDER_RULE (fr)->sources == NULL) {
		dialog = gnome_ok_dialog (_("You need to to specify at least one folder as a source."));
		gnome_dialog_run_and_close (GNOME_DIALOG (dialog));
		
		return 0;
	}
	
	return FILTER_RULE_CLASS (parent_class)->validate (fr);
}

static int
list_eq (GList *al, GList *bl)
{
	int truth = TRUE;
	
	while (truth && al && bl) {
		char *a = al->data, *b = bl->data;
		
		truth = strcmp (a, b) == 0;
		al = al->next;
		bl = bl->next;
	}
	
	return truth && al == NULL && bl == NULL;
}

static int
vfolder_eq (FilterRule *fr, FilterRule *cm)
{
        return FILTER_RULE_CLASS (parent_class)->eq (fr, cm)
		&& list_eq (((VfolderRule *) fr)->sources, ((VfolderRule *) cm)->sources);
}

static xmlNodePtr
xml_encode (FilterRule *fr)
{
	xmlNodePtr node, set, work;
	GList *l;
	VfolderRule *vr = (VfolderRule *)fr;
	
        node = FILTER_RULE_CLASS (parent_class)->xml_encode (fr);
	g_assert(node != NULL);
	set = xmlNewNode (NULL, "sources");
	xmlAddChild (node, set);
	l = vr->sources;
	while (l) {
		work = xmlNewNode (NULL, "folder");
		xmlSetProp (work, "uri", l->data);
		xmlAddChild (set, work);
		l = l->next;
	}
	
	return node;
}

static int
xml_decode (FilterRule *fr, xmlNodePtr node, struct _RuleContext *f)
{
	xmlNodePtr set, work;
	int result;
	VfolderRule *vr = (VfolderRule *)fr;
	char *uri;
	
        result = FILTER_RULE_CLASS (parent_class)->xml_decode (fr, node, f);
	if (result != 0)
		return result;
	
	set = node->childs;
	while (set) {
		if (!strcmp (set->name, "sources")) {
			work = set->childs;
			while (work) {
				if (!strcmp (work->name, "folder")) {
					uri = xmlGetProp (work, "uri");
					if (uri) {
						vr->sources = g_list_append (vr->sources, g_strdup (uri));
						xmlFree (uri);
					}
				}
				work = work->next;
			}
		}
		set = set->next;
	}
	return 0;
}

static void
rule_copy (FilterRule *dest, FilterRule *src)
{
	VfolderRule *vdest, *vsrc;
	GList *node;
	
	vdest = (VfolderRule *) dest;
	vsrc = (VfolderRule *) src;
	
	if (vdest->sources) {
		g_list_foreach (vdest->sources, (GFunc) g_free, NULL);
		g_list_free (vdest->sources);
		vdest->sources = NULL;
	}
	
	node = vsrc->sources;
	while (node) {
		char *uri = node->data;
		
		vdest->sources = g_list_append (vdest->sources, g_strdup (uri));
		node = node->next;
	}
	
	FILTER_RULE_CLASS (parent_class)->copy (dest, src);
}


enum {
	BUTTON_ADD,
	BUTTON_REMOVE,
	BUTTON_LAST,
};

struct _source_data {
	RuleContext *rc;
	VfolderRule *vr;
	const char *current;
	GtkList *list;
	GtkButton *buttons[BUTTON_LAST];
};

static void source_add(GtkWidget *widget, struct _source_data *data);
static void source_remove(GtkWidget *widget, struct _source_data *data);

static struct {
	char *name;
	GtkSignalFunc func;
} edit_buttons[] = {
	{ "source_add",    GTK_SIGNAL_FUNC (source_add)    },
	{ "source_remove", GTK_SIGNAL_FUNC (source_remove) },
};

static void
set_sensitive (struct _source_data *data)
{
	gtk_widget_set_sensitive ((GtkWidget *) data->buttons[BUTTON_ADD], TRUE);
	gtk_widget_set_sensitive ((GtkWidget *) data->buttons[BUTTON_REMOVE], data->current != NULL);
}

static void
select_source (GtkWidget *widget, GtkWidget *child, struct _source_data *data)
{
	data->current = g_object_get_data ((GObject *) child, "source");
	set_sensitive (data);
}

static void
select_source_with (GtkWidget *widget, struct _source_data *data)
{
	char *source = g_object_get_data ((GObject *) widget, "source");
	
	filter_rule_set_source ((FilterRule *) data->vr, source);
}

static void
source_add(GtkWidget *widget, struct _source_data *data)
{
	static const char *allowed_types[] = { "mail/*", NULL };
	GNOME_Evolution_Folder *folder;
	GtkListItem *item;
	char *def, *uri;
	GList *l;
	
	gtk_widget_set_sensitive (widget, FALSE);
	def = "";
	evolution_shell_client_user_select_folder (global_shell_client,
						   GTK_WINDOW (gtk_widget_get_toplevel (widget)),
						   _("Select Folder"),
						   def, allowed_types, &folder);
	
	if (GTK_OBJECT_DESTROYED (widget)) {
		if (folder)
			CORBA_free (folder);
		return;
	}
	
	gtk_widget_set_sensitive (widget, TRUE);
	
	if (folder) {
		uri = g_strdup (folder->physicalUri);
		data->vr->sources = g_list_append (data->vr->sources, uri);
		
		l = NULL;
		item = (GtkListItem *)gtk_list_item_new_with_label (uri);
		g_object_set_data ((GObject *) item, "source", uri);
		gtk_widget_show ((GtkWidget *) item);
		l = g_list_append (NULL, item);
		gtk_list_append_items (data->list, l);
		gtk_list_select_child (data->list, (GtkWidget *) item);
		data->current = uri;
	}
	
	CORBA_free (folder);
	set_sensitive (data);
}

static void
source_remove (GtkWidget *widget, struct _source_data *data)
{
	const char *source;
	int index = 0;
	GList *l;
	GtkListItem *item;
	
	source = NULL;
	while ((source = vfolder_rule_next_source (data->vr, source))) {
		if (data->current == source) {
			vfolder_rule_remove_source (data->vr, source);
			item = g_list_nth_data (data->list->children, index);
			l = g_list_append (NULL, item);
			gtk_list_remove_items (data->list, l);
			g_list_free (l);
			data->current = NULL;
			break;
		}
		index++;
	}
	set_sensitive (data);
}

/* DO NOT internationalise these strings */
const char *source_names[] = {
	"specific",
	"local",
	"remote_active",
	"local_remote_active"
};

static GtkWidget *
get_widget (FilterRule *fr, RuleContext *rc)
{
	VfolderRule *vr = (VfolderRule *) fr;
	struct _source_data *data;
	GtkWidget *widget, *frame;
	GtkOptionMenu *omenu;
	const char *source;
	GladeXML *gui;
	int i, row;
	GList *l;
	
        widget = FILTER_RULE_CLASS (parent_class)->get_widget (fr, rc);
	
	data = g_malloc0 (sizeof (*data));
	data->rc = rc;
	data->vr = vr;
	
	gui = glade_xml_new (FILTER_GLADEDIR "/filter.glade", "vfolder_source_frame", NULL);
        frame = glade_xml_get_widget (gui, "vfolder_source_frame");
	
	g_object_set_data_full ((GObject *) frame, "data", data, g_free);
	
	for (i = 0; i < BUTTON_LAST; i++) {
		/* FIXME: I think these need to be unref'd */
		data->buttons[i] = (GtkButton *) glade_xml_get_widget (gui, edit_buttons[i].name);
		g_signal_connect (data->buttons[i], "clicked", edit_buttons[i].func, data);
	}
	
        data->list = (GtkList *) glade_xml_get_widget (gui, "source_list");
	
	l = NULL;
	source = NULL;
	while ((source = vfolder_rule_next_source (vr, source))) {
		GtkListItem *item;
		
		item = (GtkListItem *) gtk_list_item_new_with_label (source);
		g_object_set_data ((GObject *) item, "source", (void *) source);
		gtk_widget_show (GTK_WIDGET (item));
		l = g_list_append (l, item);
	}
	
	gtk_list_append_items (data->list, l);
	
	g_signal_connect (data->list, "select_child", GTK_SIGNAL_FUNC (select_source), data);
	
	omenu = (GtkOptionMenu *) glade_xml_get_widget (gui, "source_option");
	l = GTK_MENU_SHELL (omenu->menu)->children;
	i = 0;
	row = 0;
	while (l) {
		GtkWidget *item = GTK_WIDGET (l->data);
		
		/* make sure that the glade is in sync with the source list! */
		if (i < sizeof (source_names) / sizeof (source_names[0])) {
			g_object_set_data ((GObject *) item, "source", (char *) source_names[i]);
			if (fr->source && strcmp (source_names[i], fr->source) == 0) {
				row = i;
			}
		} else {
			g_warning ("Glade file " FILTER_GLADEDIR "/filter.glade out of sync with editor code");
		}
		
		g_signal_connect (item, "activate", GTK_SIGNAL_FUNC (select_source_with), data);
		
		i++;
		l = l->next;
	}
	
	gtk_option_menu_set_history (omenu, row);
	if (fr->source == NULL)
		filter_rule_set_source (fr, (char *) source_names[row]);
	
	set_sensitive (data);
	
	gtk_box_pack_start (GTK_BOX (widget), frame, TRUE, TRUE, 3);
	
	g_object_unref (gui);
	
	return widget;
}
