/*
 *  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.
 *
 */

#include <glib.h>
#include <string.h>
 
#include <mialm/mialandmark.h>


/**
 * SECTION:mialandmark
 * @Short_description: Landmark description for gray scale volume data
 * @Title: Landmarks in 3D space
 * 
 * #MiaLandmarks olds the location of a landmark in 3D space. In addition it comprises 
 * #MiaCamera that describes the best view for visualizing this landmark, and the intensity 
 * value in the grayscale data that corresponds to the landmark location and can, hence, be used to 
 * properly visualize the iso-surface is landmark can be found on. 
 *
 * In addition, an image file name can be provided that shows the landmark in its anatomical context 
 * in an example data set. 
 */

/**
   MiaLandmark:

   This class defines a landmark in 3D space with a camera, the corrosponding view 
   definition, the intensity value corresponding to its location in a corresponding 
   volume data, and a picture illustrating its location in the anatomical context.  
*/

struct _MiaLandmark {
	/*< private >*/
	GObject parent;
	gboolean dispose_has_run;
	
	/*< public >*/
	GString *name;
	GString *picfile;
	MiaVector3d *location; 
	MiaCamera *camera; 
	gfloat iso;
	
	
};


const char mia_landmark_location_property[] = "location";
const char mia_landmark_isovalue_property[] = "isovalue";
const char mia_landmark_camera_property[] = "camera";
const char mia_landmark_name_property[] = "name";
const char mia_landmark_picfile_property[] = "picfile";
	
static GObject *__mia_landmark_constructor (GType type,
                            guint  n_construct_properties,
                            GObjectConstructParam *construct_properties);


static void __mia_landmark_class_init (gpointer g_class,
                                       gpointer g_class_data);

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



GType mia_landmark_get_type()
{
	static GType type = 0;
        if (type == 0) {
                static const GTypeInfo info = {
                        sizeof (MiaLandmarkClass),
                        NULL,   /* base_init */
                        NULL,   /* base_finalize */
                        __mia_landmark_class_init,   /* class_init */
                        NULL,   /* class_finalize */
                        NULL,   /* class_data */
                        sizeof (MiaLandmark),
                        0,      /* n_preallocs */
                        __mia_landmark_instance_init, 	/* instance_init */
			NULL /* value_table */
                };
                type = g_type_register_static (G_TYPE_OBJECT,
                                               "MiaLandmarkType",
                                               &info, 0);
        }
        return type;	
}

/**
 * mia_landmark_new:
 * @name: Name of the newly created landmark 
 * @location: 3D coordinates of the landmark 
 * @iso: intensity value corresponding to the 3D coordinates 
 * in the visualized volume data set 
 * @camera: viewing camera when marking the landmark 
 * @picfile: image file to give an idea where to find the landmark
 *
 *  Create a new landmark with the given characteristics.
 *  
 *  
 *  Returns: The newly created landmark
 *  
 */


MiaLandmark *mia_landmark_new(const gchar *name, 
			      MiaVector3d *location, 
			      float iso,
			      MiaCamera *camera,
			      gchar *picfile)
{
	MiaLandmark *self = (MiaLandmark*)g_object_new(MIA_TYPE_LANDMARK, NULL);
	
	g_return_val_if_fail(self, NULL);
	
	self->name = g_string_new(name);
	self->location = location;
	self->iso = iso; 
	self->camera = camera;
	self->picfile = g_string_new(picfile);
	return self; 			    
}

/**
 * mia_landmark_set_name:
 * @self: a landmark 
 * @name: the new name of the landmark 
 * 
 * Rename the landmark. 
 */

void mia_landmark_set_name(MiaLandmark *self, const  gchar *name)
{
	g_assert(MIA_IS_LANDMARK(self));
	if (self->name) 
		g_string_assign(self->name, name);
	else
		self->name = g_string_new(name);
}


/**
 * mia_landmark_get_name:
 * @self: a landmark 
 * 
 * Get the name of the landmark. 
 * 
 * Returns: the name of the landmark as character string 
 */
const gchar *mia_landmark_get_name(const MiaLandmark *self)
{
	g_assert(MIA_IS_LANDMARK(self));
	return self->name ? self->name->str : NULL;
}

/**
 * mia_landmark_set_location:
 * @self: a landmark 
 * @location: the new location of the landmark 
 * 
 * Set a new location to the landmark. The old one is unreferenced.
 * 
 * Returns: a pointer to the location of the landmark
 */
void mia_landmark_set_location(MiaLandmark *self, MiaVector3d *location)
{
	g_assert(MIA_IS_LANDMARK(self));
	if (G_IS_OBJECT(self->location))
		g_object_unref(self->location);
	self->location = location;
}

/**
 * mia_landmark_get_location:
 * @self: a landmark 
 * 
 * Get the location of the landmark. 
 * 
 * Returns: a pointer to the location of the landmark
 */
MiaVector3d *mia_landmark_get_location(const MiaLandmark *self)
{
	g_assert(MIA_IS_LANDMARK(self));
	return self->location;
}

