/* sml_dev_inf_property.c
 *
 * Copyright (C) 2009 Michael Bell <michael.bell@opensync.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 */


#include "sml_dev_inf_property.h"
#include <libsyncml/syncml.h>
#include "../sml_error_internals.h"

G_DEFINE_TYPE (SmlDevInfProperty, sml_dev_inf_property, G_TYPE_OBJECT)

enum
{
	PROP_0,
	PROP_PROP_NAME,
	PROP_SIZE,
	PROP_DATA_TYPE,
	PROP_MAX_OCCUR,
	PROP_MAX_SIZE,
	PROP_NO_TRUNCATE,
	PROP_DISPLAY_NAME,
	PROP_VAL_ENUMS,
	PROP_PARAMS
};

struct _SmlDevInfPropertyPrivate
{
	gchar*   prop_name;
	gsize    size;
	gchar*   data_type;
	gsize    max_occur;
	gsize    max_size;
	gboolean no_truncate;
	gchar*   display_name;
	GList*   val_enums;
	GList*   params;
};

static void
sml_dev_inf_property_get_property (GObject    *object,
                                   guint       property_id,
                                   GValue     *value,
                                   GParamSpec *pspec)
{
	switch (property_id) {
	case PROP_PROP_NAME:
		g_value_set_string (value, SML_DEV_INF_PROPERTY (object)->priv->prop_name);
		break;
	case PROP_SIZE:
		g_value_set_uint (value, SML_DEV_INF_PROPERTY (object)->priv->size);
		break;
	case PROP_DATA_TYPE:
		g_value_set_string (value, SML_DEV_INF_PROPERTY (object)->priv->data_type);
		break;
	case PROP_MAX_OCCUR:
		g_value_set_uint (value, SML_DEV_INF_PROPERTY (object)->priv->max_occur);
		break;
	case PROP_MAX_SIZE:
		g_value_set_uint (value, SML_DEV_INF_PROPERTY (object)->priv->max_size);
		break;
	case PROP_NO_TRUNCATE:
		g_value_set_boolean (value, SML_DEV_INF_PROPERTY (object)->priv->no_truncate);
		break;
	case PROP_DISPLAY_NAME:
		g_value_set_string (value, SML_DEV_INF_PROPERTY (object)->priv->display_name);
		break;
	case PROP_VAL_ENUMS:
		g_value_set_pointer (value, SML_DEV_INF_PROPERTY (object)->priv->val_enums);
		break;
	case PROP_PARAMS:
		g_value_set_pointer (value, SML_DEV_INF_PROPERTY (object)->priv->params);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
sml_dev_inf_property_set_property (GObject      *object,
                                   guint         property_id,
                                   const GValue *value,
                                   GParamSpec   *pspec)
{
	switch (property_id) {
	case PROP_PROP_NAME:
		g_free (SML_DEV_INF_PROPERTY (object)->priv->prop_name);
		SML_DEV_INF_PROPERTY (object)->priv->prop_name = g_strdup (g_value_get_string (value));
		break;
	case PROP_SIZE:
		SML_DEV_INF_PROPERTY (object)->priv->size = g_value_get_uint (value);
		break;
	case PROP_DATA_TYPE:
		g_free (SML_DEV_INF_PROPERTY (object)->priv->data_type);
		SML_DEV_INF_PROPERTY (object)->priv->data_type = g_strdup (g_value_get_string (value));
		break;
	case PROP_MAX_OCCUR:
		SML_DEV_INF_PROPERTY (object)->priv->max_occur = g_value_get_uint (value);
		break;
	case PROP_MAX_SIZE:
		SML_DEV_INF_PROPERTY (object)->priv->max_size = g_value_get_uint (value);
		break;
	case PROP_NO_TRUNCATE:
		SML_DEV_INF_PROPERTY (object)->priv->no_truncate = g_value_get_boolean (value);
		break;
	case PROP_DISPLAY_NAME:
		g_free (SML_DEV_INF_PROPERTY (object)->priv->display_name);
		SML_DEV_INF_PROPERTY (object)->priv->display_name = g_strdup (g_value_get_string (value));
		break;
	case PROP_VAL_ENUMS:
		SML_DEV_INF_PROPERTY (object)->priv->val_enums = (GList *) g_value_get_pointer(value);
		break;
	case PROP_PARAMS:
		SML_DEV_INF_PROPERTY (object)->priv->params = (GList *) g_value_get_pointer(value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
	}
}

static void
sml_dev_inf_property_finalize (GObject *object)
{
	SmlDevInfProperty *self = (SmlDevInfProperty *) object;
	g_free(self->priv->prop_name);
	g_free(self->priv->data_type);
	g_free(self->priv->display_name);
	while(self->priv->val_enums) {
		gchar *val_enum = self->priv->val_enums->data;
		self->priv->val_enums = g_list_remove(self->priv->val_enums, val_enum);
		g_free(val_enum);
	}
	while(self->priv->params) {
		SmlDevInfPropParam *param = self->priv->params->data;
		self->priv->params = g_list_remove(self->priv->params, param);
		g_object_unref(param);
	}
	/* all pointers must be NULL */
	self->priv->prop_name = NULL;
	self->priv->data_type = NULL;
	self->priv->display_name = NULL;
	G_OBJECT_CLASS (sml_dev_inf_property_parent_class)->finalize (object);
}

static void
sml_dev_inf_property_class_init (SmlDevInfPropertyClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	g_type_class_add_private (klass, sizeof (SmlDevInfPropertyPrivate));

	object_class->get_property = sml_dev_inf_property_get_property;
	object_class->set_property = sml_dev_inf_property_set_property;
	object_class->finalize     = sml_dev_inf_property_finalize;

	/**
	 * SmlDevInfProperty:PropName:
	 *
	 * The  property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_PROP_NAME,
	                                 g_param_spec_string ("PropName",
	                                                      "",
	                                                      "",
	                                                      NULL,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDevInfProperty:Size:
	 *
	 * The  property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_SIZE,
	                                 g_param_spec_uint64 ("Size",
	                                                      "",
	                                                      "",
	                                                      0,
	                                                      G_MAXUINT64,
	                                                      0,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDevInfProperty:DataType:
	 *
	 * The  property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_DATA_TYPE,
	                                 g_param_spec_string ("DataType",
	                                                      "",
	                                                      "",
	                                                      NULL,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDevInfProperty:MaxOccur:
	 *
	 * The  property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_MAX_OCCUR,
	                                 g_param_spec_uint64 ("MaxOccur",
	                                                      "",
	                                                      "",
	                                                      0,
	                                                      G_MAXUINT64,
	                                                      0,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDevInfProperty:MaxSize:
	 *
	 * The  property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_MAX_SIZE,
	                                 g_param_spec_uint64 ("MaxSize",
	                                                      "",
	                                                      "",
	                                                      0,
	                                                      G_MAXUINT64,
	                                                      0,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDevInfProperty:NoTruncate:
	 *
	 * The  property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_NO_TRUNCATE,
	                                 g_param_spec_boolean ("NoTruncate",
	                                                       "",
	                                                       "",
	                                                       FALSE,
	                                                       G_PARAM_READWRITE));
	/**
	 * SmlDevInfProperty:DisplayName:
	 *
	 * The  property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_DISPLAY_NAME,
	                                 g_param_spec_string ("DisplayName",
	                                                      "",
	                                                      "",
	                                                      NULL,
	                                                      G_PARAM_READWRITE));
	/**
	 * SmlDevInfProperty:ValEnums:
	 *
	 * The  property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_VAL_ENUMS,
	                                 g_param_spec_pointer ("ValEnums",
	                                                      "ValEnums",
	                                                      "",
	                                                      G_PARAM_PRIVATE));
	/**
	 * SmlDevInfProperty:PropParams:
	 *
	 * The  property.
	 */
	g_object_class_install_property (object_class,
	                                 PROP_PARAMS,
	                                 g_param_spec_pointer ("Params",
	                                                      "Params",
	                                                      "",
	                                                      G_PARAM_PRIVATE));
}

static void
sml_dev_inf_property_init (SmlDevInfProperty *self)
{
	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
	                                          SML_TYPE_DEV_INF_PROPERTY,
	                                          SmlDevInfPropertyPrivate);
}

/**
 * sml_dev_inf_property_new:
 *
 * Creates a new instance of #SmlDevInfProperty.
 *
 * Return value: the newly created #SmlDevInfProperty instance
 */
SmlDevInfProperty*
sml_dev_inf_property_new (void)
{
	return g_object_new (SML_TYPE_DEV_INF_PROPERTY, NULL);
}

/**
 * sml_dev_inf_property_get_prop_name:
 * @self: A #SmlDevInfProperty
 *
 * Gets the  property.
 *
 * Return value: 
 */
G_CONST_RETURN gchar*
sml_dev_inf_property_get_prop_name (SmlDevInfProperty *self)
{
	g_return_val_if_fail (SML_IS_DEV_INF_PROPERTY (self), NULL);
	return self->priv->prop_name;
}

/**
 * sml_dev_inf_property_set_prop_name:
 * @self: A #SmlDevInfProperty
 * @prop_name:
 *
 * Sets the  property.
 * The property cannot have the name NULL but it can have the name ""
 * (the empty word) because SyncML 1.1 requires that a CTCap has at
 * minimum one property.
 *
 * Example: If the content type is text/plain then it makes no sense
 * to define properties with names but it makes sense to define a
 * maximum size for the document.
 */
gboolean
sml_dev_inf_property_set_prop_name (SmlDevInfProperty *self,
                                    const gchar* prop_name,
                                    GError **error)
{
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (SML_IS_DEV_INF_PROPERTY (self), FALSE, error, SML_ERROR_GENERIC, "There is no SmlDevInfProperty object.");
	sml_return_val_error_if_fail (prop_name, FALSE, error, SML_ERROR_GENERIC, "The property name is required.");

	g_free (self->priv->prop_name);

	self->priv->prop_name = g_strdup (prop_name);
	sml_return_val_error_if_fail (self->priv->prop_name, FALSE, error, SML_ERROR_GENERIC, "Cannot copy property name - out of memory.");

	return TRUE;
}

/**
 * sml_dev_inf_property_get_size:
 * @self: A #SmlDevInfProperty
 *
 * Gets the  property.
 *
 * Return value: 
 */
gsize
sml_dev_inf_property_get_size (SmlDevInfProperty *self)
{
	g_return_val_if_fail (SML_IS_DEV_INF_PROPERTY (self), 0);
	return self->priv->size;
}

/**
 * sml_dev_inf_property_set_size:
 * @self: A #SmlDevInfProperty
 * @size:
 *
 * Sets the  property.
 */
gboolean
sml_dev_inf_property_set_size (SmlDevInfProperty *self,
                               gsize size,
                               GError **error)
{
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (SML_IS_DEV_INF_PROPERTY (self), FALSE, error, SML_ERROR_GENERIC, "There is no SmlDevInfProperty object.");

	if (0 < self->priv->max_size && self->priv->max_size < size) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "The size must be lower than the maximum size.");
		return FALSE;
	}
	self->priv->size = size;

	return TRUE;
}

