/*-
 * $Id: echo.c,v 1.23 2002/07/15 20:07:37 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 "echo.h"

#include <stdio.h>
#include <string.h>

static GObjectClass *parent_class = NULL;

static gboolean frame_available (RRChannel *channel, RRFrame *frame, 
				 GError **error);
static void close_confirmation (RRChannel *channel, gint code,
				const gchar *xml_lang, 
				const gchar *description);

static void finalize (GObject *object);

static void
rr_null_echo_init (GObject *object)
{
	RRNullEcho *echo = RR_NULL_ECHO (object);

	echo->active_mutex = g_mutex_new ();
	echo->active_cond  = g_cond_new  ();
}

static void
rr_null_echo_class_init (GObjectClass *klass)
{
	RRChannelClass *channel_class = (RRChannelClass *)klass;

	channel_class->frame_available     = frame_available;
	channel_class->close_confirmation  = close_confirmation;

	klass->finalize = finalize;

	parent_class = g_type_class_peek_parent (klass);
}

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

	if (!rr_type) {
		static GTypeInfo type_info = {
			sizeof (RRNullEchoClass),
			NULL,
			NULL,
			(GClassInitFunc) rr_null_echo_class_init,
			NULL,
			NULL,
			sizeof (RRNullEcho),
			16,
			(GInstanceInitFunc) rr_null_echo_init
		};
		rr_type = g_type_register_static (RR_TYPE_CHANNEL, 
						  "RRNullEcho", 
						  &type_info, 0);

		rr_channel_set_uri (rr_type, NULL_ECHO_URI);
	}
	return rr_type;
}

static void
finalize (GObject *object)
{
	RRNullEcho *echo = RR_NULL_ECHO (object);

	g_mutex_free (echo->active_mutex);
	g_cond_free  (echo->active_cond);

	parent_class->finalize (object);
}

static void
wait_inactive (RRNullEcho *echo)
{
	g_mutex_lock (echo->active_mutex);
	while (echo->active)
		g_cond_wait (echo->active_cond, echo->active_mutex);
	g_mutex_unlock (echo->active_mutex);
}

static void
set_active (RRNullEcho *echo, gboolean active)
{
	g_mutex_lock (echo->active_mutex);
	echo->active = active;
	g_cond_signal (echo->active_cond);
	g_mutex_unlock (echo->active_mutex);
}
static gboolean
is_active (RRNullEcho *echo)
{
	gboolean active;
	g_mutex_lock (echo->active_mutex);
	active = echo->active;
	g_mutex_unlock (echo->active_mutex);
	return active;
}

static void
close_confirmation (RRChannel *channel, gint code,
		    const gchar *xml_lang, const gchar *description)
{
	RRNullEcho *echo = RR_NULL_ECHO (channel);

	if (is_active (echo)) {
		echo->echo_error =
			g_error_new (RR_ERROR, /* error domain */
				     RR_ERROR_OTHER,/* error code */
				     "Channel closed.");

		set_active (echo, FALSE);
	}
}

static gboolean
frame_available (RRChannel *channel, RRFrame *frame, GError **error)
{
	RRNullEcho *echo = RR_NULL_ECHO (channel);

	switch (frame->type) {
	case RR_FRAME_TYPE_MSG:
		/* Return the frame, only the frame type is changed
		 * from MSG to RPY */
		g_object_ref (G_OBJECT (frame));
		frame->type = RR_FRAME_TYPE_RPY;
		if (!rr_channel_send_frame (RR_CHANNEL (echo), frame, error))
			return FALSE;

		break;
	case RR_FRAME_TYPE_RPY:
		/* Append the data we received to the output buffer */
		memcpy (echo->obuffer, frame->payload, frame->size);
		echo->obuffer += frame->size;
		/* Are we done? */
		if (!frame->more)
			set_active (echo, FALSE);
		break;
	default:
		echo->echo_error =
			g_error_new (RR_BEEP_ERROR, RR_BEEP_CODE_SYNTAX_ERROR,
				     "Unexpected frame received.");
		
		set_active (echo, FALSE);
		break;
	}
	return TRUE;
}

gboolean
rr_null_echo_trip (RRNullEcho *echo, 
		   const guint8 *ibuffer, gsize ilen,
		   guint8 *obuffer, gsize olen,
		   GError **error)
{
	RRMessage *msg;

	g_return_val_if_fail (RR_IS_NULL_ECHO (echo), FALSE);
	g_return_val_if_fail (!is_active (echo), FALSE);

	set_active (echo, TRUE);

	echo->obuffer = obuffer;

	msg = rr_message_static_new (RR_FRAME_TYPE_MSG, (guint8 *)ibuffer, 
				     ilen, FALSE);

	if (!rr_channel_send_message (RR_CHANNEL (echo), msg, error))
		return FALSE;

	wait_inactive (echo);

	if (echo->echo_error) {

		g_propagate_error (error, echo->echo_error);
		echo->echo_error = NULL;
		return FALSE;
	}

	return TRUE;
}


syntax highlighted by Code2HTML, v. 0.9.1