/**********************************************************************
 
	Copyright (C) 2003-2004
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomoki SEKIYAMA <sekiyama@yahoo.co.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/



#include <stdlib.h>
#include <string.h>

#include "v/VObject.h"
extern "C" {
#include "lock_level.h"
#include "utils.h"
#include "memory_debug.h"
int get_tid();
}

#define LC_WHITE_SPACE_H ' '
#define LC_WHITE_SPACE_Z 0xc0012121

void v_type_name_init();
char* v_type_name(int type);
void v_object_hash_init();
VObject* v_object_hash_search(unsigned int id);
void v_object_hash_add(VObject* obj, unsigned int id);
void v_object_hash_remove(VObject* obj, unsigned int id);

int VObject::next_id = 1000;
VObjectList * VObject::v_zonbie_list = 0;
SEM VObject::v_zonbie_lock;
	
extern "C" VObjectList *
vobject_list_remove(VObjectList *target, VObjectList *prev, VObjectList **top)
{
	VObjectList *next = target->next;
	if ( prev )
		prev->next = next;
	if ( target == *top )
		*top = next;
	delete target;
	return next;
}

extern "C" void
convert_to_nmc_list(VObjectList **l)
{
	VObjectList *list = *l, *ret = list, *prev = 0, *next;
	VObject *nmc;
	for ( ; list ; prev = list, list = next ) {
		next = list->next;
		nmc = list->object->get_nmc(0);
		if ( nmc != list->object ) {
			if ( nmc )
				list->object = nmc;
			else {
				if ( prev )
					prev->next = next;
				else
					ret = next;
				delete list;
				list = prev;
			}
		}
	}
	*l = ret;
}

extern "C" void
free_vobject_status(VObjectStatus *s)
{
	VObjectList *list, *next;
	for ( list = s->children ; list ; list = next ) {
		next = list->next;
		delete list;
	}
}

#define COPY_VOBJ_STS(__flags,__exp)	\
	if ( flags & __flags ) {	\
		__exp;			\
		flags &= ~__flags;	\
	}

extern "C" void
copy_vobject_status(
	VObjectStatus * to,
	const VObjectStatus * from,
	int flags)
{
	COPY_VOBJ_STS(VSF_PARENT,
		to->parent = from->parent)
	COPY_VOBJ_STS(VSF_CHILDREN,
		to->children = 0)
	COPY_VOBJ_STS(VSF_ID,
		to->id = from->id)
	COPY_VOBJ_STS(VSF_ATTR,
		to->attr = from->attr)
	COPY_VOBJ_STS(VSF_VALUE,
		to->value = from->value)
	COPY_VOBJ_STS(VSF_WS,
		to->ws = from->ws)
	COPY_VOBJ_STS(VSF_FSIZE,
		to->fsize = from->fsize)
	COPY_VOBJ_STS(VSF_DESC,
		to->descriptor = ll_copy_str((L_CHAR*)from->descriptor))
	COPY_VOBJ_STS(VSF_VERTD,
		to->vert_desc = from->vert_desc)
	COPY_VOBJ_STS(VSF_VISIBLE,
		to->visible = from->visible)
	COPY_VOBJ_STS(VSF_ENABLED,
		to->enabled = from->enabled)
	COPY_VOBJ_STS(VSF_HOMOGEN,
		to->homogeneous = from->homogeneous)
	COPY_VOBJ_STS(VSF_SPACING,
		to->spacing = from->spacing)
	COPY_VOBJ_STS(VSF_ALIGN,
		(to->alignh = from->alignh,
		to->alignv = from->alignv))
	COPY_VOBJ_STS(VSF_PADDING,
		to->padding = from->padding)
	COPY_VOBJ_STS(VSF_CURSOR,
		er_panic("cursor"))
	COPY_VOBJ_STS(VSF_VALUE_EH,
		(to->value_event_handler = from->value_event_handler,
		to->value_eh_arg = from->value_eh_arg))
	COPY_VOBJ_STS(VSF_DESC_EH,
		(to->descriptor_event_handler = from->descriptor_event_handler,
		to->desc_eh_arg = from->desc_eh_arg))
	COPY_VOBJ_STS(VSF_DESTROY_H,
		(to->destroy_handler = from->destroy_handler,
		to->destroy_h_arg = from->destroy_h_arg))

	COPY_VOBJ_STS(VSF_MIN_SIZE,
		to->min_size =from->min_size)
	COPY_VOBJ_STS(VSF_SIZE,
		to->size = from->size)
	COPY_VOBJ_STS(VSF_POSITION,
		to->position = from->position)
}

extern "C" void
free_vobject_status_copy(VObjectStatus * sts)
{
	if ( sts->descriptor)
		d_f_ree((L_CHAR*)sts->descriptor);
}

extern "C" L_CHAR*
v_make_vert_desc(const L_CHAR* desc)
{
	if ( desc == 0 || desc[0] == 0 ) {
		L_CHAR *ret = (L_CHAR*)d_alloc(sizeof(L_CHAR));
		*ret = 0;
		set_buffer(ret);
		return ret;
	}
	
	int lines = 0, max_len = 0, i, j;
	const L_CHAR *t;
	i = 0;
	for ( t = desc ; *t ; t++ ) {
		i++;
		if ( *t == '\n' ) {
			if ( max_len < i )
				max_len = i;
			i = 0;
			lines++;
		}
	}
	if ( *(t-1) != '\n' ) {
		if ( max_len < i+1 )
			max_len = i+1;
		lines++;
	}
	
	lines *= 2;
	max_len--;
	
	L_CHAR *ret = (L_CHAR*)d_alloc((max_len*lines)*sizeof(L_CHAR));
	for ( j = 0 ; j < max_len ; j++ ) {
		for ( i = 1 ; i < lines-1 ; i += 2 )
			ret[i+j*lines] = LC_WHITE_SPACE_H;
		ret[(j+1)*lines-1] = '\n';
	}
	ret[j*lines-1] = 0;
	
	i = lines-2;
	j = 0;
	t = desc;
	while ( 1 ) {
		if ( *t == 0 || *t == '\n' ) {
			for ( ; j < max_len ; j++ )
				ret[i+j*lines] = LC_WHITE_SPACE_Z;
			if ( *t == 0 )
				break;
			j = 0;
			i -= 2;
		}
		else {
			ret[i+j*lines] = *t;
			j++;
		}
		t++;
	}
	set_buffer(ret);
	return ret;
}

extern "C" L_CHAR*
v_copy_desc_temp(const L_CHAR* desc)
{
	L_CHAR* ret = ll_copy_str(const_cast<L_CHAR*>(desc));
	set_buffer(ret);
	return ret;
}


// ========= v_type_name_hash =========

#define V_TYPE_NAME_HASH_SIZE	50
struct VTypeNameHash
{
	int 		type;
	char *		name;
	VTypeNameHash *	next;
} *v_type_name_hash[V_TYPE_NAME_HASH_SIZE];

void
v_type_name_init()
{
	for ( int key = 0 ; key < V_TYPE_NAME_HASH_SIZE ; key++ )
		v_type_name_hash[key] = 0;
}

char*
v_type_name(int type)
{
	int key = type % V_TYPE_NAME_HASH_SIZE;
	for ( VTypeNameHash* hash = v_type_name_hash[key] ; hash ; hash = hash->next )
		if ( hash->type == type )
			return hash->name;
	return 0;
}

void
v_type_name_add(int type, char *name)
{
	int key = type % V_TYPE_NAME_HASH_SIZE;
	VTypeNameHash* hash = (VTypeNameHash*)d_alloc(sizeof(VTypeNameHash));
	hash->type = type;
	hash->name = name;
	hash->next = v_type_name_hash[key];
	v_type_name_hash[key] = hash;
}


// ========== v_object_hash ===========

#define V_OBJECT_HASH_SIZE	100

struct VObjectHash
{
	unsigned int 	id;
	VObject *	object;
	VObjectHash *	next;
} *v_object_hash[V_OBJECT_HASH_SIZE];

SEM v_object_hash_lock;

void
v_object_hash_init()
{
	for ( int key = 0 ; key < V_OBJECT_HASH_SIZE ; key++ )
		v_object_hash[key] = 0;
	v_object_hash_lock = new_lock(LL_VOBJECT);
}

VObject*
v_object_hash_search(unsigned int id)
{
	VObject* ret = 0;
	int key = id % V_OBJECT_HASH_SIZE;
	lock_task(v_object_hash_lock);
	for ( VObjectHash* hash = v_object_hash[key] ; hash ; hash = hash->next )
		if ( hash->id == id )
			ret = hash->object;
	unlock_task(v_object_hash_lock, "v_object_hash_search");
	return ret;
}

void
v_object_hash_add(VObject* obj, unsigned int id)
{
	int key = id % V_OBJECT_HASH_SIZE;
	VObjectHash* hash = (VObjectHash*)d_alloc(sizeof(VObjectHash));
	hash->id = id;
	hash->object = obj;
	lock_task(v_object_hash_lock);
	hash->next = v_object_hash[key];
	v_object_hash[key] = hash;
	unlock_task(v_object_hash_lock, "v_object_hash_add");
}

void
v_object_hash_remove(VObject* obj, unsigned int id)
{
	int key = id % V_OBJECT_HASH_SIZE;
	lock_task(v_object_hash_lock);
	VObjectHash* prev = 0;
	for ( VObjectHash* hash = v_object_hash[key] ; hash ; hash = hash->next ) {
		if ( hash->id == id ) {
			if ( prev )
				prev->next = hash->next;
			else
				v_object_hash[key] = hash->next;
			d_f_ree(hash);
			break;
		}
		prev = hash;
	}
	unlock_task(v_object_hash_lock, "v_object_hash_add");
}


// ========== Error management utils ========

VExError
initial_VExError(enum VError code,int subcode1,int subcode2)
{
VExError ret;
	ret.code = code;
	ret.subcode1 = subcode1;
	ret.subcode2 = subcode2;
	return ret;
}

VExError
merge_VExError_vstatus_type(VExError er1,VExError er2)
{
	if ( er1.code == V_ER_NO_ERR && er2.code == V_ER_NO_ERR )
		return initial_VExError(V_ER_NO_ERR,er1.subcode1 & er2.subcode1,0);
	if ( er1.code != V_ER_NO_ERR )
		return initial_VExError(er1.code,er1.subcode1 & er2.subcode1,er1.subcode2);
	return initial_VExError(er2.code,er1.subcode1 & er2.subcode1,er2.subcode2);
}

// ========== vobject init ==========

void
VObject::init()
{
	v_zonbie_lock = new_lock(LL_VOBJECT);
	new_tick((void(*)(int))v_zonbie_tick,V_OBJECT_ZONBIE_INTERVAL,0);
	v_object_hash_init();
#ifdef USE_V_LAYOUT
extern SEM v_layout_lock;
	v_layout_lock = new_lock(LL_VOBJECT);
#endif
}

// ========== public accessor ==========

VError
VObject::destroy()
{
	VError err = V_ER_NO_ERR;
	VExError ex;
	VObjectStatus ps;
	VObjectList * objs, * op, * op2;
	FreeList * fl;
	ps.parent = sts.parent;
	objs = 0;
	op2 = 0;
	for ( ; ; ) {
		if ( ps.parent == 0 )
			break;
		err = ps.parent->lock_d(__FILE__,__LINE__);
		if ( err )
			return V_ER_NO_ERR;
		op = new VObjectList;
		op->object = ps.parent;
		op->next = objs;
		objs = op;
		if ( ps.parent->get_type() < VO_MACR ) {
			unlock(ps.parent);
			break;
		}
		unlock(ps.parent);
		ex = ps.parent->get_status(&ps,VSF_PARENT);
		switch ( ex.code ) {
		case 0:
			break;
		case V_ER_DESTROYED:
			err = V_ER_NO_ERR;
			goto error;
		default:
			err = ex.code;
			goto error;
		}
	}
	for ( op2 = objs ; op2  ; op2 = op2->next ) {
		err = op2->object->lock_d(__FILE__,__LINE__);
		if ( err ) {
			err = V_ER_NO_ERR;
			goto error;
		}
	}
	lock(this,__FILE__,__LINE__);
	if ( destroy_time != V_OBJECT_ALIVE ) {
		err = V_ER_NO_ERR;
		goto error;
 	}

	if ( sts.destroy_handler )
		(*sts.destroy_handler)(this,sts.destroy_h_arg,0);

	v_object_hash_remove(this, sts.id);

	if ( sts.children ) {
		ps.visible = false;
		set_status(&ps,VSF_VISIBLE);
		for ( ; sts.children ; )
			sts.children->object->destroy();
	}
	if ( sts.parent )
		sts.parent->remove_child(this);

	destroy_do(ps.parent);

	v_zonbie_insert();

	fl = free_list;
	for ( ; fl ; fl = fl->next ) {
		if ( free_list->free_func )
			(*free_list->free_func)(VFLT_ZONBIE,free_list->target);
	}

	unlock(this);
	destroy_do_out_of_lock(ps.parent);
	goto ok;
error:
	unlock(this);
ok:
	for ( ; objs != op2 ; ) {
		op = objs->next;
		unlock(objs->object);
		objs = op;
	}
	return err;
}

VExError
VObject::create_fin(const VObjectStatus* s, int flags,VObject * nmp, void* arg)
{
VExError err, err_s;
	set_parent(s->parent);	// 'create' do a pre-test, so this call never fails.
	err = create_do(s, flags,nmp , arg);
	if ( err.code )
		return err;
	lock(this,__FILE__,__LINE__);
	v_object_hash_add(this, sts.id);
	err_s = set_status(&v_default_sts, VSF_DEF_FLAGS & 
				(~flags) &
				(~err.subcode1) );
	if ( err_s.code != V_ER_NO_ERR )
		printf("init status error : %s %x %x %x\n",
			describe_self(), err_s.code, err_s.subcode1, err_s.subcode2);
	err = set_status(s, flags & (~err.subcode1));
	if ( err.code == V_ER_NO_ERR )
		err.subcode1 = 0;
	else
		printf("create error : %s %x %x %x\n",
			describe_self(), err.code, err.subcode1, err.subcode2);
	unlock(this);
	return err;
}

VObject::VObject()
{
	self_describe = 0;
	detached = 0;
	sts.id = next_id++;
	sts.parent = 0;
	sts.children = 0;
	sts.value_event_handler = 0;
	sts.descriptor_event_handler = 0;
	sts.destroy_handler = 0;
	sts.ws = 0;
	sts.fsize = V_DEFAULT_FSIZE;
	sts.descriptor = 0;
	destroy_time = V_OBJECT_ALIVE;
	free_list = 0;
	v_lock = new VSemaphore;
}

VObject::~VObject()
{
	while ( free_list ) {
		FreeList *next = free_list->next;
		if ( free_list->free_func )
			(*free_list->free_func)(VFLT_DELETE,free_list->target);
		else	d_f_ree(free_list->target);
		delete free_list;
		free_list = next;
	}
	delete v_lock;
printf("free %d\n", sts.id);
}

VExError
VObject::get_status(VObjectStatus *s, int flags) const
{
	V_OP_START_EX

	if ( flags & VSF_ID ) {
		s->id = sts.id;
		flags &= ~ VSF_ID;
	}
	if ( flags & VSF_ATTR ) {
		s->attr = sts.attr;
		flags &= ~ VSF_ATTR;
	}
	if ( flags & VSF_VALUE ) {
		s->value = sts.value;
		flags &= ~ VSF_VALUE;
	}
	if ( flags & VSF_WS ) {
		s->ws = sts.ws;
		flags &= ~ VSF_WS;
	}
	if ( flags & VSF_FSIZE ) {
		s->fsize = sts.fsize;
		flags &= ~ VSF_FSIZE;
	}
	if ( flags & VSF_DESC ) {
		s->descriptor = v_copy_desc_temp(sts.descriptor);
		flags &= ~ VSF_DESC;
	}
	if ( flags & VSF_VERTD ) {
		s->vert_desc = sts.vert_desc;
		flags &= ~ VSF_VERTD;
	}
	if ( flags & VSF_VISIBLE ) {
		s->visible = sts.visible;
		flags &= ~ VSF_VISIBLE;
	}
	if ( flags & VSF_ENABLED ) {
		s->enabled = sts.enabled;
		flags &= ~ VSF_ENABLED;
	}
	if ( flags & VSF_HOMOGEN ) {
		s->homogeneous = sts.homogeneous;
		flags &= ~ VSF_HOMOGEN;
	}
	if ( flags & VSF_SPACING ) {
		s->spacing = sts.spacing;
		flags &= ~ VSF_SPACING;
	}
	if ( flags & VSF_ALIGN ) {
		s->alignh = sts.alignh;
		s->alignv = sts.alignv;
		flags &= ~ VSF_ALIGN;
	}
	if ( flags & VSF_PADDING ) {
		s->padding = sts.padding;
		flags &= ~ VSF_PADDING;
	}
	if ( flags & VSF_CURSOR ) {
		s->cursor = sts.cursor;
		flags &= ~ VSF_CURSOR;
	}
	if ( flags & VSF_VALUE_EH ) {
		s->value_event_handler = sts.value_event_handler;
		s->value_eh_arg = sts.value_eh_arg;
		flags &= ~ VSF_VALUE_EH;
	}
	if ( flags & VSF_DESC_EH ) {
		s->descriptor_event_handler = sts.descriptor_event_handler;
		s->desc_eh_arg = sts.desc_eh_arg;
		flags &= ~ VSF_DESC_EH;
	}
	if ( flags & VSF_DESTROY_H ) {
		s->destroy_handler = sts.destroy_handler;
		s->destroy_h_arg = sts.destroy_h_arg;
		flags &= ~ VSF_DESTROY_H;
	}
	if ( flags & VSF_SIZE ) {
		s->size = sts.size;
		flags &= ~ VSF_SIZE;
	}
	if ( flags & VSF_MIN_SIZE ) {
		s->min_size = sts.min_size;
		flags &= ~ VSF_MIN_SIZE;
	}
	if ( flags & VSF_POSITION ) {
		s->position = sts.position;
		flags &= ~ VSF_POSITION;
	}
	s->children = 0;
	if ( flags & VSF_CHILDREN ) {
		VObjectList *list, *new_list, *ret;
		ret = new_list = 0;
		for ( list = sts.children ; list ; list = list->next ) {
			if ( new_list ) {
				new_list->next = new VObjectList;
				new_list = new_list->next;
			}
			else {
				ret = new_list = new VObjectList;
			}
			new_list->object = list->object;
		}
		if ( new_list )
			new_list->next = 0;
		s->children = ret;
		flags &= ~ VSF_CHILDREN;
	}
	if ( flags & VSF_PARENT ) {
		s->parent = sts.parent;
		s->window = sts.window;
		flags &= ~ VSF_PARENT;
	}
	
	V_OP_END
	return initial_VExError(V_ER_NO_ERR,flags,0);
};

VExError
VObject::set_status(const VObjectStatus *s, int flags)
{
	V_OP_START_EX
	VExError err = initial_VExError(V_ER_NO_ERR,flags,0);
	
	if ( flags & VSF_ID ) {
		err.code = V_ER_CANT_SET;
		err.subcode1 &= ~VSF_ID;
		err.subcode2 = VSF_ID;
		goto end;
	}
	if ( flags & VSF_ATTR ) {
		sts.attr = s->attr;
		err.subcode1 &= ~ VSF_ATTR;
	}
	if ( flags & VSF_VALUE ) {
		sts.value = s->value;
		err.subcode1 &= ~ VSF_VALUE;
	}
	if ( flags & VSF_WS ) {
		sts.ws = s->ws;
		err.subcode1 &= ~ VSF_WS;
	}
	if ( flags & VSF_FSIZE ) {
		sts.fsize = s->fsize;
		err.subcode1 &= ~ VSF_FSIZE;
	}
	if ( flags & VSF_DESC ) {
		if ( sts.descriptor )
			d_f_ree((void*)sts.descriptor);
		if ( s->descriptor )
			sts.descriptor = ll_copy_str(const_cast<L_CHAR*>(s->descriptor));
		else
			sts.descriptor = 0;
		err.subcode1 &= ~ VSF_DESC;
	}
	if ( flags & VSF_VERTD ) {
		sts.vert_desc = s->vert_desc;
		err.subcode1 &= ~ VSF_VERTD;
	}
	if ( flags & VSF_VISIBLE ) {
		sts.visible = s->visible;
		err.subcode1 &= ~ VSF_VISIBLE;
	}
	if ( flags & VSF_ENABLED ) {
		sts.enabled = s->enabled;
		err.subcode1 &= ~ VSF_ENABLED;
	}
	if ( flags & VSF_HOMOGEN ) {
		sts.homogeneous = s->homogeneous;
		err.subcode1 &= ~ VSF_HOMOGEN;
	}
	if ( flags & VSF_ALIGN ) {
		sts.alignh = s->alignh;
		sts.alignv = s->alignv;
		err.subcode1 &= ~ VSF_ALIGN;
	}
	if ( flags & VSF_SPACING ) {
		sts.spacing = s->spacing;
		err.subcode1 &= ~ VSF_SPACING;
	}
	if ( flags & VSF_PADDING ) {
		sts.padding = s->padding;
		err.subcode1 &= ~ VSF_PADDING;
	}
	if ( flags & VSF_CURSOR ) {
		sts.cursor = s->cursor;
		err.subcode1 &= ~ VSF_CURSOR;
	}
	if ( flags & VSF_VALUE_EH ) {
		sts.value_event_handler = s->value_event_handler;
		sts.value_eh_arg = s->value_eh_arg;
		err.subcode1 &= ~ VSF_VALUE_EH;
	}
	if ( flags & VSF_DESC_EH ) {
		sts.descriptor_event_handler = s->descriptor_event_handler;
		sts.desc_eh_arg = s->desc_eh_arg;
		err.subcode1 &= ~ VSF_DESC_EH;
	}
	if ( flags & VSF_DESTROY_H ) {
		sts.destroy_handler = s->destroy_handler;
		sts.destroy_h_arg = s->destroy_h_arg;
		err.subcode1 &= ~ VSF_DESTROY_H;
	}
	if ( flags & VSF_SIZE ) {
		sts.size = s->size;
		err.subcode1 &= ~ VSF_SIZE;
	}
	if ( flags & VSF_MIN_SIZE ) {
		sts.min_size = s->min_size;
		err.subcode1 &= ~ VSF_MIN_SIZE;
	}
	if ( flags & VSF_POSITION ) {
		sts.position = s->position;
		err.subcode1 &= ~ VSF_POSITION;
	}
end:
	V_OP_END
	return err;
};

void
VObject::redraw(VRect* rect) const
{
	_V_OP_START_VOID
	// propagate to children
	for ( VObjectList *list = sts.children ; list ; list = list->next )
		list->object->redraw(rect);
	V_OP_END
}


//========== utility ==========


VObject *
VObject::get_object_by_id(unsigned id)
{
	return v_object_hash_search(id);
}

const char *
VObject::describe_self()
{
	if ( self_describe == 0 ) {
		self_describe = (char*)d_alloc(128);
		int len;
		char *type_name = v_type_name(get_type());
		if ( type_name )
			len = sprintf(self_describe, "%s - %d", type_name, sts.id);
		else
			len = sprintf(self_describe, "%d - %d", get_type(), sts.id);
		self_describe = (char*)d_re_alloc(self_describe, len+1);
	}

/*
	char *ret = (char*)d_alloc(256);
	strcpy(ret, self_describe);
	if ( sts.descriptor )
		strncat(ret, n_string(std_cm,(L_CHAR*)sts.descriptor), 256);
	set_buffer(ret);
*/
	if ( destroy_time != V_OBJECT_ALIVE )
		strcat(self_describe, " DESTROYED");
	return self_describe;
}

