/*
 * Sample implementation of LibV kernel object.
 */

#include <../source/TAL/objbase.h>

/*
 * Class definition
 */
typedef struct obj_s
{
	libv_object_t	base;	/* required; must be first field */
	/*
	 * The rest depends on object's purpose
	 */
	char	*mem_in_object;
	char	*mem_allocated;
	int		(V_CALL *print_hello)(vhandle_t handle);
}	obj_t, *obj_p;

#ifdef	CHECKED_BUILD
#define	TOBJ_MAGIC	0x01020304		/* RTTI field */
static	char 	*tobj_name = "test object";	/* object name */
#endif	/* CHECKED_BUILD */

/*
 * Destructor and member function(s)
 */

/*
 * All objects are reference-counted. The main destructor wrapper -
 * libvCloseHandle() - doesn't modify counter; counter must be
 * decreased and analysed in object-specific destructor. When counter
 * reaches zero, object must be removed from memory. It's up to this
 * routine to do these steps.
 */
u32_t	V_CALL
obj_destroy(vhandle_t handle)
{
	/*
	 * Here we are sure that 'handle' is a pointer to 'obj_t' -
	 * necessary check was already performed in libvCloseHandle().
	 */
	obj_p self=(obj_p)handle;

	conPrintf("obj::destroy: ref_count is %u.\n",self->base.ref_count);

	if(--self->base.ref_count)
		/* there are still references: keep object */
		return self->base.ref_count;
	/*
	 * No more references: destroy object. At first, we must
	 * perform object-specific cleanups.
	 */
	memFree(self->mem_allocated);
	/*
	 * This step is necessary for any object:
	 */
	memFreeObject(self);
	
	conPuts("obj::destroy: object destroyed.\n");
	return 0;
}

int	V_CALL
obj_print_hello(vhandle_t handle)
{
	obj_t *self = (obj_t*)handle;
	conPrintf("%s%s\n",self->mem_in_object,self->mem_allocated);
	return 0;
}

/*
 * Constructor and global functions
 */
vhandle_t	/* V_GLOBAL */
testobjCreate(void)
{
	obj_p obj;
	/*
	 * The usual way to allocate and initialize memory for
	 * objects is call to 'ALLOC_OBJECT ' macro. This macro
	 * takes four arguments:
	 *  - identifier of pointer-to-object to be created;
	 *  - name of object's type (previously defined C type);
	 *  - amount additional memory (can be accesed at (obj+1));
	 *  - 'magic' dword for run-time type checks.
	 */
	ALLOC_OBJECT(obj,obj_t,1024,TOBJ_MAGIC);
	/*
	 * Override default destructor
	 */
	obj->base.destroy = obj_destroy;
	/*
	 * Perform custom initialization
	 */
	obj->print_hello = obj_print_hello;
	obj->mem_in_object=(char*)(obj+1);
	obj->mem_allocated=(char*)memAlloc(1024);
	strCopy(obj->mem_in_object,"Hello",1024);
	strCopy(obj->mem_allocated,", world !",1024);
	return obj;
}

/*
 * Object will not be duplicated; instead, this procedure
 * increments it's reference counter.
 */
vhandle_t	/* V_GLOBAL */
testobjDuplicate(vhandle_t handle)
{
	obj_p self;
	self=(obj_p)libvIsHandle(handle,"testobjDuplicate",tobj_name,TOBJ_MAGIC);
	if(self == LIBV_INVALID_HANDLE)
		return LIBV_INVALID_HANDLE;
	self->base.ref_count++;
	return self;
}	

/*
 * The only member function (not really useful, although...)
 */
int	/* V_GLOBAL */
testobjDoIt(vhandle_t handle)
{
	obj_p self;
	self=(obj_p)libvIsHandle(handle,"testobjDoIt",tobj_name,TOBJ_MAGIC);
	if(self == LIBV_INVALID_HANDLE)
		return -1;
	return self->print_hello(self);
}

/*
 * Public (client-visible) destructor
 */
void	/* V_GLOBAL */
testobjDestroy(vhandle_t handle)
{
	u32_t newref;
	newref=libvCloseHandle(handle,"testobjDestroy()",tobj_name,TOBJ_MAGIC);
	if(newref)
		conPrintf("testobjDestroy(): still exists (%u refs)\n",newref);
	else
		conPuts("testobjDestroy(): not exists anymore.\n");
}

/*
 * Part 4: test suite for all above madness.
 */

static void	print_mem_stats(void)
{
	u32_t mc,pc,oc;
	memStats(&mc,&pc,&oc);
	printf("Memory stats: %u total,%u pointer(s),%u handle(s).\n",mc,pc,oc);
}

/*
 * Let's do some dirty things with memory ...
 */
#define	WE_NOT_FEAR_TO_SEE_SEGFAULT

int main()
{
	vhandle_t object, dupe;
	logStartConsoleLog();
	logUpperLevel(LOG_TRACE);
	print_mem_stats();
	puts("Calling testobjCreate() ...");
	object=testobjCreate();			/* create object */
	print_mem_stats();
	puts("Calling testobjDuplicate() ...");
	dupe=testobjDuplicate(object);	/* increase ref_count */
	print_mem_stats();
	puts("Calling object ...");
	if(testobjDoIt(object) == 0)
		puts("Call succeded.");
	else
		puts("Call failed.");
	puts("Calling destructor ...");
	testobjDestroy(object);			/* decrease ref_count */
	print_mem_stats();
	puts("Calling destructor ...");
	testobjDestroy(object);			/* decrease ref_count */
	print_mem_stats();
	puts("Trying to call object again ...");
	if(testobjDoIt(object) == 0)
		puts("Call succeded.");
	else
		puts("Call failed.");
#ifdef	WE_NOT_FEAR_TO_SEE_SEGFAULT
	puts("Trying to access memory previously occuped by handle ...");
	puts("(this may cause a core dump, so be prepared ;)");
	fflush(stdout);
	printf("First dword of datablock now contain: 0x%08X\n",
		*((u32_t*)object));
#endif	/* WE_NOT_FEAR_TO_SEE_SEGFAULT */
	logStopConsoleLog();
	print_mem_stats();
	return 0;
}