/**
 * sml_dev_inf_property_get_datatype:
 * @self: A #SmlDevInfProperty
 *
 * Gets the  property.
 *
 * Return value: 
 */
G_CONST_RETURN gchar*
sml_dev_inf_property_get_data_type (SmlDevInfProperty *self)
{
	g_return_val_if_fail (SML_IS_DEV_INF_PROPERTY (self), NULL);
	return self->priv->data_type;
}

/**
 * sml_dev_inf_property_set_data_type:
 * @self: A #SmlDevInfProperty
 * @data_type:
 *
 * Sets the  property.
 */
gboolean
sml_dev_inf_property_set_data_type (SmlDevInfProperty *self,
                                    const gchar* data_type,
                                    GError **error)
{
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (SML_IS_DEV_INF_PROPERTY (self), FALSE, error, SML_ERROR_GENERIC, "There is no SmlDevInfProperty object.");

	g_free (self->priv->data_type);

	/* normalization */
	if (data_type && strlen(data_type) == 0)
		data_type = NULL;

	self->priv->data_type = g_strdup (data_type);
	sml_return_val_error_if_fail (!data_type || self->priv->data_type, FALSE, error, SML_ERROR_GENERIC, "Cannot copy data type - out of memory.");

	return TRUE;
}

/**
 * sml_dev_inf_property_get_max_occur:
 * @self: A #SmlDevInfProperty
 *
 * Gets the  property.
 *
 * Return value: 
 */
