/*
 * $Id: vs_node_object.c 4533 2008-08-11 15:00:51Z jiri $ 
 *
 * ***** BEGIN BSD LICENSE BLOCK *****
 *
 * Copyright (c) 2005-2008, The Uni-Verse Consortium.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END BSD LICENSE BLOCK *****
 *
 */

#include <stdio.h>
#include <stdlib.h>

#include "v_cmd_gen.h"

#if !defined V_GENERATE_FUNC_MODE

#include "verse.h"
#include "v_util.h"
#include "vs_server.h"

extern void verse_send_o_link_set(VNodeID node_id, uint16 link_id, VNodeID link, const char *name, uint32 target_id);
extern void verse_send_o_link_destroy(VNodeID node_id, uint16 link_id);

typedef struct {
	VNodeID link;
	char	name[16];
	uint32	target_id;
	/* Animation parameters. */
	uint32	time_s;
	uint32	time_f;
	uint32	dimensions;
	real64	pos[4];
	real64	speed[4];
	real64	accel[4];
	real64	scale[4];
	real64	scale_speed[4];
} VSLink;

typedef struct {
	char		name[16];
	uint8		param_count;
	VNOParamType *param_types;
	char		*param_names;
} VSMethod;

typedef struct {
	char		name[VN_O_METHOD_GROUP_NAME_SIZE];
	VSMethod	*methods;
	unsigned int	method_count;
	VSSubscriptionList *subscribers;
} VSMethodGroup;

typedef struct {
	real64	position[3];
	VNQuat64 rotation;
	real64	scale[3];
/*	VSSubscriptionList *subscribers;*/
} VSTransform;

typedef struct {
	VSNodeHead			head;
	VSTransform			transform;
	VSSubscriptionList	*trans_sub64;
	VSSubscriptionList	*trans_sub32;
	real64				light[3];
	VSMethodGroup		*groups;
	uint16				group_count;
	VSLink				*links;
	uint16				link_count;
	boolean				hidden;
} VSNodeObject;

VSNodeObject * vs_o_create_node(unsigned int owner)
{
	VSNodeObject *node;
	unsigned int i, j;
	char name[48];
	node = malloc(sizeof *node);
	vs_add_new_node(&node->head, V_NT_OBJECT);
	sprintf(name, "Object_Node_%u", node->head.id);
	create_node_head(&node->head, name, owner);
	node->trans_sub64 = vs_create_subscription_list();
	node->trans_sub32 = vs_create_subscription_list();
	node->transform.position[0] = node->transform.position[1] = node->transform.position[2] = 0;
	node->transform.rotation.x = node->transform.rotation.y = node->transform.rotation.z = 0.0;
	node->transform.rotation.w = 1.0;
	node->transform.scale[0] = node->transform.scale[1] = node->transform.scale[2] = 1.0;
	node->light[0] = node->light[1] = node->light[2] = V_REAL64_MAX;
	node->groups = malloc((sizeof *node->groups) * 16);
	node->group_count = 16;
	for(i = 0; i < 16; i++)
	{
		node->groups[i].name[0] = 0;
		node->groups[i].methods = NULL;
		node->groups[i].method_count = 0;
		node->groups[i].subscribers = NULL;
	}

	node->link_count = 16;
	node->links = malloc((sizeof *node->links) * node->link_count);
	for(i = 0; i < node->link_count; i++)
	{
		node->links[i].link = -1;
		node->links[i].name[0] = 0;
		node->links[i].target_id = -1;
		node->links[i].dimensions = 0;
		for(j = 0; j < 4; j++)
		{
			node->links[i].pos[j] = 0.0;
			node->links[i].speed[j] = 0.0;
			node->links[i].accel[j] = 0.0;
			node->links[i].scale[j] = 0.0;
			node->links[i].scale_speed[j] = 0.0;
		}
	}
	node->hidden = FALSE;
	return node;
}

