/*
	ZOONIC RELEASE 2

	(c)1997 by Tinic Uro
*/
#include "zoonic.h"

Class *OpenzOOnicClass(char *classname, int32 minversion, int32 *error);			

static sem_id	classlist_sem	= 0;
static Class 	*classlist		= 0;

#ifdef __cplusplus

struct TagItem *FindTagItem(struct TagItem *tags, int32 TagID )
{	
	if(tags)
	{
		for(;tags->TagID!=TAG_END;tags++)
		{
			if(tags->TagID==TAG_MORE)tags=(struct TagItem *)tags->TagData;
			if(tags->TagID==TagID) return tags;
		}
	}
	return 0;
}

int32 GetTagData(struct TagItem *tags, int32 defval, int32 TagID )
{
	if(tags)
	{
		for(;tags->TagID!=TAG_END;tags++)
		{
			if(tags->TagID==TAG_MORE)tags=(struct TagItem *)tags->TagData;
			if(tags->TagID==(int32)TagID) return (int32)tags->TagData;
		}
	}
	return defval;
}

static int32 	defaultdispatcher(Class *c, Object *o, uint32 MethodID, uint32 *data) 
{
	c=c;
	data=data;
	
	switch(MethodID)
	{
		case	B_METHOD_CONSTRUCT:
		case	B_METHOD_DESTROY:
		case	B_METHOD_SETATTR:
				o->SetError(B_NO_ERROR);
				return NULL;
		case	B_METHOD_GETATTR:
				o->SetError(B_UNKNOWN_ATTRIBUTE);
				return NULL;
		default:
				o->SetError(B_UNKNOWN_METHOD);
				return NULL;
	}
}

int32 CreatezOOnic(void)
{
	classlist_sem 				= create_sem(1,"classlist_sem");
	classlist 					= new Class("",0,0,0);
	classlist->_nextclass		= 0;
	classlist->_dispatcher		= defaultdispatcher;
	classlist->_superclass		= 0;
	classlist->_classname		= strdup("root.zclass");
	classlist->_instanceoffset	= 0;
	classlist->_instancesize	= 0;
	classlist->_error			= 0;
	classlist->_image_id		= 0;

	if(classlist&&classlist_sem)return B_NO_ERROR;
	else 						return B_ERROR;
}

void DestroyzOOnic(void)
{
	Class *c=classlist;
	for(;c;)
	{
		classlist=c;c=c->_nextclass;
		
		if(classlist->_superclassname)	free(classlist->_superclassname);
		if(classlist->_classname)		free(classlist->_classname);
		if(classlist->_image_id)		unload_add_on(classlist->_image_id);

		delete classlist;
	}
	delete_sem(classlist_sem);
}

Class *OpenzOOnicClass(char *classname, int32 minversion, int32 *error)
{
	if(!classname) return classlist;

	if(acquire_sem(classlist_sem)==B_NO_ERROR)
	{
		Class *firstclass=classlist;
		do
		{
			if(firstclass->_classname)
			if(strcmp(firstclass->_classname,classname)==0)
			{
				release_sem(classlist_sem);
				return firstclass;
			}
			firstclass=firstclass->_nextclass;
		}		
		while (firstclass);
		release_sem(classlist_sem);
	}

	image_id iimage_id;	
	char filename[MAX_CLASSNAMELENGTH+21]="/boot/system/add-ons/zclasses/";
	strcat(filename,classname);
	iimage_id = load_add_on(filename);

	if(iimage_id != B_ERROR)
	{
		int32 (*class_version)();
		if(get_image_symbol(iimage_id,"class_version__Fv",						2,&class_version)		!=B_NO_ERROR) return NULL;
		int32 version=class_version();

		int32 (*class_superversion)();
		if(get_image_symbol(iimage_id,"class_superversion__Fv",					2,&class_superversion)	!=B_NO_ERROR) return NULL;
		int32 superversion=class_superversion();

		int32 (*class_instancesize)();
		if(get_image_symbol(iimage_id,"class_instancesize__Fv",					2,&class_instancesize)	!=B_NO_ERROR) return NULL;
		int32 instancesize=class_instancesize();

		char *(*class_super)();
		if(get_image_symbol(iimage_id,"class_superclass__Fv",					2,&class_super)			!=B_NO_ERROR) return NULL;
		char *superclassname=class_super();

		int32 (*dispatcher)(Class *c, Object *o, uint32 MethodID, uint32 *data);
		if(get_image_symbol(iimage_id,"class_dispatcher__FP5ClassP6ObjectUlPUl",2,&dispatcher)			!=B_NO_ERROR) return NULL;

		if(version>=minversion)
		{
			Class *c = new Class(0,0,0,0);
			int32 cerror;
			
			c->_instanceoffset	= 0;
			c->_instancesize 	= instancesize;
			c->_version 		= version;
			c->_superclassname	= strdup(superclassname);
			c->_classname 		= strdup(classname);
			c->_image_id 		= iimage_id;
			c->_dispatcher 		= dispatcher;
			c->_nextclass 		= 0;
			c->_superclass 		= OpenzOOnicClass(superclassname,superversion,&cerror);

			if(c->_superclass)
			{	
				c->_instanceoffset=c->_superclass->_instanceoffset+c->_superclass->_instancesize;

				if(acquire_sem(classlist_sem)==B_NO_ERROR)
				{
					Class *lastclass=classlist;
					for(;lastclass->_nextclass;) lastclass=lastclass->_nextclass;
					lastclass->_nextclass=c;	
					release_sem(classlist_sem);
					
					fprintf(stderr,"%s loaded.\n",classname);
					
					return c;
				}						
			}			
			else
			{
				*error=B_BAD_SUPERCLASS;
			}
			c->_image_id 		= 0;
			delete c;
		}
		else
		{
			*error=B_FALSE_VERSION;
		}
		unload_add_on(iimage_id);
	}
	else
	{
		*error=B_UNKNOWN_CLASS;
	}
	return NULL;
}

