/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * gsf-doc-meta-data.c:
 *
 * Copyright (C) 2002-2006 Dom Lachowicz (cinamod@hotmail.com)
 * 			   Jody Goldberg (jody@gnome.org)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2.1 of the GNU Lesser 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 Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 */

#include <gsf-config.h>
#include <gsf/gsf-doc-meta-data.h>
#include <gsf/gsf-docprop-vector.h>
#include <gsf/gsf-impl-utils.h>

struct _GsfDocMetaData {
	GObject	base;

	GHashTable *table;
};
typedef GObjectClass GsfDocMetaDataClass;

struct _GsfDocProp {
	char   *name;
	GValue *val;
	char   *linked_to; /* optionally NULL */
};

static GObjectClass *parent_class;

static void
gsf_doc_meta_data_finalize (GObject *obj)
{
	g_hash_table_destroy (GSF_DOC_META_DATA (obj)->table);
	parent_class->finalize (obj);
}

static void
gsf_doc_meta_data_init (GObject *obj)
{
	GsfDocMetaData *meta = GSF_DOC_META_DATA (obj);
	meta->table = g_hash_table_new_full (g_str_hash, g_str_equal,
		NULL, (GDestroyNotify) gsf_doc_prop_free);
}

static void
gsf_doc_meta_data_class_init (GObjectClass *gobject_class)
{
	gobject_class->finalize = gsf_doc_meta_data_finalize;
	parent_class = g_type_class_peek_parent (gobject_class);
}

GSF_CLASS(GsfDocMetaData, gsf_doc_meta_data,
	  gsf_doc_meta_data_class_init, gsf_doc_meta_data_init,
	  G_TYPE_OBJECT)

/**********************************************************************/

/**
 * gsf_doc_meta_data_new :
 *
 * Returns: a new metadata property collection
 **/
GsfDocMetaData *
gsf_doc_meta_data_new (void)
{
	return g_object_new (GSF_DOC_META_DATA_TYPE, NULL);
}

/**
 * gsf_doc_meta_data_lookup :
 * @meta : #GsfDocMetaData
 * @name :
 *
 * Returns: the property with name @id in @meta.  The caller can modify the
 * property value and link but not the name.
 **/
GsfDocProp *
gsf_doc_meta_data_lookup (GsfDocMetaData const *meta, char const *name)
{
	g_return_val_if_fail (IS_GSF_DOC_META_DATA (meta), NULL);
	g_return_val_if_fail (name != NULL, NULL);
	return g_hash_table_lookup (meta->table, name);
}

/**
 * gsf_doc_meta_data_insert :
 * @meta : #GsfDocMetaData
 * @name :
 * @value : #GValue
 *
 * Take ownership of @name and @value and insert a property into @meta.
 * If a property exists with @name, it is replaced (The link is lost)
 **/
void
gsf_doc_meta_data_insert (GsfDocMetaData *meta, char *name, GValue *value)
{
	GsfDocProp *prop;
	
	g_return_if_fail (IS_GSF_DOC_META_DATA (meta));
	g_return_if_fail (name != NULL);
	prop = g_new (GsfDocProp, 1);
	prop->name = name;
	prop->val  = value;
	prop->linked_to = NULL;
	g_hash_table_replace (meta->table, prop->name, prop);
}

/**
 * gsf_doc_meta_data_remove :
 * @meta : the collection
 * @name : the non-null string name of the property
 *
 * If @name does not exist in the collection, do nothing. If @name does exist,
 * remove it and its value from the collection
 **/
void
gsf_doc_meta_data_remove (GsfDocMetaData *meta, char const *name)
{
	g_return_if_fail (IS_GSF_DOC_META_DATA (meta));
	g_return_if_fail (name != NULL);
	g_hash_table_remove (meta->table, name);
}

/**
 * gsf_doc_meta_data_store :
 * @meta : #GsfDocMetaData
 * @name : 
 *
 **/
GsfDocProp *
gsf_doc_meta_data_steal (GsfDocMetaData *meta, char const *name)
{
	GsfDocProp *prop;
	g_return_val_if_fail (IS_GSF_DOC_META_DATA (meta), NULL);
	g_return_val_if_fail (name != NULL, NULL);
	prop = g_hash_table_lookup (meta->table, name);
	if (NULL != prop)
		g_hash_table_steal (meta->table, name);
	return prop;
}

/**
 * gsf_doc_meta_data_store :
 * @meta : #GsfDocMetaData
 * @prop : #GsfDocProp
 *
 **/
void
gsf_doc_meta_data_store (GsfDocMetaData *meta, GsfDocProp *prop)
{
	g_return_if_fail (IS_GSF_DOC_META_DATA (meta));
	g_return_if_fail (prop != NULL);
	g_return_if_fail (prop != g_hash_table_lookup (meta->table, prop->name));
	g_hash_table_replace (meta->table, prop->name, prop);
}

/**
 * gsf_doc_meta_data_foreach :
 * @meta : the collection
 * @func : the function called once for each element in the collection
 * @user_data : any supplied user data or %NULL
 *
 * Iterate through each (key, value) pair in this collection
 **/
void
gsf_doc_meta_data_foreach (GsfDocMetaData const *meta, GHFunc func, gpointer user_data)
{
	g_return_if_fail (IS_GSF_DOC_META_DATA (meta));
	g_hash_table_foreach (meta->table, func, user_data);
}