void vs_o_destroy_node(VSNodeObject *node)
{
	unsigned int i, j;
	destroy_node_head(&node->head);
	for(i = 0; i < node->group_count; i++)
	{
		if(node->groups[i].name[0] != 0)
		{
			for(j = 0; j < node->groups[i].method_count; j++)
			{
				if(node->groups[i].methods[j].name[0] != 0 && node->groups[i].methods[j].param_count != 0)
				{
					free(node->groups[i].methods[j].param_types);
					free(node->groups[i].methods[j].param_names);
				}
			}
			if(node->groups[i].methods != NULL)
				free(node->groups[i].methods);
		}
	}
	free(node->groups);
	free(node);
}

void vs_o_subscribe(VSNodeObject *node)
{
	unsigned int i;
	for(i = 0; i < node->link_count; i++)
	{
		const VSLink	*lnk = node->links + i;

		if(lnk->name[0] != 0)
		{
			verse_send_o_link_set(node->head.id, i, lnk->link, lnk->name, lnk->target_id);
			if(lnk->dimensions != 0)
			{
				verse_send_o_anim_run(node->head.id, i, lnk->time_s, lnk->time_f, lnk->dimensions,
						      lnk->pos, lnk->speed, lnk->accel,
						      lnk->scale, lnk->scale_speed);
			}
		}
	}
	if(node->light[0] != V_REAL64_MAX || node->light[1] != V_REAL64_MAX || node->light[2] != V_REAL64_MAX)
		verse_send_o_light_set(node->head.id, node->light[0], node->light[1], node->light[2]);
	for(i = 0; i < node->group_count; i++)
	{
		if(node->groups[i].name[0] != 0)
			verse_send_o_method_group_create(node->head.id, i, node->groups[i].name);
	}
	if(node->hidden)
		verse_send_o_hide(node->head.id, TRUE);
}

void vs_o_unsubscribe(VSNodeObject *node)
{
	unsigned int i;
	for(i = 0; i < node->group_count; i++)
		if(node->groups[i].name[0] != 0)
			vs_remove_subscriptor(node->groups[i].subscribers);
	vs_remove_subscriptor(node->trans_sub64);
	vs_remove_subscriptor(node->trans_sub32);
}

static void callback_send_o_transform_pos_real32(void *user, VNodeID node_id, uint32 time_s, uint32 time_f, real32 *pos, real32 *speed, real32 *accelerate, real32 *drag_normal, real32 drag)
{
	VSNodeObject *node;
	unsigned int i, count;

	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL)
		return;
	node->transform.position[0] = pos[0];
	node->transform.position[1] = pos[1];
	node->transform.position[2] = pos[2];

	if((count = vs_get_subscript_count(node->trans_sub64)) > 0)	/* Anyone listening at 64 bits? */
	{
		real64	spd[3], acc[3], drn[3], *pspd = NULL, *pacc = NULL, *pdrn = NULL;

		pspd = (speed != NULL) ? spd : NULL;
		pacc = (accelerate != NULL) ? acc : NULL;
		pdrn = (drag_normal != NULL) ? drn : NULL;
		/* Convert non-position values to 64-bit. */
		for(i = 0; i < 3; i++)
		{
			if(speed != NULL)
				spd[i] = speed[i];
			if(accelerate != NULL)
				acc[i] = accelerate[i];
			if(drag_normal != NULL)
				drn[i] = drag_normal[i];
		}
		for(i = 0; i < count; i++)
		{
			vs_set_subscript_session(node->trans_sub64, i);
			verse_send_o_transform_pos_real64(node_id, time_s, time_f, node->transform.position, pspd, pacc, pdrn, drag);
		}
	}
	count =	vs_get_subscript_count(node->trans_sub32);
	for(i = 0; i < count; i++)
	{

		vs_set_subscript_session(node->trans_sub32, i);
		verse_send_o_transform_pos_real32(node_id, time_s, time_f, pos, speed, accelerate, drag_normal, drag);
	}
	vs_reset_subscript_session();
}

