/*
 * Copyright (C) 2012 - Juan Ferrer Toribio
 *
 * 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, see <http://www.gnu.org/licenses/>.
 */

#include "gvn-misc.h"
#include "gvn-param.h"
#include "gvn-value.h"

/**
 * SECTION: gvn-param
 * @Short_description:
 * @Title: GvnParam 
 **/
G_DEFINE_INTERFACE (GvnParam, gvn_param, G_TYPE_OBJECT);

enum {
	 VALUE_CHANGED
	,SPEC_CHANGED
	,STATUS_CHANGED
	,LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = {0};

//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public

/**
 * gvn_param_get_value:
 * @self: a #GvnParam where be took the value
 *
 * Gets the value of param.
 *
 * Return value: (transfer none): the #GValue
 **/
const GValue * gvn_param_get_value (GvnParam * self)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), NULL);

	return GVN_PARAM_GET_INTERFACE (self)->get_value (self);
}

/**
 * gvn_param_request_value:
 * @self: #GvnParam object where @value wants to be validated.
 * @value: new value.
 * @err: (out) (allow-none): the return location for an allocated @GError, or
 * NULL to ignore errors.
 *
 * Sets @value into @self.
 *
 * Return value: %TRUE if assigment is valid, %FALSE otherwise.
 **/
gboolean gvn_param_request_value (GvnParam * self, const GValue * value, GError ** err)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), FALSE);
	g_return_val_if_fail (G_IS_VALUE (value), FALSE);

	return GVN_PARAM_GET_INTERFACE (self)->request_value (self, value, err);
}

/**
 * gvn_param_set_value:
 * @self: #GvnParam object where @value wants to be validated.
 * @value: new value.
 *
 * Sets @value into @self.
 **/
void gvn_param_set_value (GvnParam * self, const GValue * value)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), FALSE);
	g_return_val_if_fail (G_IS_VALUE (value), FALSE);
	
	gvn_param_request_value (self, value, NULL);
}

/**
 * gvn_param_get_master:
 * @self: a #GvnParam where be took the value
 *
 * Gets the master param of @self
 *
 * Return value: (transfer none): the master #GvnParam or %NULL if it haven't.
 **/
GvnParam * gvn_param_get_master (GvnParam * self)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), NULL);

	return GVN_PARAM_GET_INTERFACE (self)->get_master (self);
}

/**
 * gvn_param_set_master:
 * @self: #GvnParam object
 * @master: #GvnParam master
 *
 * Sets @dst as a master of @src
 **/
void gvn_param_set_master (GvnParam * self, GvnParam * master)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), NULL);
	g_return_val_if_fail (GVN_IS_PARAM (master), NULL);

	GVN_PARAM_GET_INTERFACE (self)->set_master (self, master);
}

/**
 * gvn_param_get_spec:
 * @self: #GvnParam object
 *
 * Gets the #GvnParamSpec of @self
 *
 * Return value: (transfer none): the #GvnParamSpec
 **/
const GvnParamSpec * gvn_param_get_spec (GvnParam * self)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), NULL);

	return GVN_PARAM_GET_INTERFACE (self)->get_spec (self);
}

/**
 * gvn_param_get_status:
 * @self: #GvnParam object
 *
 * Gets the status of @self
 *
 * Return value: the current status of the param.
 **/
GvnParamStatus gvn_param_get_status (GvnParam * self)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), GVN_PARAM_STATUS_OK);

	return GVN_PARAM_GET_INTERFACE (self)->get_status (self);
}

/**
 * gvn_param_value_changed:
 * @self: a #GvnParam
 *
 * Emits the "value-changed" signal for that param.
 **/
void gvn_param_value_changed (GvnParam * self)
{
	g_signal_emit (self, signals[VALUE_CHANGED], 0, gvn_param_get_value (self));
}

// Useful methods

static void gvn_param_take_value (GvnParam * self, GValue * value)
{
	gvn_param_set_value (self, value);
	gvn_value_free (value);
}

gboolean gvn_param_is_null (GvnParam * self)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), TRUE);

	return gvn_value_is_null (gvn_param_get_value (self));
}

void gvn_param_set_null (GvnParam * self)
{
	g_return_if_fail (GVN_IS_PARAM (self));
	
	gvn_param_take_value (self, gvn_value_new_null ());
}

gboolean gvn_param_get_boolean (GvnParam * self)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), FALSE);

	return gvn_value_get_boolean (gvn_param_get_value (self));
}

void gvn_param_set_boolean (GvnParam * self, gboolean value)
{
	g_return_if_fail (GVN_IS_PARAM (self));

	gvn_param_take_value (self, gvn_value_new_boolean (value));
}

gint gvn_param_get_int (GvnParam * self)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), 0);

	return gvn_value_get_int (gvn_param_get_value (self));;
}

void gvn_param_set_int (GvnParam * self, gint value)
{
	g_return_if_fail (GVN_IS_PARAM (self));

	gvn_param_take_value (self, gvn_value_new_int (value));
}

glong gvn_param_get_long (GvnParam * self)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), 0);

	return gvn_value_get_long (gvn_param_get_value (self));
}

void gvn_param_set_long (GvnParam * self, glong value)
{
	g_return_if_fail (GVN_IS_PARAM (self));

	gvn_param_take_value (self, gvn_value_new_long (value));
}

gdouble gvn_param_get_double (GvnParam * self)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), 0);

	return gvn_value_get_double (gvn_param_get_value (self));
}

void gvn_param_set_double (GvnParam * self, gdouble value)
{
	g_return_if_fail (GVN_IS_PARAM (self));

	gvn_param_take_value (self, gvn_value_new_double (value));
}

const gchar * gvn_param_get_string (GvnParam * self)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), NULL);

	return gvn_value_get_string (gvn_param_get_value (self));
}

void gvn_param_set_string (GvnParam * self, const gchar * value)
{
	g_return_if_fail (GVN_IS_PARAM (self));

	gvn_param_take_value (self, gvn_value_new_string (value));
}

/**
 * gvn_param_get_boxed:
 * Return value: (transfer none):
 **/
gpointer gvn_param_get_boxed (GvnParam * self)
{
	g_return_val_if_fail (GVN_IS_PARAM (self), NULL);

	return gvn_value_get_boxed (gvn_param_get_value (self));
}

void gvn_param_set_boxed (GvnParam * self, gpointer value)
{
	g_return_if_fail (GVN_IS_PARAM (self));

	gvn_param_take_value (self, gvn_value_new_boxed (value));
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class

static void gvn_param_default_init (GvnParamInterface * iface)
{
	signals[VALUE_CHANGED] = g_signal_new ("value-changed",
		GVN_TYPE_PARAM, G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
		g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, G_TYPE_VALUE
	);
	signals[SPEC_CHANGED] = g_signal_new ("spec-changed",
		GVN_TYPE_PARAM, G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
		g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0
	);
	signals[STATUS_CHANGED] = g_signal_new ("status-changed",
		GVN_TYPE_PARAM, G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
		g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0
	);

	g_object_interface_install_property (iface,
		g_param_spec_boxed ("value"
			,_("Value")
			,_("The value of the param")
			,G_TYPE_VALUE
			,G_PARAM_READWRITE
	));
	g_object_interface_install_property (iface,
		g_param_spec_object ("master"
			,_("Master")
			,_("The master parameter")
			,GVN_TYPE_PARAM
			,G_PARAM_READWRITE
	));
}