/**
 * gsf_doc_meta_data_size :
 * @meta : the collection
 *
 * Returns: the number of items in this collection
 **/
gsize
gsf_doc_meta_data_size (GsfDocMetaData const *meta)
{
	g_return_val_if_fail (meta != NULL, 0);
	return (gsize) g_hash_table_size (meta->table);
}

static void
cb_print_property (G_GNUC_UNUSED char const *name,
		   GsfDocProp const *prop)
{
	if (gsf_doc_prop_get_link (prop) != NULL)
		g_print ("prop '%s' LINKED TO  -> '%s'\n",
			 name, gsf_doc_prop_get_link (prop));
	else
		g_print ("prop '%s'\n", name);

	gsf_doc_prop_dump (prop);
}

/**
 * gsf_doc_meta_dump :
 * @meta : #GsfDocMetaData
 *
 * A debugging utility to dump the content of @meta via g_print
 **/
void
gsf_doc_meta_dump (GsfDocMetaData const *meta)
{
	gsf_doc_meta_data_foreach (meta,
		(GHFunc) cb_print_property, NULL);
}

/**********************************************************************/

/**
 * gsf_doc_prop_new :
 * @name :
 *
 * Returns: a new #GsfDocProp which the caller is responsible for freeing.
 * Takes ownership of @name.
 **/
GsfDocProp *
gsf_doc_prop_new  (char *name)
{
	GsfDocProp *prop;

	g_return_val_if_fail (name != NULL, NULL);

	prop = g_new (GsfDocProp, 1);
	prop->name = name;
	prop->val  = NULL;
	prop->linked_to = NULL;

	return prop;
}

/**
 * gsf_doc_prop_free :
 * @prop : #GsfDocProp
 *
 * If @prop is non %NULL free the memory associated with it
 **/
void
gsf_doc_prop_free (GsfDocProp *prop)
{
	if (NULL != prop) {
		g_free (prop->linked_to);

		if (prop->val) {
			g_value_unset (prop->val);
			g_free (prop->val);
		}
		g_free (prop->name);
		g_free (prop);
	}
}

/**
 * gsf_doc_prop_get_name :
 * @prop : #GsfDocProp
 *
 * Returns: the name of the property, the caller should not modify the result.
 **/
char const *
gsf_doc_prop_get_name (GsfDocProp const *prop)
{
	g_return_val_if_fail (prop != NULL, NULL);
	return prop->name;
}

/**
 * gsf_doc_prop_get_val :
 * @prop : the property
 *
 * Returns: the value of the property, the caller should not modify the result.
 **/
GValue const *
gsf_doc_prop_get_val (GsfDocProp const *prop)
{
	g_return_val_if_fail (prop != NULL, NULL);
	return prop->val;
}

/**
 * gsf_doc_prop_set_val :
 * @prop : #GsfDocProp
 * @val  : #GValue
 *
 * Assigns @val to @prop, and unsets and frees the current value.
 **/
void
gsf_doc_prop_set_val (GsfDocProp *prop, GValue *val)
{
	g_return_if_fail (prop != NULL);

	if (val != prop->val) {
		if (prop->val != NULL) {
			g_value_unset (prop->val);
			g_free (prop->val);
		}
		prop->val = val;
	}
}

/**
 * gsf_doc_prop_swap_val :
 * @prop : #GsfDocProp
 * @val  : #GValue
 *
 * Returns: the current value of @prop, and replaces it with @val
 * 	Caller is responsible for unsetting and freeing the result.
 **/
GValue *
gsf_doc_prop_swap_val (GsfDocProp *prop, GValue *val)
{
	GValue *old_val;
	g_return_val_if_fail (prop != NULL, NULL);

	old_val = prop->val;
	prop->val = val;
	return old_val;
}

/**
 * gsf_doc_prop_get_link :
 * @prop : #GsfDocProp
 *
 * Returns: the current link descriptor of @prop.  The result should not be
 * 	freed or modified.
 **/
char const *
gsf_doc_prop_get_link (GsfDocProp const *prop)
{
	g_return_val_if_fail (prop != NULL, NULL);
	return prop->linked_to;
}

/**
 * gsf_doc_prop_set_link :
 * @prop : #GsfDocProp
 * @link : optionally %NULL
 *
 * Sets @prop's link to @link
 **/
void
gsf_doc_prop_set_link (GsfDocProp *prop, char *link)
{
	g_return_if_fail (prop != NULL);

	if (link != prop->linked_to) {
		g_free (prop->linked_to);
		prop->linked_to = link;
	}
}

/**
 * gsf_doc_prop_dump :
 * @prop : #GsfDocProp
 *
 * A debugging utility to dump @prop as text via g_print
 * New in 1.14.2
 **/
void
gsf_doc_prop_dump (GsfDocProp const *prop)
{
	GValue const *val = gsf_doc_prop_get_val  (prop);
	char *tmp;
	if (VAL_IS_GSF_DOCPROP_VECTOR ((GValue *)val)) {
		GValueArray *va = gsf_value_get_docprop_varray (val);
		unsigned i;

		for (i = 0 ; i < va->n_values; i++) {
			tmp = g_strdup_value_contents (
				g_value_array_get_nth (va, i));
			g_print ("\t[%u] = %s\n", i, tmp);
			g_free (tmp);
		}
	} else {
		tmp = g_strdup_value_contents (val);
		g_print ("\t= %s\n", tmp);
		g_free (tmp);
	}
}


syntax highlighted by Code2HTML, v. 0.9.1