static void callback_send_o_transform_rot_real32(void *user, VNodeID node_id, uint32 time_s, uint32 time_f, const VNQuat32 *rot,
						 const VNQuat32 *speed, const VNQuat32 *accelerate, const VNQuat32 *drag_normal, real32 drag)
{
	VSNodeObject *node;
	unsigned int i, count;
	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL)
		return;
	node->transform.rotation.x = rot->x;
	node->transform.rotation.y = rot->y;
	node->transform.rotation.z = rot->z;
	node->transform.rotation.w = rot->w;
	if((count = vs_get_subscript_count(node->trans_sub64)) > 0)
	{
		VNQuat64	spd, acc, drn, *p[3];

		/* Convert 32-bit quaternions to 64-bit. Converter handles NULLs, has nice return semantics. */
		p[0] = v_quat64_from_quat32(&spd, speed);
		p[1] = v_quat64_from_quat32(&acc, accelerate);
		p[2] = v_quat64_from_quat32(&drn, drag_normal);
		for(i = 0; i < count; i++)
		{
			vs_set_subscript_session(node->trans_sub64, i);
			verse_send_o_transform_rot_real64(node_id, time_s, time_f, &node->transform.rotation, p[0], p[1], p[2], drag);
		}
	}
	count =	vs_get_subscript_count(node->trans_sub32);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->trans_sub32, i);
		verse_send_o_transform_rot_real32(node_id, time_s, time_f, rot, speed, accelerate, drag_normal, drag);
	}
	vs_reset_subscript_session();
}


static void callback_send_o_transform_scale_real32(void *user, VNodeID node_id, real32 scale_x, real32 scale_y, real32 scale_z)
{
	VSNodeObject *node;
	unsigned int i, count;
	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL)
		return;
	node->transform.scale[0] = scale_x;
	node->transform.scale[1] = scale_y;
	node->transform.scale[2] = scale_z;
	count =	vs_get_subscript_count(node->trans_sub64);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->trans_sub64, i);
		verse_send_o_transform_scale_real64(node_id, scale_x, scale_y, scale_z);
	}
	count =	vs_get_subscript_count(node->trans_sub32);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->trans_sub32, i);
		verse_send_o_transform_scale_real32(node_id, scale_x, scale_y, scale_z);
	}
	vs_reset_subscript_session();
}

static void callback_send_o_transform_pos_real64(void *user, VNodeID node_id, uint32 time_s, uint32 time_f, const real64 *pos,
						 const real64 *speed, const real64 *accelerate, const real64 *drag_normal, real64 drag)
{
	VSNodeObject *node;
	unsigned int i, count;

	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL)
		return;
	node->transform.position[0] = pos[0];
	node->transform.position[1] = pos[1];
	node->transform.position[2] = pos[2];
	count =	vs_get_subscript_count(node->trans_sub64);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->trans_sub64, i);
		verse_send_o_transform_pos_real64(node_id, time_s, time_f, node->transform.position, speed, accelerate, drag_normal, drag);
	}
	if((count = vs_get_subscript_count(node->trans_sub32)) > 0)	/* Anyone listening at 32 bits? */
	{
		real32	ps[3], spd[3], acc[3], drn[3], *p[] = { NULL, NULL, NULL };

		ps[0] = pos[0];
		ps[1] = pos[1];
		ps[2] = pos[2];
		if(speed != NULL)
		{
			p[0] = spd;
			spd[0] = speed[0];
			spd[1] = speed[1];
			spd[2] = speed[2];
		}
		else
			p[0] = NULL;
		if(accelerate != NULL)
		{
			p[1] = acc;
			acc[0] = accelerate[0];
			acc[1] = accelerate[1];
			acc[2] = accelerate[2];
		}
		else
			p[1] = NULL;
		if(drag_normal != NULL)
		{
			p[1] = drn;
			drn[0] = drag_normal[0];
			drn[1] = drag_normal[1];
			drn[2] = drag_normal[2];
		}
		else
			p[2] = NULL;

		for(i = 0; i < count; i++)
		{
			vs_set_subscript_session(node->trans_sub32, i);
			verse_send_o_transform_pos_real32(node_id, time_s, time_f, ps, p[0], p[1], p[2], (real32) drag);
		}
	}
	vs_reset_subscript_session();
}