VObject *
VObject::get_nmc(int c)
{
VError err;
VObject * ret;
	err = lock_d(__FILE__,__LINE__);
	if ( err )
		return 0;
	if ( get_type() < VO_MACR && c == 0 ) {
		ret = this;
		goto end;
	}
	if ( sts.children )
		ret = sts.children->object->get_nmc(0);
	else	ret = 0;
end:
	unlock(this);
	return ret;
}


//========== tree management ==========


VError
VObject::set_parent(VObject* parent) {
	VError err = V_ER_NO_ERR, err2;

	if ( get_type() == VO_WIND )
		sts.window = this;
	else {
		parent->get_status(&sts, VSF_PARENT); // set sts.window
	}
	
	sts.parent = parent;
	
	err2 = parent->add_child(this);
	if ( err2 )	
		err = err2;
	return err;
}


VError
VObject::add_child(VObject *child)
{
	VObjectList *prev = 0;
	VError err = accept_child(child->get_type(), child, &prev);
	if ( err )
		return err;
	
	VObjectList *new_list = new VObjectList;
	new_list->object = child;
	new_list->next = 0;
	if ( prev )
		prev->next = new_list;
	else
		sts.children = new_list;
//printf("add child  %s  >  %s\n",child->describe_self(), describe_self());
	return V_ER_NO_ERR;
}

