/*-
 * $Id: rr-framefactory.c,v 1.11 2002/05/11 17:54:01 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 <stdio.h>
#include <string.h>

static GStaticRWLock rwlock = G_STATIC_RW_LOCK_INIT;
static GSList *frame_types = NULL;   /* Protected by the rwlock above */

typedef struct {
	const gchar *header_id;
	GType type;
} FrameTypeInfo;

static GType
get_frame_type (const guint8 *buffer)
{
	char str[4];
	GSList *list;
	FrameTypeInfo *info;
	GType type = G_TYPE_INVALID;

	g_return_val_if_fail (buffer != NULL, type);

	if (sscanf (buffer, "%3s", str) != 1) {
		return G_TYPE_INVALID;
	}

	g_static_rw_lock_reader_lock (&rwlock);

	list = frame_types;

	while (list) {

		info = list->data;

		if (strcmp (info->header_id, str) == 0) {
			type = info->type;
			break;
		}
		list = list->next;
	}

	g_static_rw_lock_reader_unlock (&rwlock);

	return type;
}

void
rr_framefactory_register_type (const gchar *header_id, GType type)
{
	FrameTypeInfo *info;

	info = g_new (FrameTypeInfo, 1);
	g_return_if_fail (info != NULL);

	info->header_id = header_id;
	info->type      = type;

	g_static_rw_lock_writer_lock (&rwlock);
	frame_types = g_slist_append (frame_types, info);
	g_static_rw_lock_writer_unlock (&rwlock);
}

static const gchar *
find_body (const gchar *str, gint len)
{
	const gchar *end = str + len;

	while (str < end) {
		if (*str == '\n')
			return str + 1;
		if (*str == 0)
			return NULL;
		str++;
	}
	return 0;
}

gint
rr_framefactory_parse_frame (RRConnection *conn, const gchar *buffer, 
			     gint len, RRFrame **frame, GError **error)
{
	GType type;
	const gchar *body;
	gint header_len;
	gint bytes;

	g_return_val_if_fail (RR_IS_CONNECTION (conn), 0);
	g_return_val_if_fail (buffer != 0, 0);
	g_return_val_if_fail (len > 0, 0);
	g_return_val_if_fail (frame != NULL, 0);

	*frame = NULL;
	body = find_body (buffer, len);

	/* Not enough data for a complete header */
	if (body == NULL)
		return 0;

	header_len = body - buffer;

	type = get_frame_type (buffer);

	if (type == G_TYPE_INVALID) {
		g_set_error (error, RR_BEEP_ERROR, RR_BEEP_CODE_SYNTAX_ERROR,
			     "Frame header parse error");
		return -1;
	}

	g_return_val_if_fail (type != G_TYPE_INVALID, 0);
	
	*frame = g_object_new (type, NULL);

	g_return_val_if_fail (RR_IS_FRAME (*frame), 0);

	bytes = rr_frame_parse (*frame, buffer, body, len, error);

	if (bytes <= 0) {
		g_object_unref (G_OBJECT (*frame));
		*frame = NULL;
		return bytes;
	}
	return bytes;
}



syntax highlighted by Code2HTML, v. 0.9.1