/*
 * 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 destructor's wrapper -
 * libvCloseHandle() - doesn't modify counter; counter must be
 * decreased and then 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' - the
	 * necessary check was already performed in libvCloseHandle().
	 */
	obj_p self = (obj_p)handle;

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

	/*
	 * In real programe, here must be a macro:
	UNREF_EXIT(self);
	 * This macro expands to:
	 */
	if(--self->base.ref_count)
		/* there are still referencies: keep the object */
		return self->base.ref_count;
	/*
	 * No more referencies: destroy object. At first, we must
	 * perform object-specific cleanups.
	 */
	memFree(self->mem_allocated);
	/*
	 * The final 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	/* in real program, should also be V_GLOBAL */
testobjCreate(void)
{
	obj_p obj;
	/*
	 * The usual way to allocate and initialize memory for
	 * objects is to 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 of 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 entirely; instead, this
 * routine increments the 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, though...)
 */
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 this madness.
 */

static void	print_mem_stats(void)
{
	u32_t mc,pc,oc;
	memStats(&mc,&pc,&oc);
	printf("Memory usage: %u bytes, %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;
	print_mem_stats();
	puts("Turning logging on ...");
	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 contains: 0x%08X\n",
		*((u32_t*)object));
#endif	/* WE_NOT_FEAR_TO_SEE_SEGFAULT */
	puts("Turning logging off ...");
	logStopConsoleLog();
	print_mem_stats();
	return 0;
}