gsize
sml_dev_inf_property_get_max_occur (SmlDevInfProperty *self)
{
	g_return_val_if_fail (SML_IS_DEV_INF_PROPERTY (self), 0);
	return self->priv->max_occur;
}

/**
 * sml_dev_inf_property_set_max_occur:
 * @self: A #SmlDevInfProperty
 * @maxoccur:
 *
 * Sets the  property.
 */
void
sml_dev_inf_property_set_max_occur (SmlDevInfProperty *self,
                                    gsize max_occur)
{
	g_return_if_fail (SML_IS_DEV_INF_PROPERTY (self));
	self->priv->max_occur = max_occur;
}

/**
 * sml_dev_inf_property_get_max_size:
 * @self: A #SmlDevInfProperty
 *
 * Gets the  property.
 *
 * Return value: 
 */
gsize
sml_dev_inf_property_get_max_size (SmlDevInfProperty *self)
{
	g_return_val_if_fail (SML_IS_DEV_INF_PROPERTY (self), 0);
	return self->priv->max_size;
}

/**
 * sml_dev_inf_property_set_max_size:
 * @self: A #SmlDevInfProperty
 * @maxsize:
 *
 * Sets the  property.
 */
gboolean
sml_dev_inf_property_set_max_size (SmlDevInfProperty *self,
                                   gsize max_size,
                                   GError **error)
{
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (SML_IS_DEV_INF_PROPERTY (self), FALSE, error, SML_ERROR_GENERIC, "There is no SmlDevInfProperty object.");

	if (0 < max_size && max_size < self->priv->size) {
		*error = g_error_new(SML_ERROR, SML_ERROR_GENERIC, "The maximum size must be greater than the size.");
		return FALSE;
	}
	self->priv->max_size = max_size;
	return TRUE;
}