int32 CreateObjectList(Object ***list, const char *path, int32 minversion)
{
	char fullpath[1024]="/boot/home/config/add-ons/zclasses/";
	strcat(fullpath,path);
	BDirectory bdir = BDirectory(fullpath);
			
	if(int32 num=bdir.CountEntries())
	{
		BEntry bentry = BEntry();
		BPath bpath = BPath();
		*list=(Object **)malloc(sizeof(Object*)*(num+1));
		
		bdir.Rewind();
		for(int c=0;c<num;c++)
		{
			bdir.GetNextEntry(&bentry);
			bentry.GetPath(&bpath);
			*list[c] = new Object((char *)bpath.Path()[22],minversion);
		}
		*list[num]=0;
		return B_NO_ERROR;
	}
	return B_ERROR;
}

int32 DeleteObjectList(Object ***list)
{	
	int c=0;
	while(*list[c]) delete *list[c++];
	free(*list);
	return B_NO_ERROR;
}

Class::Class(char *superclassname, int32 superminversion, int32 instancesize, int32 (*dispatcher)(Class *c, Object *o,uint32 MethodID,uint32 *data))
{
	if(superclassname==0)	return;
	if(superclassname[0]==0)return;

	_image_id		= 0;
	_superclass		= 0;
	_classname		= 0;
	_instancesize	= instancesize;
	_dispatcher		= dispatcher;
	_version		= 0;
	_superclassname = strdup(superclassname);

	if((_superclass=OpenzOOnicClass(superclassname,superminversion,&_error))!=0)
	{
		_instanceoffset=_superclass->_instanceoffset+_superclass->_instancesize;
		_error = B_NO_ERROR; 
	}
}

Class::Class(Class *superclass, int32 instancesize, int32 (*dispatcher)(Class *c, Object *o, uint32 MethodID, uint32 *data))
{
	_image_id		= 0;
	_nextclass		= 0;
	_superclass		= superclass;
	_classname		= 0;
	_error			= B_NO_ERROR;
	_instancesize	= instancesize;
	_dispatcher		= dispatcher;
	_version		= 0;
	_instanceoffset = _superclass->_instanceoffset+_superclass->_instancesize;
	
	if(superclass->_classname)
	{
		_superclassname = strdup(superclass->_classname);
	}	
	else
	{
		_superclassname = 0;
	}	
}

Class::~Class()
{
	if(acquire_sem(classlist_sem)==B_NO_ERROR)
	{
		Class *scanclass=classlist,*prevclass=0;
		
		bool found=FALSE;

		for(;scanclass->_nextclass;)
		{
			if(this==scanclass)
			{
				found=TRUE;
				break;
			}
			prevclass=scanclass;
			scanclass=scanclass->_nextclass;
		}
		
		if(found)
		{
			if(prevclass)
			{
				prevclass->_nextclass=scanclass->_nextclass;
			}
			else
			{
				classlist=scanclass->_nextclass;
			}
		}
						
		release_sem(classlist_sem);
	}
}

void Class::SetError(int32 errorcode)
{
	_error=errorcode;
};

int32 Class::Error()
{
	return _error;
};

Object::Object(Class *baseclass, ...)
{
	_instance=0;
	_baseclass=0;

	if((_baseclass=baseclass)!=0)
	{
		_instance = (void *)(new uchar [_baseclass->_instanceoffset+_baseclass->_instancesize+4]);
		
		va_list	argptr;
		va_start (argptr,baseclass);
		_baseclass->_dispatcher(_baseclass,this,B_METHOD_CONSTRUCT,(uint32 *)argptr);
		va_end (argptr);
		
		_error=B_NO_ERROR;
		return;
	}

	_error=B_ERROR;
};

Object::Object(char *baseclassname, int32 minversion, ...)
{
	_instance=0;
	_baseclass=0;
	_error=B_UNKNOWN_CLASS;
	
	if(baseclassname)
	{
		if((_baseclass = OpenzOOnicClass(baseclassname, minversion, &_error))!=0)
		{	
			_instance = (void *)(new uchar [_baseclass->_instanceoffset+_baseclass->_instancesize+4]);

			va_list argptr;
			va_start (argptr,minversion);
			_baseclass->_dispatcher(_baseclass,this,B_METHOD_CONSTRUCT,(uint32 *)argptr);
			va_end (argptr);

			_error=B_NO_ERROR;
			return;
		}
	}
};

Object::~Object()
{
	if(_baseclass)
	{
		_baseclass->_dispatcher(_baseclass,this,B_METHOD_DESTROY,0);
	}
	if(_instance)
	{
		delete [] ((uchar *)_instance);
	}
}
#endif