static void callback_send_o_transform_rot_real64(void *user, VNodeID node_id, uint32 time_s, uint32 time_f, const VNQuat64 *rot,
						 const VNQuat64 *speed, const VNQuat64 *accelerate, const VNQuat64 *drag_normal, real64 drag)
{
	VSNodeObject *node;
	unsigned int i, count;

	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL)
		return;
	node->transform.rotation = *rot;
	count =	vs_get_subscript_count(node->trans_sub64);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->trans_sub64, i);
		verse_send_o_transform_rot_real64(node_id, time_s, time_f, &node->transform.rotation, speed, accelerate, drag_normal, drag);
	}
	if((count = vs_get_subscript_count(node->trans_sub32)) > 0)	/* Anyone listening at 32 bits? */
	{
		VNQuat32	rt, spd, acc, drn, *p[3];

		v_quat32_from_quat64(&rt, rot);
		p[0] = v_quat32_from_quat64(&spd, speed);
		p[1] = v_quat32_from_quat64(&acc, accelerate);
		p[2] = v_quat32_from_quat64(&drn, drag_normal);
		for(i = 0; i < count; i++)
		{
			vs_set_subscript_session(node->trans_sub32, i);
			verse_send_o_transform_rot_real32(node_id, time_s, time_f, &rt, p[0], p[1], p[2], (real32) drag);
		}
	}
	vs_reset_subscript_session();
}

static void callback_send_o_transform_scale_real64(void *user, VNodeID node_id, real64 scale_x, real64 scale_y, real64 scale_z)
{
	VSNodeObject *node;
	unsigned int i, count;
	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL)
		return;
	node->transform.scale[0] = scale_x;
	node->transform.scale[1] = scale_y;
	node->transform.scale[2] = scale_z;
	count =	vs_get_subscript_count(node->trans_sub64);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->trans_sub64, i);
		verse_send_o_transform_scale_real64(node_id, scale_x, scale_y, scale_z);
	}
	count =	vs_get_subscript_count(node->trans_sub32);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->trans_sub32, i);
		verse_send_o_transform_scale_real32(node_id, (real32) scale_x, (real32) scale_y, (real32) scale_z);
	}
	vs_reset_subscript_session();
}

static void callback_send_o_transform_subscribe(void *user, VNodeID node_id, VNRealFormat type)
{
	VSNodeObject *node;
	uint32 time_s, time_f;

	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL)
		return;
	verse_session_get_time(&time_s, &time_f);
	if(type == VN_FORMAT_REAL32)
	{
		real32	tpos[3];
		VNQuat32 rot;

		vs_add_new_subscriptor(node->trans_sub32);
		tpos[0] = node->transform.position[0];
		tpos[1] = node->transform.position[1];
		tpos[2] = node->transform.position[2];
		verse_send_o_transform_pos_real32(node_id, time_s, time_f, tpos, NULL, NULL, NULL, 0.0f);
		v_quat32_from_quat64(&rot, &node->transform.rotation);
		verse_send_o_transform_rot_real32(node_id, time_s, time_f, &rot, NULL, NULL, NULL, 0.0f);
		verse_send_o_transform_scale_real32(node_id, (real32) node->transform.scale[0], (real32) node->transform.scale[1], (real32) node->transform.scale[2]);
	}
	else
	{
		vs_add_new_subscriptor(node->trans_sub64);
		verse_send_o_transform_pos_real64(node_id, time_s, time_f, node->transform.position, NULL, NULL, NULL, 0);
		verse_send_o_transform_rot_real64(node_id, time_s, time_f, &node->transform.rotation, NULL, NULL, NULL, 0);
		verse_send_o_transform_scale_real64(node_id, node->transform.scale[0], node->transform.scale[1], node->transform.scale[2]);
	}
}

