/*
 *  Copyright (c) 2004-2013 Gert Wollny <gw.fossdev@gmail.com>
 *
 *  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 3 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, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 *  As an exception to this license, "NEC C&C Research Labs" may use
 *  this software under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation.
 *
 */
 
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <mialm/mialandmarklist.h>

/**
 * SECTION:mialandmarklist 
 * @Short_description: List of 3D landmarks and their manipulation 
 * @Title: Landmark list 
 * 
 * A #MiaLandmarklist comprises a series of anatomical landmarks stored as #MiaLandmark. 
 * Functions are provided to manupilate and iterate the landmark list. 
 * 
 */


/**
 * MiaLandmarklist:
 * 
 * A list of 3D landmarks. 
 */

struct _MiaLandmarklist {
	GObject parent;
	gboolean dispose_has_run; 

	GHashTable *list;
	GString *name; 
	GString *selected;
};


#ifndef NDEBUG
static int n_lml = 0; 
void print_lml_left(void)
{
	if (n_lml)
		g_warning("left %d landmark lists behind", n_lml);
}
#endif

static GObject *
__mia_landmarklist_constructor (GType                  type,
				guint                  n_construct_properties,
				GObjectConstructParam *construct_properties);


static void 
__mia_landmarklist_class_init (gpointer g_class,
                               gpointer g_class_data);

static void 
__mia_landmarklist_instance_init (GTypeInstance   *instance,
                                  gpointer         g_class);

GType mia_landmarklist_get_type(void)
{
	static GType type = 0;
        if (type == 0) {
                static const GTypeInfo info = {
                        sizeof (MiaLandmarklistClass),
                        NULL,   /* base_init */
                        NULL,   /* base_finalize */
                        __mia_landmarklist_class_init,   /* class_init */
                        NULL,   /* class_finalize */
                        NULL,   /* class_data */
                        sizeof (MiaLandmarklist),
                        0,      /* n_preallocs */
                        __mia_landmarklist_instance_init,    /* instance_init */
			NULL
                };
                type = g_type_register_static (G_TYPE_OBJECT,
                                               "MiaLandmarklistType",
                                               &info, 0);
        }
        return type;	
}

/**
 * mia_landmarklist_new: 
 * @name: landmark list name
 *
 * Create a new landmark list with the given name. 
 *
 */

MiaLandmarklist *mia_landmarklist_new(const gchar *name)
{
	MiaLandmarklist *obj = (MiaLandmarklist *)g_object_new(MIA_TYPE_LANDMARKLIST,NULL);
	/* add your code here */ 
	obj->name = g_string_new(name ? name : "(unnamed)");
	return obj; 
}


/**
 * mia_landmarklist_get_selected: 
 * @self: a landmark list 
 *
 * Get the currently selected landmark in #self. No information is given whether 
 * the name actually exists within the landmark list. 
 *
 * Returns: name of the currently selected landmark or an empty string. 
 *
 */

const gchar *mia_landmarklist_get_selected(MiaLandmarklist *self)
{
	MIA_IS_LANDMARKLIST(self);
	if (self->selected)
		return (self->selected->str);
	return ""; 
}

/**
 * mia_landmarklist_set_selected: 
 * @self: a landmark list 
 * @selected: a landmark name
 *
 * Set the currently selected landmark based on the name of the passed landmark
 * It is not tested whether the name actually exists. 
 *
 */
void mia_landmarklist_set_selected(MiaLandmarklist *self, const gchar *selected)
{
	if (self->selected)
		g_string_assign(self->selected, selected);
	else
		self->selected = g_string_new(selected);
}


enum {lmlp_name = 1};

static void
__mia_landmarklist_set_property (GObject      *object,
                     guint         property_id,
                     const GValue *value,
                     GParamSpec   *pspec)
{
        MiaLandmarklist *self = (MiaLandmarklist *) object;

        switch (property_id) {
	case lmlp_name: 
		mia_landmarklist_set_name(self, g_value_get_string(value));
		break; 
	default:
                /* We don't have any other property... */
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
        }
}

static void
__mia_landmarklist_get_property (GObject      *object,
                     guint         property_id,
                     GValue       *value,
                     GParamSpec   *pspec)
{
        MiaLandmarklist *self = (MiaLandmarklist *) object;

        switch (property_id) {
	case lmlp_name: 
		g_value_set_string(value, mia_landmarklist_get_name(self));
		break; 
        default:
                /* We don't have any other property... */
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
        }
}

static void
__mia_landmarklist_instance_init (GTypeInstance   *instance,
				  gpointer         UNUSED(g_class))
{
        MiaLandmarklist *self = (MiaLandmarklist *)instance;
	self->dispose_has_run = FALSE; 
	
	self->list = g_hash_table_new_full(g_str_hash, g_str_equal,
					   NULL, g_object_unref);
#ifndef NDEBUG
	n_lml++;
#endif	
}

