/********************************************************************\
 * kvp_util.c -- misc odd-job kvp utils                             *
 * Copyright (C) 2001 Linas Vepstas <linas@linas.org>               *
 * Copyright (C) 2006 Neil Williams <linux@codehelp.co.uk>          *
 *                                                                  *
 * 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, contact:                        *
 *                                                                  *
 * Free Software Foundation           Voice:  +1-617-542-5942       *
 * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
 * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
 *                                                                  *
\********************************************************************/

#include "config.h"
#include <glib.h>
#include <stdio.h>
#include "qof.h"
#include "kvputil-p.h"

static KvpFrame *
qof_kvp_array_va (KvpFrame * kvp_root, const gchar *path,
	QofTime *qt, const gchar *first_name, va_list ap)
{
	KvpFrame *cwd;
	const gchar *name;

	if (!kvp_root)
		return NULL;
	if (!first_name)
		return NULL;

	/* Create subdirectory and put the actual data */
	cwd = kvp_frame_new ();

	/* Record the time */
	kvp_frame_set_time (cwd, "time", qt);

	/* Loop over the args */
	name = first_name;
	while (name)
	{
		const GUID *guid;
		guid = va_arg (ap, const GUID *);

		kvp_frame_set_guid (cwd, name, guid);

		name = va_arg (ap, const char *);
	}

	/* Attach cwd into the array */
	kvp_frame_add_frame_nc (kvp_root, path, cwd);
	return cwd;
}

KvpFrame *
qof_kvp_bag_add (KvpFrame * pwd, const gchar *path,
	QofTime *qt, const gchar *first_name, ...)
{
	KvpFrame *cwd;
	va_list ap;
	va_start (ap, first_name);
	cwd = qof_kvp_array_va (pwd, path, qt, first_name, ap);
	va_end (ap);
	return cwd;
}

/* ================================================================ */

#define MATCH_GUID(elt) {                                       \
  KvpFrame *fr = kvp_value_get_frame (elt);                     \
  if (fr) {                                                     \
     GUID *guid = kvp_frame_get_guid (fr, guid_name);           \
     if (guid && guid_equal (desired_guid, guid)) return fr;    \
  }                                                             \
}

KvpFrame *
qof_kvp_bag_find_by_guid (KvpFrame * root, const gchar *path,
	const gchar *guid_name, GUID * desired_guid)
{
	KvpValue *arr;
	KvpValueType valtype;
	GList *node;

	arr = kvp_frame_get_value (root, path);
	valtype = kvp_value_get_type (arr);
	if (KVP_TYPE_FRAME == valtype)
	{
		MATCH_GUID (arr);
		return NULL;
	}

	/* Its got to be a single isolated frame, or a list of them. */
	if (KVP_TYPE_GLIST != valtype)
		return NULL;

	for (node = kvp_value_get_glist (arr); node; node = node->next)
	{
		KvpValue *va = node->data;
		MATCH_GUID (va);
	}
	return NULL;
}

/* ================================================================ */

void
qof_kvp_bag_remove_frame (KvpFrame * root, const gchar *path, KvpFrame * fr)
{
	KvpValue *arr;
	KvpValueType valtype;
	GList *node, *listhead;

	arr = kvp_frame_get_value (root, path);
	valtype = kvp_value_get_type (arr);
	if (KVP_TYPE_FRAME == valtype)
	{
		if (fr == kvp_value_get_frame (arr))
		{
			KvpValue *old_val =
				kvp_frame_replace_value_nc (root, path, NULL);
			kvp_value_replace_frame_nc (old_val, NULL);
			kvp_value_delete (old_val);
		}
		return;
	}

	/* Its got to be a single isolated frame, or a list of them. */
	if (KVP_TYPE_GLIST != valtype)
		return;

	listhead = kvp_value_get_glist (arr);
	for (node = listhead; node; node = node->next)
	{
		KvpValue *va = node->data;
		if (fr == kvp_value_get_frame (va))
		{
			listhead = g_list_remove_link (listhead, node);
			g_list_free_1 (node);
			kvp_value_replace_glist_nc (arr, listhead);
			kvp_value_replace_frame_nc (va, NULL);
			kvp_value_delete (va);
			return;
		}
	}
}

/* ================================================================ */

static KvpFrame *
qof_kvp_bag_get_first (KvpFrame * root, const gchar *path)
{
	KvpValue *arr, *va;
	KvpValueType valtype;
	GList *node;

	arr = kvp_frame_get_value (root, path);
	valtype = kvp_value_get_type (arr);
	if (KVP_TYPE_FRAME == valtype)
		return kvp_value_get_frame (arr);

	/* Its gotta be a single isolated frame, or a list of them. */
	if (KVP_TYPE_GLIST != valtype)
		return NULL;

	node = kvp_value_get_glist (arr);
	if (NULL == node)
		return NULL;

	va = node->data;
	return kvp_value_get_frame (va);
}

void
qof_kvp_bag_merge (KvpFrame * kvp_into, const gchar *intopath,
	KvpFrame * kvp_from, const gchar *frompath)
{
	KvpFrame *fr;

	fr = qof_kvp_bag_get_first (kvp_from, frompath);
	while (fr)
	{
		qof_kvp_bag_remove_frame (kvp_from, frompath, fr);
		kvp_frame_add_frame_nc (kvp_into, intopath, fr);
		fr = qof_kvp_bag_get_first (kvp_from, frompath);
	}
}

static void
kv_pair_helper (gpointer key, gpointer val, gpointer user_data)
{
	GSList **result = (GSList **) user_data;
	GHashTableKVPair *kvp = g_new0 (GHashTableKVPair, 1);

	kvp->key = key;
	kvp->value = val;
	*result = g_slist_prepend (*result, kvp);
}

GSList *
g_hash_table_key_value_pairs (GHashTable * table)
{
	GSList *result_list = NULL;
	g_hash_table_foreach (table, kv_pair_helper, &result_list);
	return result_list;
}

void
g_hash_table_kv_pair_free_gfunc (gpointer data, gpointer user_data
	__attribute__ ((unused)))
{
	GHashTableKVPair *kvp = (GHashTableKVPair *) data;
	g_free (kvp);
}

/*======================== END OF FILE =============================*/