static void callback_send_o_transform_unsubscribe(void *user, VNodeID node_id, VNRealFormat type)
{
	VSNodeObject *node;
	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL)
		return;
	if(type == VN_FORMAT_REAL32)
		vs_remove_subscriptor(node->trans_sub32);
	else
		vs_remove_subscriptor(node->trans_sub64);
}

static void callback_send_o_light_set(void *user, VNodeID node_id, real64 light_r, real64 light_g, real64 light_b)
{
	VSNodeObject *node;
	unsigned int i, count;
	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL)
		return;
	node->light[0] = light_r;
	node->light[1] = light_g;
	node->light[2] = light_b;
	count =	vs_get_subscript_count(node->head.subscribers);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->head.subscribers, i);
		verse_send_o_light_set(node_id, light_r, light_g, light_b);
	}
	vs_reset_subscript_session();
}

static void callback_send_o_link_set(void *user, VNodeID node_id, uint16 link_id, VNodeID link, const char *name, uint32 target_id)
{
	VSNodeObject *node;
	unsigned int i, count;
	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);

	if(node == NULL)
		return;

	if(name[0] == 0)
		return;
	if(vs_get_node_head(link) == 0)
		link = 0;

	if(link_id >= node->link_count || node->links[link_id].name[0] == 0)
	{
		for(link_id = 0; link_id < node->link_count && node->links[link_id].name[0] != 0; link_id++);
	
		if(link_id == node->link_count)
		{
			i = node->link_count;
			node->link_count += 16;
			node->links = realloc(node->links, (sizeof *node->links) * node->link_count);
			for(; i < node->link_count; i++)
			{
				node->links[i].name[0] = 0;
				node->links[i].dimensions = 0;
			}
		}
	}

	node->links[link_id].link = link;
	for(i = 0; i < 15 && name[i] != 0; i++)
		node->links[link_id].name[i] = name[i];
	node->links[link_id].name[i] = 0;
	node->links[link_id].target_id = target_id;

	count =	vs_get_subscript_count(node->head.subscribers);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->head.subscribers, i);
		verse_send_o_link_set(node_id, link_id, link, name, target_id);
	}
	vs_reset_subscript_session();
}

static void callback_send_o_link_destroy(void *user, VNodeID node_id, uint16 link_id)
{
	VSNodeObject *node;
	unsigned int i, count;
	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL)
		return;

	if(link_id >= node->link_count || node->links[link_id].name[0] == 0)
		return;

	node->links[link_id].name[0] = 0;

	count =	vs_get_subscript_count(node->head.subscribers);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->head.subscribers, i);
		verse_send_o_link_destroy(node_id, link_id);
	}
	vs_reset_subscript_session();
}

static void callback_send_o_method_group_create(void *user, VNodeID node_id, uint16 group_id, char *name)
{
	VSNodeObject *node;
	unsigned int i, j, count;

	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL || vs_get_node(node_id, V_NT_OBJECT) == NULL)
		return;

	for(i = 0; i < node->group_count; i++)
	{
		for(j = 0; node->groups[i].name[j] == name[j] && node->groups[i].name[j] != 0; j++);
		if(node->groups[i].name[j] == name[j])
			return;
	}
	if(group_id >= node->group_count || node->groups[group_id].name[0] == 0)
	{
		for(group_id = 0; group_id < node->group_count && node->groups[group_id].name[0] != 0; group_id++)
		if(group_id == node->group_count)
		{
			node->groups = realloc(node->groups, sizeof(*node->groups) * (node->group_count + 16));
			for(i = node->group_count; i < node->group_count + 16u; i++)
			{
				node->groups[i].name[0] = 0;
				node->groups[i].methods = NULL;
				node->groups[i].method_count = 0;
			}
			node->group_count += 16;
		}
		node->groups[group_id].subscribers = vs_create_subscription_list();
	}
	for(i = 0; i < 15 && name[i] != 0; i++)
		node->groups[group_id].name[i] = name[i];
	node->groups[group_id].name[i] = 0;
	count =	vs_get_subscript_count(node->head.subscribers);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->head.subscribers, i);
		verse_send_o_method_group_create(node_id, group_id, name);
	}
	vs_reset_subscript_session();
}