VError
VObject::remove_child(VObject *child)
{
	VObjectList *list, *prev;
	for ( prev = 0, list = sts.children ; list ; prev = list, list = list->next ) {
		if ( list->object == child ) {
			if ( prev )
				prev->next = list->next;
			else
				sts.children = list->next;
//printf("remove child  %s  >  %s\n",child->describe_self(), describe_self());
#ifdef USE_V_LAYOUT
			if ( this->get_type() )
				VLayout::mark(this);
#endif
			delete list;
			return V_ER_NO_ERR;
		}
	}
	return V_ER_NOT_FOUND;
}


VError
VObject::accept_child(long type, VObject *obj, VObjectList** last_item) const
{
	if ( type == 0 )
		return V_ER_PARAM;
	VError err = accept_child_ex(type, obj);
	if ( err )
		return err;
	VObjectList *list, *last = 0;
	unsigned cnt = 0;
	unsigned maxc = max_children();
	if ( maxc == 0 )
		return V_ER_MAX_CHILDREN;
	for ( last = 0, list = sts.children ; list ; last = list, list = list->next ) {
		if ( list->object == obj )
			return V_ER_ALREADY;
		if ( ++cnt >= maxc )
			return V_ER_MAX_CHILDREN;
	}
	if ( last_item )
		*last_item = last;
	return V_ER_NO_ERR;
}