/**
 * sml_dev_inf_property_get_no_truncate:
 * @self: A #SmlDevInfProperty
 *
 * Gets the  property.
 *
 * Return value: 
 */
gboolean
sml_dev_inf_property_get_no_truncate (SmlDevInfProperty *self)
{
	g_return_val_if_fail (SML_IS_DEV_INF_PROPERTY (self), FALSE);
	return self->priv->no_truncate;
}

/**
 * sml_dev_inf_property_set_no_truncate:
 * @self: A #SmlDevInfProperty
 * @notruncate:
 *
 * Sets the  property.
 */
void
sml_dev_inf_property_set_no_truncate (SmlDevInfProperty *self,
                                      gboolean no_truncate)
{
	g_return_if_fail (SML_IS_DEV_INF_PROPERTY (self));
	self->priv->no_truncate = no_truncate;
}

/**
 * sml_dev_inf_property_get_display_name:
 * @self: A #SmlDevInfProperty
 *
 * Gets the  property.
 *
 * Return value: 
 */
G_CONST_RETURN gchar*
sml_dev_inf_property_get_display_name (SmlDevInfProperty *self)
{
	g_return_val_if_fail (SML_IS_DEV_INF_PROPERTY (self), NULL);
	return self->priv->display_name;
}

/**
 * sml_dev_inf_property_set_display_name:
 * @self: A #SmlDevInfProperty
 * @displayname:
 *
 * Sets the  property.
 */
gboolean
sml_dev_inf_property_set_display_name (SmlDevInfProperty *self,
                                      const gchar* display_name,
                                      GError **error)
{
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (SML_IS_DEV_INF_PROPERTY (self), FALSE, error, SML_ERROR_GENERIC, "There is no SmlDevInfProperty object.");

	g_free (self->priv->display_name);

	/* normalization */
	if (display_name && strlen(display_name) == 0)
		display_name = NULL;

	self->priv->display_name = g_strdup (display_name);
	sml_return_val_error_if_fail (!display_name || self->priv->display_name, FALSE, error, SML_ERROR_GENERIC, "Cannot copy display name - out of memory.");

	return TRUE;
}