static void callback_send_o_method_group_destroy(void *user, VNodeID node_id, uint16 group_id)
{
	VSNodeObject *node;
	unsigned int i, count;
	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL || vs_get_node(node_id, V_NT_OBJECT) == NULL)
		return;

	if(group_id >= node->group_count || node->groups[group_id].name[0] == 0)
		return;
	node->groups[group_id].name[0] = 0;
	for(i = 0; i < node->groups[group_id].method_count; i++)
	{
		if(node->groups[group_id].methods[i].name[0] != 0 && node->groups[group_id].methods[i].param_count > 0)
		{
			free(node->groups[group_id].methods[i].param_names);
			free(node->groups[group_id].methods[i].param_types);
		}
	}
	free(node->groups[group_id].methods);
	node->groups[group_id].methods = NULL;
	node->groups[group_id].method_count = 0;
	vs_destroy_subscription_list(node->groups[group_id].subscribers);
	count =	vs_get_subscript_count(node->head.subscribers);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->head.subscribers, i);
		verse_send_o_method_group_destroy(node_id, group_id);
	}
	vs_reset_subscript_session();
}

static void callback_send_o_method_group_subscribe(void *user, VNodeID node_id, uint16 group_id)
{
	VSNodeObject *node;
	unsigned int i, j;

	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL || vs_get_node(node_id, V_NT_OBJECT) == NULL)
		return;
	if(group_id < node->group_count && node->groups[group_id].name[0] != 0)
		vs_add_new_subscriptor(node->groups[group_id].subscribers);
	for(i = 0; i < node->groups[group_id].method_count; i++)
	{
		if(node->groups[group_id].methods[i].name[0] != 0)
		{
			char *names[255];
			for(j = 0; j < node->groups[group_id].methods[i].param_count; j++)
				names[j] = &node->groups[group_id].methods[i].param_names[j * 16];
			verse_send_o_method_create(node_id, group_id, i, node->groups[group_id].methods[i].name, node->groups[group_id].methods[i].param_count, node->groups[group_id].methods[i].param_types, (const char **) names);
		}
	}
}

static void callback_send_o_method_group_unsubscribe(void *user, VNodeID node_id, uint16 group_id)
{
	VSNodeObject *node;
	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL || vs_get_node(node_id, V_NT_OBJECT) == NULL)
		return;
	if(group_id < node->group_count && node->groups[group_id].name[0] != 0)
		vs_remove_subscriptor(node->groups[group_id].subscribers);
}

