/*-
 * $Id: rr-message-close.c,v 1.19 2002/11/08 12:35:28 jonas Exp $
 *
 * See the file LICENSE for redistribution information. 
 * If you have not received a copy of the license, please contact CodeFactory
 * by email at info@codefactory.se, or on the web at http://www.codefactory.se/
 * You may also write to: CodeFactory AB, SE-903 47, Umeå, Sweden.
 *
 * Copyright (c) 2002 Jonas Borgström <jonas@codefactory.se>
 * Copyright (c) 2002 Daniel Lundin   <daniel@codefactory.se>
 * Copyright (c) 2002 CodeFactory AB.  All rights reserved.
 */

#include <librr/rr.h>
#include <string.h>

#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

static GObjectClass *parent_class = NULL;
static void finalize (GObject *object);
static gboolean process_frame (RRMessage *message, RRFrame *frame, GError **error);
static RRFrame *get_frame (RRMessage *stat, gsize max_size);

static void
finalize (GObject *object)
{
	RRMessageClose *mclose = (RRMessageClose *)object;
	
	if (mclose->xml_lang)
		g_free (mclose->xml_lang);

	if (mclose->diagnostic)
		g_free (mclose->diagnostic);

	g_mutex_free (mclose->done_mutex);
	g_cond_free (mclose->done_cond);
	parent_class->finalize (object);
}

static void
rr_message_close_init (GObject *object)
{
	RRMessageClose *mclose = (RRMessageClose *)object;
	RRMessage *msg = (RRMessage *)object;

	mclose->done_mutex = g_mutex_new ();
	mclose->done_cond  = g_cond_new ();
	msg->type = RR_FRAME_TYPE_MSG;
}

static void
rr_message_close_class_init (GObjectClass *klass)
{
	RRMessageClass *msg_class = (RRMessageClass *)klass;

	klass->finalize = finalize;
	msg_class->get_frame = get_frame;
	msg_class->process_frame = process_frame;

	parent_class = g_type_class_peek_parent (klass);
}

GType 
rr_message_close_get_type (void)
{
	static GType rr_type = 0;

	if (!rr_type) {
		static GTypeInfo type_info = {
			sizeof (RRMessageCloseClass),
			NULL,
			NULL,
			(GClassInitFunc) rr_message_close_class_init,
			NULL,
			NULL,
			sizeof (RRMessageClose),
			16,
			(GInstanceInitFunc) rr_message_close_init
		};
		rr_type = g_type_register_static (RR_TYPE_MESSAGE, 
						  "RRMessageClose", 
						  &type_info, 0);
	}
	return rr_type;
}


RRMessageClose *
rr_message_close_new (gint channelnumber, gint code, const gchar *xml_lang,
		      const gchar *diagnostic)
{
	RRMessageClose *mclose;

	mclose = g_object_new (RR_TYPE_MESSAGE_CLOSE, NULL);

	mclose->channelnumber = channelnumber;
	mclose->code = code;
	mclose->xml_lang = g_strdup (xml_lang);
	mclose->diagnostic = g_strdup (diagnostic);

	return mclose;
}