/**
 * sml_dev_inf_property_num_val_enums:
 * @self: A #SmlDevInfProperty
 *
 * 
 */
gsize
sml_dev_inf_property_num_val_enums (SmlDevInfProperty *self)
{
	g_return_val_if_fail (SML_IS_DEV_INF_PROPERTY (self), 0);
	return g_list_length(self->priv->val_enums);
}

/**
 * sml_dev_inf_property_get_nth_val_enum:
 * @self: A #SmlDevInfProperty
 *
 * 
 */
const gchar*
sml_dev_inf_property_get_nth_val_enum (SmlDevInfProperty *self,
                                       gsize n)
{
	g_return_val_if_fail (SML_IS_DEV_INF_PROPERTY (self), NULL);
	return (gchar *)g_list_nth_data(self->priv->val_enums, n);
}

/**
 * sml_dev_inf_property_add_val_enum:
 * @self: A #SmlDevInfProperty
 *
 * 
 */
gboolean
sml_dev_inf_property_add_val_enum (SmlDevInfProperty *self,
                                   const gchar* val_enum,
                                   GError **error)
{
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (SML_IS_DEV_INF_PROPERTY (self), FALSE, error, SML_ERROR_GENERIC, "There is no SmlDevInfProperty object.");
	sml_return_val_error_if_fail (val_enum, FALSE, error, SML_ERROR_GENERIC, "The ValEnum must be filled.");
	sml_return_val_error_if_fail (strlen(val_enum), FALSE, error, SML_ERROR_GENERIC, "The ValEnum must not be the empty word.");

	self->priv->val_enums = g_list_append(self->priv->val_enums, g_strdup(val_enum));
	sml_return_val_error_if_fail (self->priv->val_enums, FALSE, error, SML_ERROR_GENERIC, "Cannot add ValEnum to ValEnum list of Property.");

	return TRUE;
}

/**
 * sml_dev_inf_property_num_params:
 * @self: A #SmlDevInfProperty
 *
 * 
 */
gsize
sml_dev_inf_property_num_params (SmlDevInfProperty *self)
{
	g_return_val_if_fail (SML_IS_DEV_INF_PROPERTY (self), 0);
	return g_list_length(self->priv->params);
}

/**
 * sml_dev_inf_property_get_nth_param:
 * @self: A #SmlDevInfProperty
 *
 * 
 */
SmlDevInfPropParam*
sml_dev_inf_property_get_nth_param (SmlDevInfProperty *self,
                                    gsize n)
{
	g_return_val_if_fail (SML_IS_DEV_INF_PROPERTY (self), NULL);
	return (SmlDevInfPropParam *)g_list_nth_data(self->priv->params, n);
}

/**
 * sml_dev_inf_property_add_param:
 * @self: A #SmlDevInfProperty
 *
 * The SmlDevInfPropParam object is handled like a normal const string.
 * This means that the SmlDevInfProperty object registers an own reference
 * to the object. The caller has to maintain its reference to the
 * SmlDevInfPropParam object by himself.
 *
 * sml_dev_inf_property_add_prop_param does not consume a reference of
 * the SmlDevInfPropParam object.
 */
gboolean
sml_dev_inf_property_add_param (SmlDevInfProperty *self,
                                SmlDevInfPropParam *param,
                                GError **error)
{
	CHECK_ERROR_REF
	sml_return_val_error_if_fail (SML_IS_DEV_INF_PROPERTY (self), FALSE, error, SML_ERROR_GENERIC, "There is no SmlDevInfProperty object.");
	sml_return_val_error_if_fail (param, FALSE, error, SML_ERROR_GENERIC, "The SmlDevInfPropParam object is missing.");
	sml_return_val_error_if_fail (SML_IS_DEV_INF_PROP_PARAM (param), FALSE, error, SML_ERROR_GENERIC, "The param must be a SmlDevInfPropParam object.");

	g_object_ref(param);
	self->priv->params = g_list_append(self->priv->params, param);
	sml_return_val_error_if_fail (self->priv->params, FALSE, error, SML_ERROR_GENERIC, "Cannot add PropParam to PropParam list of Property.");

	return TRUE;
}