static void callback_send_o_method_create(void *user, VNodeID node_id, uint16 group_id, uint16 method_id, char *name, uint8 param_count, VNOParamType *param_types, char * *param_names)
{
	VSNodeObject *node;
	unsigned int i, j, count;
	VSMethodGroup *group;

	node = (VSNodeObject *) vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL || vs_get_node(node_id, V_NT_OBJECT) == NULL)
		return;
	if(group_id >= node->group_count || node->groups[group_id].name[0] == 0)
		return;
	group = &node->groups[group_id];
	for(i = 0; i < group->method_count; i++)
	{
		if(i != method_id)
		{
			for(j = 0; group->methods[i].name[j] == name[j] && group->methods[i].name[j] != 0; j++);
			if(group->methods[i].name[j] == name[j])
				return;
		}
	}
	if(method_id < group->method_count && group->methods[method_id].name[0] != 0)
	{
		for(i = 0; i < 16; i++)
			group->methods[method_id].name[i] = name[i];
		if(group->methods[method_id].param_count != 0)
		{
			free(group->methods[method_id].param_names);
			free(group->methods[method_id].param_types);
		}
	}else
	{
		for(method_id = 0; method_id < group->method_count && group->methods[method_id].name[0] != 0; method_id++);
		if(method_id == group->method_count)
		{
			group->methods = realloc(group->methods, sizeof(*group->methods) * (group->method_count + 16));
			for(i = group->method_count; i < group->method_count + 16; i++)
				group->methods[i].name[0] = 0;
			group->method_count += 16;
		}
	}
	for(i = 0; i < VN_O_METHOD_NAME_SIZE && name[i] != 0; i++)
		group->methods[method_id].name[i] = name[i];
	group->methods[method_id].name[i] = '\0';
	group->methods[method_id].param_count = param_count; 
	if(param_count > 0)
	{
		group->methods[method_id].param_types = malloc((sizeof *group->methods[method_id].param_types) * param_count);
		group->methods[method_id].param_names = malloc((sizeof *group->methods[method_id].param_names) * param_count * 16);
	}
	for(i = 0; i < param_count; i++)
	{
		group->methods[method_id].param_types[i] = param_types[i];
		for(j = 0; j < 15 && param_names[i][j] != 0; j++)
			group->methods[method_id].param_names[i * 16 + j] = param_names[i][j];
		group->methods[method_id].param_names[i * 16 + j] = 0;
	}
	count =	vs_get_subscript_count(node->groups[group_id].subscribers);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->groups[group_id].subscribers, i);
		verse_send_o_method_create(node_id, group_id, method_id, name, param_count, param_types, (const char **) param_names);
	}
	vs_reset_subscript_session();
}

static void callback_send_o_method_destroy(void *user, VNodeID node_id, uint16 group_id, uint16 method_id)
{
	VSNodeObject *node;
	unsigned int i, count;
	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL || vs_get_node(node_id, V_NT_OBJECT) == NULL)
		return;
	if(group_id >= node->group_count || node->groups[group_id].name[0] == 0 || method_id >= node->groups[group_id].method_count || node->groups[group_id].methods[method_id].name[0] == 0)
		return;

	node->groups[group_id].methods[method_id].name[0] = 0;
	if(node->groups[group_id].methods[method_id].param_count != 0)
	{
		free(node->groups[group_id].methods[method_id].param_names);
		free(node->groups[group_id].methods[method_id].param_types);
	}
	count =	vs_get_subscript_count(node->groups[group_id].subscribers);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->groups[group_id].subscribers, i);
		verse_send_o_method_destroy(node_id, group_id, method_id);
	}
	vs_reset_subscript_session();
}

static void callback_send_o_method_call(void *user, VNodeID node_id, uint16 group_id, uint16 method_id, VNodeID sender, void *params)
{
	VNOParam unpacked_params[255];
	void *data;
	VSNodeObject *node;
	unsigned int i, count;

	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL || vs_get_node(node_id, V_NT_OBJECT) == NULL)
		return;

	if(group_id >= node->group_count || node->groups[group_id].name[0] == 0 || method_id >= node->groups[group_id].method_count || node->groups[group_id].methods[method_id].name[0] == 0)
		return;
	if(!verse_method_call_unpack(params, node->groups[group_id].methods[method_id].param_count, node->groups[group_id].methods[method_id].param_types, unpacked_params))
		return;
	sender = vs_get_avatar();
	count =	vs_get_subscript_count(node->groups[group_id].subscribers);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->groups[group_id].subscribers, i);
		data = verse_method_call_pack(node->groups[group_id].methods[method_id].param_count, node->groups[group_id].methods[method_id].param_types, unpacked_params);
		if(data != NULL)
			verse_send_o_method_call(node_id, group_id, method_id, sender, data);
	}
	vs_reset_subscript_session();
}