static gboolean
process_frame (RRMessage *message, RRFrame *frame, GError **error)
{
	RRMessageClose *close;
	RRConnection *conn;
	xmlDocPtr doc;
	xmlNodePtr node;
	gboolean ret;
	xmlChar *str;
	const gchar *data;
	gint len;

	g_return_val_if_fail (RR_IS_MESSAGE_CLOSE (message), FALSE);
	g_return_val_if_fail (RR_IS_FRAME (frame), FALSE);
	g_return_val_if_fail (RR_IS_CHANNEL (message->channel), FALSE);
	g_return_val_if_fail (RR_IS_CONNECTION (message->channel->connection), 
			      FALSE);

	conn = message->channel->connection;
	close = RR_MESSAGE_CLOSE (message);

	data = rr_frame_mime_get_body (frame);
	len  = rr_frame_mime_get_body_size (frame);

	doc = xmlParseMemory (data, len);
	if (doc == NULL) {
		g_set_error (error, RR_ERROR, RR_BEEP_CODE_SYNTAX_ERROR, 
			     "Invalid close message.");
		return FALSE;
	}
	g_return_val_if_fail (doc != NULL, FALSE);

	node = xmlDocGetRootElement (doc);

	if (strcmp (node->name, "close") != 0)
		goto err;

	if ((str = xmlGetProp (node, "number")) == NULL)
		goto err;
	close->channelnumber = atoi (str);
	xmlFree (str);

	if ((str = xmlGetProp (node, "code")) == NULL)
		goto err;
	close->code = atoi (str);
	xmlFree (str);

	/* FIXME: check for a "xml:" namespace */
	if ((str = xmlGetProp (node, "lang"))) {

		close->xml_lang = g_strdup (str);
		xmlFree (str);
	}

	/* Extract the piggyback data if it exists */
	if (node->children) {
		xmlNode *child, *cdata_node = NULL;
		
		child = node->children;
		while (child) {
			if (child->type == XML_CDATA_SECTION_NODE &&
			    child->content) {
				cdata_node = child;
				break;
			}
			child = child->next;
		}
		if (cdata_node && child->content)
			close->diagnostic = g_strdup (cdata_node->content);
		else if (node->children->type == XML_TEXT_NODE &&
			 node->children->content)
			close->diagnostic = g_strdup (node->children->content);
	}

	ret = TRUE;
	goto end;
 err:
	g_set_error (error, RR_ERROR, RR_BEEP_CODE_PARAM_ERROR, RR_GERROR_DEFAULT_MESSAGE); 
	ret = FALSE;
 end:
	xmlFreeDoc (doc);

	return ret;
}

static RRFrame *
get_frame (RRMessage *message, gsize max_size)
{
	RRMessageClose *mclose = RR_MESSAGE_CLOSE(message);
	RRFrame *frame;
	GString *str;

	g_assert (RR_IS_MESSAGE (mclose));

	str = g_string_new (NULL);

	g_string_printf (str, RR_BEEP_MIME_HEADER
			 "<close number='%d' code='%03d'", 
			 mclose->channelnumber, mclose->code);

	if (mclose->xml_lang) {
		g_string_append_printf (str, " xml:lang='%s'", 
					mclose->xml_lang);
	}

	if (mclose->diagnostic) {
		g_string_append_printf (str, ">%s</close>\r\n", 
					mclose->diagnostic);
	} else {
		g_string_append (str, " />\r\n");
	}

	if (str->len > max_size) {

		g_string_free (str, TRUE);
		return NULL;
	}

	frame = rr_frame_new (RR_FRAME_TYPE_MSG, 
			      message->channel->id, FALSE, message->msgno,
			      str->len, 0,
			      str->str, TRUE);

	g_string_free (str, FALSE);

	return frame;
}

void
rr_message_close_done (RRMessageClose *mclose, GError *error)
{
	g_mutex_lock (mclose->done_mutex);
	/* FIXME: */
	if (error)
		mclose->done_error = g_error_copy (error);
	mclose->done = TRUE;
	g_cond_broadcast (mclose->done_cond);
	g_mutex_unlock (mclose->done_mutex);
}

gboolean
rr_message_close_wait_for_reply (RRMessageClose *mclose, GError **error)
{
	g_mutex_lock (mclose->done_mutex);
	while (!mclose->done)
		g_cond_wait (mclose->done_cond, mclose->done_mutex);
	g_mutex_unlock (mclose->done_mutex);

	if (mclose->done_error) {
		g_propagate_error (error, mclose->done_error);
		mclose->done_error = NULL;
		return FALSE;
	}
	return TRUE;
}


syntax highlighted by Code2HTML, v. 0.9.1