/**
 * mia_landmark_get_iso_value:
 * @self: a landmark 
 * 
 * The iso value corresponds 
 * to in the intensity value that can be found at the  landmark location. 
 * 
 * Returns: a iso-surface intensity value 
 */
gfloat mia_landmark_get_iso_value(const MiaLandmark *self)
{
	g_assert(MIA_IS_LANDMARK(self));
	return self->iso;
}

/**
 * mia_landmark_set_iso_value:
 * @self: a landmark 
 * @iso: the iso-surface intensity value  
 * 
 * The iso value corresponds 
 * to in the intensity value that can be found at the  landmark location. 
 * 
 */
void mia_landmark_set_iso_value(MiaLandmark *self, gfloat iso)
{
	g_assert(MIA_IS_LANDMARK(self));
	self->iso = iso; 
}

/**
 * mia_landmark_set_camera:
 * @self: a landmark 
 * @camera: a camera   
 * 
 * Sets the viewing camera for the landmark. The viewing camera defines 
 * a view that has the landmark in its viewing range. 
 * 
 */

void mia_landmark_set_camera(MiaLandmark *self, MiaCamera *camera)
{
	g_assert(MIA_IS_LANDMARK(self));
	if (G_IS_OBJECT(self->camera))
		g_object_unref(self->camera);
	self->camera = camera;
}

/**
 * mia_landmark_get_camera:
 * @self: a landmark 
 * 
 * Get the camera defined for this landmark. 
 *
 * Returns: The camera
 *
 */

MiaCamera *mia_landmark_get_camera(const MiaLandmark *self)
{
	g_assert(MIA_IS_LANDMARK(self));
	return self->camera;
}

/**
 * mia_landmark_set_picfile:
 * @self: a landmark 
 * @picfile: The file name of an image file
 * 
 * The picture is supposed to show the landmark in its anatomical context. 
 * The old pictutre file name is replaces and if not then a copy of the 
 * string is stored. 
 *
 */
void mia_landmark_set_picfile(MiaLandmark *self, const gchar *picfile)
{
	g_assert(MIA_IS_LANDMARK(self));
	if (self->picfile) 
		g_string_assign(self->picfile, picfile);
	else
		self->picfile = g_string_new(picfile);
}

/**
 * mia_landmark_get_picfile:
 * @self: a landmark 
 * 
 * The picture is supposed to show the landmark in its anatomical context. 
 *
 * Returns: the picture file name, or NULL if it is not set. 
 */
const gchar *mia_landmark_get_picfile(const MiaLandmark *self)
{
	g_assert(MIA_IS_LANDMARK(self));
	return self->picfile ? self->picfile->str : NULL; 
}


static void
__mia_landmark_instance_init (GTypeInstance   *instance,
			      gpointer         UNUSED(g_class))
{
        MiaLandmark *self = (MiaLandmark *)instance;
        self->dispose_has_run = FALSE;
        self->name = NULL;
 }

enum {
	lm_name = 1,
	lm_location,
	lm_iso_value,
	lm_camera,
	lm_picfile,
	lm_counter
};


static void
__mia_landmark_set_property (GObject      *object,
                        guint         property_id,
                        const GValue *value,
                        GParamSpec   *pspec)
{
        MiaLandmark *self = (MiaLandmark *) object;
        switch (property_id) {
	case lm_name: 
		mia_landmark_set_name (self, g_value_get_string(value));
		break;	
	case lm_location:
		mia_landmark_set_location(self, MIA_VECTOR3D(g_value_get_pointer(value)));
		break; 
	case lm_iso_value:
		mia_landmark_set_iso_value(self,g_value_get_float(value));
		break;
	case lm_camera:
		mia_landmark_set_camera(self, MIA_CAMERA(g_value_get_pointer(value)));
		break;
	case lm_picfile:
		mia_landmark_set_picfile (self, g_value_get_string(value));
		break; 
        default:
                /* We don't have any other property but parent might have ... */
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);	
		break;
        }
}

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

        switch (property_id) {
        case lm_name:
                g_value_set_string (value, mia_landmark_get_name(self));
                break;
        case lm_location:
        	g_value_set_pointer(value, mia_landmark_get_location(self));
		break; 
        case lm_iso_value:
		g_value_set_double(value,mia_landmark_get_iso_value(self));
		break;
	case lm_camera:
		g_value_set_pointer(value, mia_landmark_get_camera(self));
		break;
	case lm_picfile:
	        g_value_set_string (value, mia_landmark_get_picfile(self));
                break;
        default:
	                /* We don't have any other property... */
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);	
	break;
        }
}

static GObject *
__mia_landmark_constructor (GType type,
                            guint n_construct_properties,
                            GObjectConstructParam *construct_properties)
{
        GObject *obj;

        {
                /* Invoke parent constructor. */
                MiaLandmarkClass *klass;
                GObjectClass *parent_class;  
                klass = MIA_LANDMARK_CLASS (g_type_class_peek (MIA_TYPE_LANDMARK));
                parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
                obj = parent_class->constructor (type,
                                                 n_construct_properties,
                                                 construct_properties);
        }
        
        /* do stuff. */
	
        return obj;
}