static const  gchar name_property[] = "name"; 

static GObject *
__mia_landmarklist_constructor (GType                  type,
                    guint                  n_construct_properties,
                    GObjectConstructParam *construct_properties)
{
        GObject *obj;
        {
                /* Invoke parent constructor. */
                MiaLandmarklistClass *klass;
                GObjectClass *parent_class;  
                klass = MIA_LANDMARKLIST_CLASS (g_type_class_peek (MIA_TYPE_LANDMARKLIST));
                parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
                obj = parent_class->constructor (type,
                                                 n_construct_properties,
                                                 construct_properties);
        }
        
        /* add your code here */
	
        return obj;
}
static void
__mia_landmarklist_dispose (GObject *obj)
{
	MiaLandmarklistClass *klass;
        GObjectClass *parent_class;
	MiaLandmarklist *self = (MiaLandmarklist *)obj;
	
	if (self->dispose_has_run)
		return; 
	
	self->dispose_has_run = TRUE; 
	
	g_hash_table_destroy(self->list);
	self->list = NULL; 
	/* chain up to the parent class */
        klass = MIA_LANDMARKLIST_CLASS (g_type_class_peek
					    (MIA_TYPE_LANDMARKLIST));
	parent_class =	G_OBJECT_CLASS(g_type_class_peek_parent (klass));
	parent_class->finalize(obj); 
}


static void
__mia_landmarklist_finalize (GObject *obj)
{
        MiaLandmarklistClass *klass;
        GObjectClass *parent_class;
        MiaLandmarklist *self = (MiaLandmarklist *)obj;

	/* add your destruction code here */
	if (self->name)
		g_string_free(self->name,TRUE);
	if (self->selected)
		g_string_free(self->selected,TRUE);
	
#ifndef NDEBUG
	n_lml--;
#endif	
	
	/* chain up to the parent class */
        klass = MIA_LANDMARKLIST_CLASS (g_type_class_peek
					    (MIA_TYPE_LANDMARKLIST));
	parent_class =	G_OBJECT_CLASS(g_type_class_peek_parent (klass));
	parent_class->finalize(obj); 
}

static void
__mia_landmarklist_class_init (gpointer g_class,
			       gpointer UNUSED(g_class_data))
{
	GParamSpec *pspec;
        GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);

	gobject_class->set_property = __mia_landmarklist_set_property;
        gobject_class->get_property = __mia_landmarklist_get_property;
	gobject_class->dispose = __mia_landmarklist_dispose;
        gobject_class->finalize = __mia_landmarklist_finalize;
        gobject_class->constructor = __mia_landmarklist_constructor;

	/* add your code here (e.g. define properties) */
	pspec = g_param_spec_string (name_property, "name","landmark name", 
				     "unknown", G_PARAM_READWRITE);	
	g_object_class_install_property (gobject_class, lmlp_name, pspec);
}

/**
 * mia_landmarklist_insert: 
 * @self: a landmark list 
 * @lm: a landmark
 *
 * Insert the landmark lm into the landmark list if it doesn't already exists. 
 *
 * Returns: #TRUE if the landmark was successfully inserted, and #FALSE if a 
 * landmark with the given name already existed in the list. 
 *
 */

gboolean
mia_landmarklist_insert(MiaLandmarklist *self, MiaLandmark *lm)
{
	MiaLandmark *help; 
	g_assert(MIA_IS_LANDMARKLIST(self));
	g_assert(MIA_IS_LANDMARK(lm));
	
	help = MIA_LANDMARK(
		g_hash_table_lookup(self->list, (gpointer)mia_landmark_get_name(lm)));
	
	if (help)
		return FALSE; 
	
	g_hash_table_insert(self->list, 
			    (gpointer)mia_landmark_get_name(lm),
			    lm);
	return TRUE; 
}

/**
 * mia_landmarklist_get_landmark: 
 * @self: a landmark list 
 * @key: a landmark name
 *
 * Retrive a landmark based on the given landmark name @key
 *
 * Returns: the #MiaLandmark if it exists in the list, otherwise NULL
 *
 */
MiaLandmark *
mia_landmarklist_get_landmark(MiaLandmarklist *self, const gchar *key)
{
	g_assert(MIA_IS_LANDMARKLIST(self));	
	return MIA_LANDMARK(g_hash_table_lookup(self->list, key));
}

/**
 * mia_landmarklist_delete_landmark: 
 * @self: a landmark list 
 * @key: a landmark name
 *
 * Delete the landmark with name @key from the list if it exists. 
 *
 */
void 
mia_landmarklist_delete_landmark(MiaLandmarklist *self, const gchar *key)
{
	g_assert(MIA_IS_LANDMARKLIST(self));
	g_hash_table_remove(self->list, key);
}

/**
 * mia_landmarklist_set_name: 
 * @self: a landmark list 
 * @name: a name
 *
 * (Re-)set the name of the lansmark list to @name. 
 *
 */