// ========== FreeList ==========

void
VObject::free_on_release(void *target,void (*func)(int,void*))
{
	_V_OP_START_VOID
	
	if ( target == 0 )
		er_panic("free_on_release");
	
	FreeList * list = new FreeList;
	list->target = target;
	list->free_func = func;
	list->next = free_list;
	free_list = list;
	
	V_OP_END
}

void
VObject::free_on_release_list(FreeList *list)
{
	if ( list == 0 )
		return;
	
	_V_OP_START_VOID
	
	FreeList *k;
	for ( k = list ; k->next ; k = k->next ) { }
	k->next = free_list;
	free_list = list;
	
	V_OP_END
}


// ========== v_zonbie ==========

void
VObject::v_zonbie_insert()
{
	lock_task(v_zonbie_lock);
	VObjectList *list = new VObjectList;
	list->object = this;
	list->next = v_zonbie_list;
	v_zonbie_list = list;
	destroy_time = get_xltime();
	unlock_task(v_zonbie_lock, "v_zonbie_insert");
}

void
VObject::v_zonbie_tick()
{
	lock_task(v_zonbie_lock);
	VObjectList *list, *prev = 0, *next;
	int time_limit = get_xltime() - V_OBJECT_ZONBIE_DELAY;
	for ( list = v_zonbie_list ; list ; list = next ) {
		next = list->next;
		if ( list->object->destroy_time <= time_limit ) {
			if ( prev )
				prev->next = next;
			else
				v_zonbie_list = next;
			delete list->object;
			delete list;
		}
		else
			prev = list;
	}
	unlock_task(v_zonbie_lock, "v_zonbie_tick");
}