static void callback_send_o_anim_run(void *user, VNodeID node_id, uint16 link_id, uint32 time_s, uint32 time_f, uint8 dimensions, real64 *pos,
				     real64 *speed, real64 *accel, real64 *scale, real64 *scale_speed)
{
	VSNodeObject *node;
	unsigned int i, count;

	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL)
	        return;
	if(link_id >= node->link_count || node->links[link_id].name[0] == 0)
		return;
	if(NULL == vs_get_node(node->links[link_id].link, V_NT_CURVE))
		return;
	node->links[link_id].time_s = time_s;
	node->links[link_id].time_f = time_f;
	node->links[link_id].dimensions = dimensions;
	for(i = 0; i < dimensions && i < 4; i++)
	{
		node->links[link_id].pos[i] = pos[i];
		node->links[link_id].speed[i] = speed[i];
		node->links[link_id].accel[i] = accel[i];
		node->links[link_id].scale[i] = scale[i];
		node->links[link_id].scale_speed[i] = scale_speed[i];
	}
	count = vs_get_subscript_count(node->head.subscribers);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->head.subscribers, i);
		verse_send_o_anim_run(node_id, link_id, time_s, time_f, dimensions, pos, speed, accel, scale, scale_speed);
	}
	vs_reset_subscript_session();
}

static void callback_send_o_hide(void *user, VNodeID node_id, uint8 hidden)
{
	VSNodeObject *node;
	unsigned int i, count;

	node = (VSNodeObject *)vs_get_node(node_id, V_NT_OBJECT);
	if(node == NULL || hidden == node->hidden)
		return;
	node->hidden = hidden;
	count = vs_get_subscript_count(node->head.subscribers);
	for(i = 0; i < count; i++)
	{
		vs_set_subscript_session(node->head.subscribers, i);
		verse_send_o_hide(node_id, hidden);
	}
	vs_reset_subscript_session();
}

void vs_o_callback_init(void)
{

	verse_callback_set(verse_send_o_transform_pos_real32, callback_send_o_transform_pos_real32, NULL);
	verse_callback_set(verse_send_o_transform_rot_real32, callback_send_o_transform_rot_real32, NULL);
	verse_callback_set(verse_send_o_transform_scale_real32, callback_send_o_transform_scale_real32, NULL);
	verse_callback_set(verse_send_o_transform_pos_real64, callback_send_o_transform_pos_real64, NULL);
	verse_callback_set(verse_send_o_transform_rot_real64, callback_send_o_transform_rot_real64, NULL);
	verse_callback_set(verse_send_o_transform_scale_real64, callback_send_o_transform_scale_real64, NULL);
	verse_callback_set(verse_send_o_transform_subscribe, callback_send_o_transform_subscribe, NULL);
	verse_callback_set(verse_send_o_transform_unsubscribe, callback_send_o_transform_unsubscribe, NULL);
	verse_callback_set(verse_send_o_link_set, callback_send_o_link_set, NULL);
	verse_callback_set(verse_send_o_light_set, callback_send_o_light_set, NULL);
	verse_callback_set(verse_send_o_link_set, callback_send_o_link_set, NULL);
	verse_callback_set(verse_send_o_link_destroy, callback_send_o_link_destroy, NULL);
	verse_callback_set(verse_send_o_method_group_create, callback_send_o_method_group_create, NULL);
	verse_callback_set(verse_send_o_method_group_destroy, callback_send_o_method_group_destroy, NULL);
	verse_callback_set(verse_send_o_method_group_subscribe, callback_send_o_method_group_subscribe, NULL);
	verse_callback_set(verse_send_o_method_group_unsubscribe, callback_send_o_method_group_unsubscribe, NULL);
	verse_callback_set(verse_send_o_method_create, callback_send_o_method_create, NULL);
	verse_callback_set(verse_send_o_method_destroy, callback_send_o_method_destroy, NULL);
	verse_callback_set(verse_send_o_method_call, callback_send_o_method_call, NULL);
	verse_callback_set(verse_send_o_anim_run, callback_send_o_anim_run, NULL);
	verse_callback_set(verse_send_o_hide, callback_send_o_hide, NULL);
}

#endif