void 
mia_landmarklist_set_name(MiaLandmarklist *self, const gchar *name)
{
	g_assert(MIA_IS_LANDMARKLIST(self));
	if (self->name) 
		g_string_assign(self->name, name);
	else
		self->name = g_string_new(name);
}

/**
 * mia_landmarklist_get_name: 
 * @self: a landmark list 
 *
 * Retrive the  name of the landmark list.
 *
 * Returns: The name of the landmark list. If not set, return an empty string. 
 */
const gchar *
mia_landmarklist_get_name(MiaLandmarklist *self)
{
	g_assert(MIA_IS_LANDMARKLIST(self));
	if (self->name) 
		return self->name->str; 
	else 
		return ""; 
}

/**
 * mia_landmarklist_foreach: 
 * @self: a landmark list 
 * @callback: a callback function #GHFunc
 * @user_data: (closure): User provided additional data
 *
 * Iterate over the landmark ist and execute @callback for each landmark. 
 *
 */
void 
mia_landmarklist_foreach(MiaLandmarklist *self, 
			 GHFunc callback, gpointer user_data)
{
	g_assert(MIA_IS_LANDMARKLIST(self));
	g_hash_table_foreach(self->list, callback, user_data);
}

void __clear_location(gpointer UNUSED(key), gpointer value, gpointer UNUSED(user_data))
{
	MiaLandmark *lm = MIA_LANDMARK(value);
	mia_landmark_set_location(lm, NULL);
}

/**
 * mia_landmarklist_clear_locations: 
 * @self: a landmark list 
 *
 * Clear the 3D location information for all landmarks. 
 *
 */
void 
mia_landmarklist_clear_locations(MiaLandmarklist *self)
{
	mia_landmarklist_foreach(self, __clear_location, NULL);
}


/**
 * mia_landmarklist_test: 
 *
 * Run some basic tests on the landmark list implementation. 
 * Returns: #TRUE if successful. 
 *
 */

gboolean mia_landmarklist_test(void)
{
	gboolean result = FALSE;
	MiaLandmarklist *test = mia_landmarklist_new("test");
	g_return_val_if_fail(test, FALSE);
	result = MIA_IS_LANDMARKLIST(test);
	
	/* add your tests here */
	
	
	g_object_unref(G_OBJECT(test));
	return result;
}

static const char landmark_ctag[] = "landmark";

static void
__landmark_start (ParserState * state, const xmlChar ** UNUSED(attrs))
{
	state->pss->data = g_object_new (MIA_TYPE_LANDMARK, NULL);
}

static void
__landmark_end (ParserState * state, const gchar *UNUSED(property))
{
	if (!mia_landmarklist_insert(MIA_LANDMARKLIST(state->data), 
			MIA_LANDMARK (state->pss->data)))
		g_warning("Ignoring second incarnation of landmark %s", 
			mia_landmark_get_name(MIA_LANDMARK (state->pss->data)));
}

const ParserTags landmarklist_parser_tags[]  = {
	{name_property, NULL, xmlio_get_string, xmlio_end_string, NULL},
	{landmark_ctag,  __landmark_start, NULL, __landmark_end, landmark_parser_tags},
	END_PARSER_TAGS
};


typedef struct _WlmcbData WlmcbData;
struct _WlmcbData {
	xmlNodePtr ptr;
	xmlNsPtr ns;
	gboolean retval;
};

static void
__write_landmarks (gpointer UNUSED(key), gpointer value, WlmcbData * user_data)
{

	xmlNodePtr root;
	xmlNodePtr lmptr; 
	MiaLandmark *lm;
	
	lm = MIA_LANDMARK (value);
	if (!lm) {
		user_data->retval = FALSE;
		return;
	}

	root = user_data->ptr;

	lmptr = xmlNewChild (root, user_data->ns, (const xmlChar *)landmark_ctag, NULL);
	
	mia_landmark_xmlio_write(lmptr, user_data->ns, lm);
	
}


/**
 * mia_landmarklist_xmlio_write: 
 * @parent: the XML parent node the landmark list is to be attached to
 * @ns: The XML namespace 
 * @list: The landmark list to write to the XML node
 * 
 * This function adds the landmark @list as child node to the given @parent node.  
 * 
 * Returns: #TRUE if the landmark list was successfully added to the parent node, #FALSE otherwise
  */
gboolean 
mia_landmarklist_xmlio_write(xmlNodePtr parent, xmlNsPtr UNUSED(ns), MiaLandmarklist *list)
{
	WlmcbData userdata = {
		parent, NULL, TRUE
	};

	gboolean result = xmlNewTextChild (parent, NULL, (const xmlChar *)name_property,
					   (const xmlChar *)mia_landmarklist_get_name(list)) != NULL;
	
	mia_landmarklist_foreach(list, (GHFunc) __write_landmarks, &userdata);
	return result && userdata.retval; 
}