// for VObject subclasses :: create implementation
VObject *VObject::create_tmp(VObject *(*new_object)(), unsigned obj_type, const VObjectStatus* s, int flags, VExError* err, void* arg){
	VObject *ret = 0;
	VObjectStatus ss;
	VExError err3;
	VObjectList * objs, * op, * op2;
	VError err2;
	int end_create = 0;

	op2 = 0;

	if ( err )
		*err = initial_VExError(V_ER_NO_ERR,0,0);
	if ( !(flags & VSF_PARENT) || s->parent == 0 ) {
		if ( err )
			*err = initial_VExError(V_ER_PARAM,flags,VSF_PARENT);
		return 0;
	}
		
	err2 = s->parent->lock_d(__FILE__,__LINE__);
	if ( err2 ) {
		if ( err )
			*err = initial_VExError(err2,flags,VSF_PARENT);
		goto end0;
	}
	ss.parent = s->parent;
	err2 = check_parent(s->parent, obj_type);
	if ( err2 ) {
		if ( err )
			*err = initial_VExError(err2,flags,VSF_PARENT);
		goto end;
	}
	objs = 0;
	unlock(s->parent);

	for ( ; ; ) {
		err2 = ss.parent->lock_d(__FILE__,__LINE__);
		if ( err2 ) {
			if ( err )
				*err = initial_VExError(V_ER_DESTROYED,flags,VSF_PARENT);
			goto end0;
		}
		op = new VObjectList;
		op->next = objs;
		objs = op;
		objs->object = ss.parent;
		if ( ss.parent->get_type() < VO_MACR )
			break;
		unlock(ss.parent);

		err3 = ss.parent->get_status(&ss,VSF_PARENT);
		if ( err3.code ) {
			if ( err )
				*err = initial_VExError(err3.code,flags,VSF_PARENT);
			goto end0;
		}
		if ( ss.parent == 0 ) {
			if ( err )
				*err = initial_VExError(V_ER_DESTROYED,flags,VSF_PARENT);
			goto end0;
		}
	}
	for ( op = objs->next ; op ; op = op->next ) {
		err2 = op->object->lock_d(__FILE__,__LINE__);
		if ( err2 ) {
			op2 = op;
			if ( err )
				*err = initial_VExError(V_ER_DESTROYED,flags,VSF_PARENT);
			goto end2;
		}
	}
	ret = new_object();
	err3 = ret->create_fin(s,flags,ss.parent,arg);
	if ( err3.code == 0 )
		end_create = 1;
	if  ( err3.code && err )
		*err = err3;
	
end2:
	op = objs->next;
	delete objs;
	objs = op;
	for ( ; objs != op2 ; ) {
		op = objs->next;
		unlock(objs->object);
		delete objs;
		objs = op;
	}
end:
	unlock(ss.parent);
end0:
	if ( end_create ) {
		err3 = ret->create_do_out_of_lock(s,flags,ss.parent,arg);
		if (err3.code && err )
			*err = err3;
	}

#ifdef USE_V_LAYOUT
	if ( s->parent->get_type() )
		VLayout::mark(s->parent);
#endif
	return ret;
}

VExError VObject::create_do_out_of_lock(const VObjectStatus *,int flags,VObject * nmp,void * arg)
{
	return initial_VExError(V_ER_NO_ERR,0,0);
}

V_CALLBACK_D(v_object_destroyer)
{
	object->destroy();
}