static void
__mia_landmark_dispose (GObject *obj)
{
        MiaLandmark *self = (MiaLandmark *)obj;

        if (self->dispose_has_run) {
                /* If dispose did already run, return. */
                return;
        }
        /* Make sure dispose does not run twice. */
        self->dispose_has_run = TRUE;
	if (self->camera) {
		g_object_unref(self->camera);
		self->camera = NULL; 
	}
	if (self->location) {
		g_object_unref(self->location);
		self->location = NULL; 
	}
}

static void
__mia_landmark_finalize (GObject *obj)
{
        MiaLandmark *self = (MiaLandmark *)obj;

       	if (self->name)
		g_string_free(self->name, TRUE);
	if (self->picfile)
		g_string_free(self->picfile, TRUE);
	
}


static void
__mia_landmark_class_init (gpointer g_class,
			   gpointer UNUSED(g_class_data))
{
        GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
        /*MiaLandmarkClass *klass = MIA_LANDMARK_CLASS (g_class);*/
	
        GParamSpec *pspec;
		
        gobject_class->set_property = __mia_landmark_set_property;
        gobject_class->get_property = __mia_landmark_get_property;
        gobject_class->dispose = __mia_landmark_dispose;
        gobject_class->finalize = __mia_landmark_finalize;
        gobject_class->constructor = __mia_landmark_constructor;

	
	pspec = g_param_spec_string (mia_landmark_name_property, "name","landmark name", 
				     "unknown", G_PARAM_READWRITE);	
	g_object_class_install_property (gobject_class, lm_name, pspec);
	
	pspec = g_param_spec_string (mia_landmark_picfile_property, "pic","picfile name", 
				     "unknown", G_PARAM_READWRITE);	
	g_object_class_install_property (gobject_class, lm_picfile, pspec);
	
	pspec = g_param_spec_pointer (mia_landmark_camera_property, "cam","propose viewpoint", 
				      G_PARAM_READWRITE);	
	g_object_class_install_property (gobject_class, lm_camera, pspec);

	pspec = g_param_spec_pointer (mia_landmark_location_property, "loc","location of the landmark", 
				      G_PARAM_READWRITE);	
	g_object_class_install_property (gobject_class, lm_location, pspec);

	pspec = g_param_spec_float (mia_landmark_isovalue_property, "iso","grayscale value of the landmark", 
			            0, 4096, 128, G_PARAM_READWRITE);
	g_object_class_install_property (gobject_class, lm_iso_value, pspec);
}

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

static void
__camara_end (ParserState * state, const  gchar *property)
{
	GValue value = G_VALUE_INIT;
	GObject *obj = G_OBJECT (state->pss->parent->data);
	g_assert (obj);
	g_value_init (&value, G_TYPE_POINTER);
	g_value_set_pointer (&value, (gpointer) state->pss->data);

	g_object_set_property (obj, property, &value);
	state->pss->data = NULL; 
}


const ParserTags landmark_parser_tags[] = {
	{mia_landmark_name_property,     NULL, xmlio_get_string, xmlio_end_string, NULL},
	{mia_landmark_location_property, NULL, xmlio_get_string, mia_vector3d_xmlio_read,  NULL},
	{mia_landmark_isovalue_property, NULL, xmlio_get_string, xmlio_end_float,  NULL},
	{mia_landmark_camera_property, __camara_start, NULL, __camara_end, camera_parser_tags},
	{mia_landmark_picfile_property,  NULL, xmlio_get_string, xmlio_end_string, NULL},
	END_PARSER_TAGS
};


/**
 * mia_landmark_xmlio_write:
 * @parent: XML node the landmark description will be attached to 
 * @ns: the XML namespace 
 * @lm: the landmark to be stored 
 * 
 * Stores the landmark as subnode of the given XML parent node.
 *
 * Returns: TRUE if the XML subtree is sucessfully created and attached to the 
 * parent node, FALSE otherwise. 
 */


gboolean
mia_landmark_xmlio_write (xmlNodePtr parent, xmlNsPtr ns, const MiaLandmark * lm)
{
	g_return_val_if_fail (lm, FALSE);

	xmlNewTextChild (parent, NULL, (const xmlChar *)mia_landmark_name_property, 
			 (const xmlChar *)mia_landmark_get_name (lm));
	xmlNewTextChild (parent, NULL, (const xmlChar *)mia_landmark_picfile_property,
			 (const xmlChar *)mia_landmark_get_picfile (lm));

	if (!mia_vector3d_xmlio_write
	    (parent, ns, mia_landmark_location_property, mia_landmark_get_location (lm)))
		return FALSE;
	if (!xml_write_float
	    (parent, ns, mia_landmark_isovalue_property, mia_landmark_get_iso_value (lm)))
		return FALSE;
	if (!mia_camera_xmlio_write
	    (parent, ns, mia_landmark_camera_property, mia_landmark_get_camera (lm)))
		return FALSE;
	return TRUE;
}
