/*********************************************************************
 FOOPY OOP system
 *********************************************************************/

/* This file is part of Fenix. Copyright Sven Karlsson 1996-1999 

   This file, or any part of it, _may_ not be used in any other 
   project,commercial or  noncommercial, without a written 
   permission by Sven Karlsson.

   However, members of the Fenix developer list are allowed to 
   modify this file as long as the modifications are not made
   public and all modifications are sent to Sven Karlsson.

   Note: This _will_ go away and will be replaced by some sort
   of public licence. Fenix will definately be some sort of
   free software eventually.

   Sven Karlsson can be reached at sven@it.lth.se
 */

#include "base/types.h"
#include "FOOPY/FOOPY.h"
#include <stdlib.h>
#include <string.h>

/*********************************************************************
 NOTE the Object class and Meta Object class implementations are coded
  using heavy spaghetti and some black magic. Ignore if you do not 
  understand.
 *********************************************************************/

/* the hashes that holds all selectors and classes */

#define CLASS_HASH_SIZE	6	
STATIC FENIX_BASE_CLASS *class_hash[1<<CLASS_HASH_SIZE];
#define SELECTOR_HASH_SIZE 8
STATIC SEL selector_FGID_hash[1<<SELECTOR_HASH_SIZE];
STATIC SEL selector_name_hash[1<<SELECTOR_HASH_SIZE];

STATIC FOOPY_EXCEPTION_ENTRY *FOOPY_exception_chain=NULL;
STATIC OBJECT                 FOOPY_dummy_malloc_exception;

STATIC CLASS                  Kludge_exception_class=NULL;
STATIC CLASS                  meta_class_class=NULL;

STATIC VOID raise_exception(UINT32 exceptionId)
{
 struct selector kludge_selector={0,FOOPY_alloc,NULL,NULL,NULL};
 OBJECT exception=(OBJECT)invoke_method_basic(Kludge_exception_class,NULL,&kludge_selector,NULL);

 if (exception)
 { 
  kludge_selector.FGID=FOOPY_init_UI;
  exception=(OBJECT)invoke_method_basic(Kludge_exception_class,exception,&kludge_selector,exceptionId);
  if (exception)
  {
   kludge_selector.FGID=FOOPY_raise;
   invoke_method_basic(Kludge_exception_class,exception,&kludge_selector,NULL); /* raise exception */
  }
 }
 exit(0); /* something very serious happened */
}

/* support functions for the object class */
STATIC IPTR object_alloc(UINT32 size)
{
 char *memory=malloc(sizeof(FENIX_BASE_OBJECT) +size);
 if (memory)
 {
  memory+=sizeof(FENIX_BASE_OBJECT);
 }
 else
 {
  struct selector kludge_selector={0,FOOPY_raise,NULL,NULL,NULL};
  invoke_method(FOOPY_dummy_malloc_exception,&kludge_selector,NULL); /* raise exception */
 }
 return((IPTR) memory);
}


STATIC IPTR object_class_dispatcher(FENIX_BASE_CLASS  *the_class,
                                    FENIX_BASE_OBJECT *object,
                                    SEL                selector,
                                    FOOPY_ARGS         args)
{
 switch(selector->FGID)
 {
  case FOOPY_alloc:
	 {
   args.any_ptr=NULL;
	 }
  case FOOPY_hiddenAlloc:
	 {
     /* the class objects are there so we can simply check them to find the instance size */
   FENIX_BASE_CLASS *bottom_class=(FENIX_BASE_CLASS *)args.any_ptr;
	  UINT32 instance_size=0;
	  if (bottom_class && (bottom_class->super_class))
	  {
	   instance_size=bottom_class->super_class->inst_off+bottom_class->inst_size;
	  }
   return(object_alloc(instance_size));
   break;
	 }
  case FOOPY_init:
	 {
   args.any_ptr=NULL;
	 }
  case FOOPY_hiddenInit:
	 {
   FENIX_BASE_CLASS *my_class=(FENIX_BASE_CLASS *)(args.any_ptr);
	  /* note that the_class points to a object class which isn't necesserily
	     the object's class! */
     /* Object has no members so we only need to init the base */		
	  object--;
	  object->next=object;
	  object->prev=object;
	  object->my_class=my_class; 
	  my_class->count_or_class.instance_count++; /* increment the instance count of the class */
   return((IPTR)++object);
	 }
  default:
	 {
   /* send all unknown methods (msgs) to the Meta Class */
   return(invoke_class_method_basic(the_class,object,selector,args.any_ptr));
	 }
 }
 /* this dispatcher really makes new objects */
 return(0);
}
/* support functions for the meta object class */

STATIC FENIX_BASE_CLASS *class_object_alloc(FENIX_BASE_CLASS *the_class, 
										                                  UINT32 nbr_of_IMPs,
											                                 UINT32 class_size)
{
 for(;the_class;the_class=the_class->super_class)
 {
  nbr_of_IMPs+=the_class->nbr_of_accellerated_methods;
 }
 nbr_of_IMPs*=sizeof(IMP);
 return((FENIX_BASE_CLASS *)(((char *)object_alloc(class_size+nbr_of_IMPs))+nbr_of_IMPs));
  /* skip the accellerated methods. They are at the start of the memory block */
}

STATIC FENIX_BASE_CLASS *meta_class_init(FENIX_BASE_CLASS *object_class);

STATIC IPTR meta_object_class_dispatcher(FENIX_BASE_CLASS  *the_class,
								                                 FENIX_BASE_CLASS  *object, 
								   /* NOTE: a meta class operates on classes not objects. 
									  Hence FENIX_BASE_CLASS and not FENIX_BASE_OBJECT! */
                                         SEL               selector,
                              								   FOOPY_ARGS        args)
{
 switch(selector->FGID)
 {
  case FOOPY_alloc:
	 {
   object=NULL;
	  args.any_ptr=NULL;
	 }
  case FOOPY_hiddenAlloc:
	 {
	  return((IPTR)class_object_alloc((FENIX_BASE_CLASS *)object,args.value,
		         object?((FENIX_BASE_CLASS *)object)->super_class->inst_off+
											((FENIX_BASE_CLASS *)object)->inst_size
					      :sizeof(FENIX_BASE_CLASS)));
   break;
	 }
  case FOOPY_deinit:
	 {
   /* destroy the Object object */
   /* first check if it could be a Meta Object. Those can never be dealloced! */
   FENIX_BASE_OBJECT *obj=((FENIX_BASE_OBJECT *) object)-1;
	  if (obj->my_class)
	  {
    /* there is a class implementing this object so it cannot be a meta class */
    /* do a out on the object */
    obj->prev->next=obj->next;
	   obj->next->prev=obj->prev;

    /* if it's a simple object just free it otherwise find calculate how many accellerated
	   methods there is and skip them to get to the start of the memory block and free that */
	   if (!((((FENIX_BASE_OBJECT *) (obj->my_class))-1)->my_class))
	   {
     /* obj isn't the start */
     FENIX_BASE_CLASS *tmp;
	    UINT32 accel_off=0;
	    for(tmp=obj->my_class;tmp;tmp=tmp->super_class)
	    {
      accel_off+=tmp->nbr_of_accellerated_methods;
	    }
	    accel_off*=sizeof(IMP);
	    obj=(FENIX_BASE_OBJECT *)(((char *)obj)-accel_off);
	   }
	   return((IPTR)obj);
	  }
	  else
	  {
    raise_exception(FOOPY_InvalidArgumentException);
	  }
   break;
	 }
  case FOOPY_dealloc:
	 {
   free(args.any_ptr);
	  return(0);
   break;
	 }
  case FOOPY_init:
	 {
   args.any_ptr=NULL;
	 }
  case FOOPY_hiddenInit:
	 {
   /* init a class object */
   /* note this is basically the same as initing the meta class */
   FENIX_BASE_OBJECT *base=((FENIX_BASE_OBJECT *)object)-1;
   FENIX_BASE_CLASS *orig_class=(FENIX_BASE_CLASS *)args.any_ptr; /* the argument is the original 
	                                                                     meta class unless this is the Meta Object
	                                                                     class in which case the argument is NULL */
	  object=meta_class_init(object);

	  if (!orig_class)
	  {
    orig_class=the_class;
	   object->dispatcher.implementation=&object_class_dispatcher;
	  }
   else
	  { 
    /* init the super class */
    object->super_class=orig_class->super_class->count_or_class.class_instance;
    /* and increment the subclass counter */
	   object->super_class->subclass_count++;

	  
	   object->inst_off=object->super_class->inst_off+
		               object->super_class->inst_size;
	  } 

   /* setup the name of the class as the meta class name minus the 'M' */

	  base->my_class=orig_class;

   if (*(orig_class->name)!='M')
	  {
    raise_exception(FOOPY_InvalidArgumentException);
	  }

	  object->name=(orig_class->name)+1;
   object->inst_size=0;
   object->types="";

	  if (!(orig_class->flags&FENIX_BASE_CLASS_flags_instanced))
	  {
    orig_class->count_or_class.class_instance=object;
	  }
	 
	  return((IPTR)object);
   break;
	 }
  case FOOPY_lookupSelector:
	 {
   UINT8  hash_key=0;
	  SEL    test;
	  int    test_value=-1;
   unsigned char *tmp;

	  for(tmp=args.any_ptr;*tmp;hash_key^=*tmp++);
	  hash_key=hash_key&((1<<SELECTOR_HASH_SIZE)-1);
	  test=selector_name_hash[hash_key];
 
	  for(;(test)&&((test_value=strcmp(test->name_type,args.any_ptr))<0);
	       test=test->name_hash_next)
	  {
	  }
	  if (test_value)
	  {
    return(0);
	  }
	  return((IPTR)test);
   break;
	 }
  case FOOPY_lookupSelector_G_A:
	 {
   UINT32 hash_key=args.value;
	  SEL    test;
#define MAKE_FGID_HASH_KEY(a) (((a)^((a)>>8)^((a)>>16)^((a)>>24))&((1<<SELECTOR_HASH_SIZE)-1))
	  hash_key=MAKE_FGID_HASH_KEY(hash_key);
	  test=selector_FGID_hash[hash_key];
	  for(;(test)&&(test->FGID<args.value);
	       test=test->name_hash_next)
	  {
	  }
	  if ((!test)||(test->FGID!=args.value))
	  {
    return(0);
	  }
	  return((IPTR)test);
   break;
	 }
  case FOOPY_registerSelector:
	 {
   FOOPY_registerSelector_args *the_args=(FOOPY_registerSelector_args *)args.any_ptr;
   struct selector kludge_selector={0,FOOPY_lookupSelector,NULL,NULL,NULL};

   SEL test=(SEL)invoke_method(object,&kludge_selector,the_args->name_type);
	  if (test)
	  {
    if (test->FGID==the_args->FGID)
	   {
	    return((IPTR)test);	    
	   }
	   else
	   {
     return(0);	/* another selector has been registered with the same name and different
					              FGID */
	   }
	  }

	  kludge_selector.FGID=FOOPY_lookupSelector_G_A;
	  test=(SEL)invoke_method(object,&kludge_selector,the_args->FGID);
	  if (test)
	  {
    if (!strcmp(test->name_type,the_args->name_type))
	   {
     return((IPTR)test);
	   }
	   else
	   {
     return(0); 	/* another selector has been registered with the same FGID and different
					               name */
	   }
	  }
	  /* there is no similar selector registered. Make a new selector and register that one.*/
	  {
    SEL new_sel=malloc(sizeof(struct selector));
	   if (new_sel)
	   {
     new_sel->FGID=the_args->FGID;
	    new_sel->name_type=the_args->name_type;
	    new_sel->name_hash_next=NULL;
	    new_sel->FGID_hash_next=NULL;
	    if (the_args->the_class)
	    {
      /* we should allocate a new IMP slot to this selector as
		       the method is accellerated */
      UINT32 accel_offset=the_args->the_class->nbr_of_accellerated_methods++;
		    FENIX_BASE_CLASS *tmp=the_args->the_class->super_class;
		    for(;tmp;tmp=tmp->super_class)
		    {
       accel_offset+=tmp->nbr_of_accellerated_methods;
		    }
      accel_offset*=sizeof(IMP);
		    accel_offset+=sizeof(FENIX_BASE_OBJECT);
		    new_sel->accel_offset=accel_offset;
	    }
	    else
	    {
	     new_sel->accel_offset=0;
	    }
     /* first into the FGID hash */
	    {
      UINT32 hash_key=new_sel->FGID;
		    SEL    old_sel=NULL;
		    hash_key=MAKE_FGID_HASH_KEY(hash_key);
		    test=selector_FGID_hash[hash_key];
		    for(;(test)&&(test->FGID<new_sel->FGID);
		        old_sel=test,test=test->FGID_hash_next)
		    {
		    }
		    if (old_sel==NULL)
		    {
	      new_sel->FGID_hash_next=selector_FGID_hash[hash_key];
       selector_FGID_hash[hash_key]=new_sel;
		    }
		    else
		    {
	      new_sel->FGID_hash_next=old_sel->FGID_hash_next;
		     old_sel->FGID_hash_next=new_sel;
		    }
	    }

	    /* then into the name hash */

	    {
      UINT8  hash_key=0;
		    SEL    old_sel=NULL;
      unsigned char *tmp;

		    for(tmp=new_sel->name_type;*tmp;hash_key^=*tmp++);
		    hash_key=hash_key&((1<<SELECTOR_HASH_SIZE)-1);
		    test=selector_name_hash[hash_key];

		    for(;(test)&&(strcmp(test->name_type,new_sel->name_type)<0);
		        old_sel=test,test=test->name_hash_next)
		    {
		    }
		    if (old_sel==NULL)
		    {
       new_sel->name_hash_next=selector_name_hash[hash_key];
       selector_name_hash[hash_key]=new_sel;
		    }
		    else
		    {
	      new_sel->name_hash_next=old_sel->name_hash_next;
		     old_sel->name_hash_next=new_sel;
		    }
	    }
     /* done */
	    return((IPTR)new_sel);
	   }
	  }
	  break;
	 }

  case FOOPY_lookupClass:
	 {
   UINT8              hash_key=0;
	  FENIX_BASE_CLASS   *test;
	  int                test_value=-1;
   unsigned char      *tmp;
     
	  for(tmp=args.any_ptr;*tmp;hash_key^=*tmp++);
	  hash_key=hash_key&((1<<CLASS_HASH_SIZE)-1);
	  test=class_hash[hash_key];

	  for(;(test)&&((test_value=strcmp(test->name+1,args.any_ptr))<0);
	       test=(FENIX_BASE_CLASS *)((((FENIX_BASE_OBJECT *)test)-1)->next))
	  {
	  }
	  if (test_value)
	  {
    return(0);
	  }
   /* check if a class instance is already present */
	  if (!test->count_or_class.class_instance)
	  {
    /* make a class object instance */
	   struct selector kludge_selector={0,FOOPY_init,NULL,NULL,NULL};
      invoke_method(test,&kludge_selector,NULL);
	  }
	  if (test->count_or_class.class_instance)
	  {
	   return((IPTR)(test->count_or_class.class_instance));
	  }
	  else
	  {
    return(0);
	  }
   break;
	 }
  case FOOPY_registerClass:
	 {
   /* register a Meta Class which args.any_ptr points to */
   char *name=((FENIX_BASE_CLASS *)args.any_ptr)->name;
	  if (*name++=='M') /* the name must start with an 'M' (Meta) */
	  {
    /* insert the class into the hash */
    UINT8              hash_key=0;
	   FENIX_BASE_CLASS   *test;
	   FENIX_BASE_OBJECT  *old_class=NULL;
	   int                test_value=-1;
    unsigned char      *tmp;
      
	   for(tmp=name;*tmp;hash_key^=*tmp++);
	   hash_key=hash_key&((1<<CLASS_HASH_SIZE)-1);
	   test=class_hash[hash_key];

	   for(;(test)&&((test_value=strcmp(test->name+1,name))<0);
	       old_class=(((FENIX_BASE_OBJECT *)test)-1),
	       test=(FENIX_BASE_CLASS *)((old_class->next)+1))
		  {
		  }
    if (test_value==0)
	   {
     if (test==args.any_ptr)
	    {
      return((IPTR)test);
	    }
     return(0);			/* The class is already registered */
	   }

	   if (old_class==NULL)
	   {
	    (((FENIX_BASE_OBJECT *)(args.any_ptr))-1)->next=(struct base_object *)class_hash[hash_key];
     class_hash[hash_key]=(FENIX_BASE_CLASS *)args.any_ptr;
	   }
	   else
	   {
     (((FENIX_BASE_OBJECT *)(args.any_ptr))-1)->next=old_class->next;
	    old_class->next=(((FENIX_BASE_OBJECT *)(args.any_ptr))-1);
    }
	   return((IPTR)args.any_ptr);
	  }
	  return(0);
	  break;
	 }
  default:
	 {
   /* strange, a method that I don't know of. Something went wrong! */
   raise_exception(FOOPY_UnimplementedMethodException);
	  break;
	 }
 }
 return(0);
}

STATIC FENIX_BASE_CLASS *meta_class_init(FENIX_BASE_CLASS *object_class)
{
 FENIX_BASE_OBJECT *base=((FENIX_BASE_OBJECT *)object_class)-1;
 base->my_class=object_class; /* a meta class do not really have a meta class! 
                                 However, we link to ourselfs so that memory errors cannot occur.*/
 base->next=base;  /* setup base link */
 base->prev=base;

 /* setup the meta class */
 object_class->dispatcher.implementation=&meta_object_class_dispatcher;
 object_class->super_class=0; /* the object class is the root class */
 object_class->name="MFObject";
 object_class->types="";
 object_class->inst_off=0;    /* the meta class decribe the class! */
 object_class->inst_size=sizeof(FENIX_BASE_CLASS);
 object_class->subclass_count=0;
 object_class->count_or_class.class_instance=0;
 object_class->nbr_of_accellerated_methods=0;
 object_class->flags=0;
 return(object_class);
}

UINT16 go_through_method_list_pre(UINT32 nbr_of_methods,FOOPY_initNewClass_method *method,
							  FENIX_BASE_CLASS *the_class,void *object)
{
 UINT16 nbr_of_accel_methods=0;
 struct selector   kludge_selector={0,0,0,0,0};
 /* first go through the methods registering them if needed*/
 /* just ask the super class it will reach the FObject eventually */
 for(;nbr_of_methods>0;method++,nbr_of_methods--)
 {
  SEL ret_sel;
  kludge_selector.FGID=FOOPY_lookupSelector_G_A;

  ret_sel=(SEL)invoke_super_method_basic(the_class,object,&kludge_selector,method->FGID);
  if (!ret_sel)
  {
   /* register the selector later if it is accellerated*/
   if (method->accellerated)
   {
    nbr_of_accel_methods++;
   }
   else
   {
    if (method->name_type)
	   {
	    FOOPY_registerSelector_args reg_args={method->name_type,method->FGID,0};
	    kludge_selector.FGID=FOOPY_registerSelector;
	    ret_sel=(SEL)invoke_super_method_basic(the_class,object,&kludge_selector,&reg_args);
	   }
   }
  }
	   
  if (ret_sel)
  {
   if ((method->FGID!=FOOPY_dispatch)&&(!ret_sel->accel_offset))
   {
    method->FGID=(-1);
   }
   else
   {
    method->FGID=ret_sel->accel_offset;
   }
   method->accellerated=FALSE;
  }
 }
 return(nbr_of_accel_methods);
}

void go_through_method_list_post(UINT32 nbr_of_methods,FOOPY_initNewClass_method *method,
							  FENIX_BASE_CLASS *super_class,FENIX_BASE_CLASS *the_class,
							  UINT16 nbr_of_accel_methods)
{
 /* copy the super class' accellerated methods */
 UINT32	accel_offset=0;
 FENIX_BASE_CLASS *tmp=super_class;
 for(;tmp;tmp=tmp->super_class)
 {
  accel_offset+=tmp->nbr_of_accellerated_methods;
 }
 accel_offset*=sizeof(IMP);
 if (accel_offset)
 {
  UINT32 start_off=sizeof(FENIX_BASE_OBJECT)+accel_offset;
  memcpy(((char *) the_class)-start_off,
         ((char *)(super_class))-start_off,
	     accel_offset);
 }
 /* now copy new IMPs from registered selectors. Note that the selectors
    really control everything*/
 
 for(;nbr_of_methods>0;nbr_of_methods--)
 {
  /* kludgy: we use the the FGID for the offset too */
  if (method->accellerated)
  {
   /* we can now register the method */
   if (nbr_of_accel_methods>the_class->nbr_of_accellerated_methods)
   {
    struct selector kludge_selector={0,FOOPY_registerSelector,0,0,0};
	   FOOPY_registerSelector_args reg_args={method->name_type,method->FGID,the_class};
	   SEL ret_sel=(SEL)invoke_super_method_basic(the_class,the_class,&kludge_selector,&reg_args);
    if (ret_sel)
	   {
     if ((method->FGID!=FOOPY_dispatch)&&(ret_sel->accel_offset))
	    {
      method->FGID=(-1);
	    }
     else
	    {
      method->FGID=ret_sel->accel_offset;
	    }
	   }
   }
   else
   {
    method->FGID=(-1);
   }
  }
  if (method->FGID!=(-1))
  {
   *((IMP *)(((char *) the_class)-method->FGID))=method->method;
  }
 }
}

/************************* the Meta Class  and Class classes ************************/
/* Class class inherits from Object class 

   Note: In theory the Meta Classes and ordinary classes are sub classes to the Class
    class. However, in practice you will not notice this except for the fact that Class
	   only have Class methods. There is a theoretical recursion in the class tree here but
	   the boot strapping takes care of everything so there are Meta Classes for Object and 
	   Class from the start.

   Final Note: Again this is not the way to implement a new class. Look at for instance 
     Fxxxxx for a proper implementation. 
 */



STATIC IPTR class_class_dispatcher(FENIX_BASE_CLASS  *the_class,
				                        				   FENIX_BASE_CLASS  *object, 
								   /* NOTE: a meta class operates on classes not objects. 
									  Hence FENIX_BASE_CLASS and not FENIX_BASE_OBJECT! */
                                   SEL               selector,
								                           FOOPY_ARGS        args)
{
 switch(selector->FGID)
 {
  default:
	 {
   /* send all unknown methods (msgs) to the super class */
   return(invoke_super_method_basic(the_class,object,selector,args.any_ptr));
	  break;
	 }
 }
 return(0);
}


/* Meta Class class. Inherits from Meta Object class */


STATIC IPTR meta_class_class_dispatcher(FENIX_BASE_CLASS  *the_class,
		                             						   FENIX_BASE_CLASS  *object, 
								   /* NOTE: a meta class operates on classes not objects. 
									  Hence FENIX_BASE_CLASS and not FENIX_BASE_OBJECT! */
                                        SEL               selector,
								                                FOOPY_ARGS        args)
{
 switch(selector->FGID)
 {
  case FOOPY_initNewMetaClass:
	 {
	  FOOPY_initNewMetaClass_args *the_args=(FOOPY_initNewMetaClass_args *) args.any_ptr;
   if (the_args)
	  {
    UINT16 nbr_of_accel_methods=go_through_method_list_pre(the_args->nbr_of_methods,
														 the_args->methods,the_class,object);
    {
     /* allocate a new meta class */
	    FENIX_BASE_CLASS *meta_class=class_object_alloc(the_args->super_class,
													   nbr_of_accel_methods,
													   sizeof(FENIX_BASE_CLASS));
     /* init the meta class */
	    if (meta_class)
	    {
	     meta_class=meta_class_init(meta_class);
	     if (meta_class)
		    {
       meta_class->name=the_args->name;
		     meta_class->types=the_args->types;
	      meta_class->super_class=the_args->super_class;
	      meta_class->inst_size=(UINT16)the_args->inst_size;
	      meta_class->inst_off=meta_class->super_class->inst_off+
							meta_class->super_class->inst_size;
		     /* fix the class instance data offset */
       go_through_method_list_post(the_args->nbr_of_methods,the_args->methods,
			                         meta_class->super_class,meta_class,nbr_of_accel_methods);
		     return((IPTR) meta_class);
		    }
	    }
	   }
	  }
	  raise_exception(FOOPY_InvalidArgumentException);
	  break;
	 }
  case FOOPY_init:
	 {
	  STATIC FOOPY_initNewClass_method the_method={FOOPY_dispatch,NULL,&class_class_dispatcher,TRUE};
	  CONST STATIC FOOPY_initNewClass_args the_args={0,"",1,&the_method};
	  struct selector kludge_selector={0,FOOPY_initNewClass,NULL,NULL,NULL};
	  return(invoke_method_basic(the_class,the_class,&kludge_selector,&the_args));
	  break;
	 }
  case FOOPY_initNewClass:
	 {
   /* this "aid" method takes care of all the kludgy class initialization */
	  /* object is the meta class 
		    args is a pointer to the FOOPY_init_new_class_args struct */
   FOOPY_initNewClass_args *the_args=(FOOPY_initNewClass_args *) args.any_ptr;
   if (the_args)
	  {
    UINT16 nbr_of_accel_methods=go_through_method_list_pre(the_args->nbr_of_methods,
														 the_args->methods,the_class,object);
    {
     /* allocate a new class */
	    struct selector kludge_selector={0,FOOPY_hiddenAlloc,NULL,NULL,NULL};
	    FENIX_BASE_CLASS *class_object=
		   (FENIX_BASE_CLASS *)invoke_super_method_basic(object,NULL,
														 &kludge_selector,
														 nbr_of_accel_methods);
       /* init the class */
	    if (class_object)
	    {
      kludge_selector.FGID=FOOPY_hiddenInit;
	     class_object=(FENIX_BASE_CLASS *)invoke_super_method_basic(object,class_object,
																   &kludge_selector,object);
	     if (class_object)
		    {
	      class_object->inst_size=(UINT16)the_args->inst_size;
		     class_object->types=the_args->types;
       go_through_method_list_post(the_args->nbr_of_methods,the_args->methods,
			                         class_object->super_class,class_object,nbr_of_accel_methods);
		     return((IPTR) class_object);
		    }
	    }
	   }
	  }
	  raise_exception(FOOPY_InvalidArgumentException);
	  break;
	 }
  default:
	 {
   /* send all unknown methods (msgs) to the super class */
   return(invoke_super_method_basic(the_class,object,selector,args.any_ptr));
	  break;
	 }
 }
 return(0);
}


STATIC FENIX_BASE_CLASS *meta_class_class_init(FENIX_BASE_CLASS *class_class,FENIX_BASE_CLASS *object_class)
{
 meta_class_init(class_class);

 class_class->dispatcher.implementation=&meta_class_class_dispatcher;
 class_class->super_class=object_class; /* the object class is the root class */
 class_class->name="MFClass";
 class_class->types="";
 class_class->inst_size=0;
 class_class->inst_off=object_class->inst_size;
 return(class_class);
}

/***************************** END Class class implementation ********************************/

/**************************** The Exception meta class and class *****************************/

/* NOTE: some things are kludgy here... */

typedef struct
{
 UINT32     exception_id;
} EXCEPTION_OBJECT_STRUCT;

STATIC struct
{
 FENIX_BASE_OBJECT base;
 EXCEPTION_OBJECT_STRUCT exc;
} dummy_malloc_exception={{0L,0L,0L},{0L}};



STATIC IPTR exception_class_dispatcher(FENIX_BASE_CLASS  *the_class,
	                            							   FENIX_BASE_CLASS  *object, 
								   /* NOTE: a meta class operates on classes not objects. 
									  Hence FENIX_BASE_CLASS and not FENIX_BASE_OBJECT! */
                                       SEL               selector,
								                               FOOPY_ARGS        args)
{
 switch(selector->FGID)
 {
  case FOOPY_raise:
	 {
   FOOPY_EXCEPTION_ENTRY *the_entry;
	  UINT32 exceptionId=((EXCEPTION_OBJECT_STRUCT *)(INST_DATA(object,the_class)))->exception_id;
     /* raise the exception */
     /* we must get the current thread's exception chain */
	  FOOPY_GET_CURR_EXCEPTION_CHAIN(the_entry);
     /* work our way up the chain */
	  for(;(the_entry);the_entry=the_entry->next)
	  {
    UINT32  catches=the_entry->catch_entries;
	   UINT32 *catch_ptr=the_entry->catch_vector;
	   for(;(catches>0)&&(catch_ptr)&&(*catch_ptr!=exceptionId);catches--,catch_ptr++);
	   if (catches>0)
	   {
	    /* if catch_entries!=0 and catch_ptr==NULL then all exceptions
	      will be handled */
     FOOPY_set_exception_chain(the_entry); /* fixup exception chain */
     longjmp(the_entry->env,(long)object);
	   }
	  }
	  /* found no handler. call the default handler*/
   FOOPY_set_exception_chain(NULL); /* fixup exception chain */     
	  /* found no default handler. exit*/
	  exit(1);
	  /* we will never reach this */
   break;
	 }
  case FOOPY_exceptionId:
	 {
   return(((EXCEPTION_OBJECT_STRUCT *)(INST_DATA(object,the_class)))->exception_id);
	 }
  case FOOPY_init_UI:
	 {
   OBJECT obj;
	  EXCEPTION_OBJECT_STRUCT *tmp;
	  struct selector kludge_selector={0,FOOPY_hiddenInit,NULL,NULL,NULL};
	  obj=(OBJECT)invoke_super_method_basic(the_class,object,&kludge_selector,the_class);
   tmp=(EXCEPTION_OBJECT_STRUCT *)(INST_DATA(obj,the_class));
	  tmp->exception_id=args.value;
   return((IPTR)obj);
	 }
  case FOOPY_alloc:
	 {
	  struct selector kludge_selector={0,FOOPY_hiddenAlloc,NULL,NULL,NULL};
   args.any_ptr=the_class;
	  return(invoke_super_method_basic(the_class,object,&kludge_selector,args.any_ptr));
	 }
  default:
	 {
   /* send all unknown methods (msgs) to the super class */
   return(invoke_super_method_basic(the_class,object,selector,args.any_ptr));
	  break;
	 }
 }
 return(0);
}


STATIC IPTR meta_exception_class_dispatcher(FENIX_BASE_CLASS  *the_class,
	                                 							   FENIX_BASE_CLASS  *object, 
								   /* NOTE: a meta class operates on classes not objects. 
									  Hence FENIX_BASE_CLASS and not FENIX_BASE_OBJECT! */
                                            SEL               selector,
								                                    FOOPY_ARGS        args)
{
 switch(selector->FGID)
 {
  case FOOPY_init:
	 {
	  struct selector kludge_selector={0,FOOPY_initNewClass,NULL,NULL,NULL};
	  STATIC FOOPY_initNewClass_method methods[4]=
		  {{FOOPY_dispatch,NULL,&exception_class_dispatcher,TRUE},
		   {FOOPY_exceptionId,"exceptionId()UI",NULL,FALSE},
		   {FOOPY_init_UI,"init(UI)L;",NULL,FALSE},
		   {FOOPY_raise,"raise()V",NULL,FALSE}};

	  CONST STATIC FOOPY_initNewClass_args the_args={sizeof(EXCEPTION_OBJECT_STRUCT),"UI",4,methods};
	  return(invoke_method_basic(meta_class_class,the_class,&kludge_selector,&the_args));
	  break;
	 }
  default:
	 {
   /* send all unknown methods (msgs) to the super class */
   return(invoke_super_method_basic(the_class,object,selector,args.any_ptr));
	  break;
	 }
 }
 return(0);
}




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

 Fenix foundation kit

 This is coded properly using sound FOOPY programming techniques.

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

/* first some global selectors */
SEL lookupSelector_G_A_sel;
SEL lookupClass_sel;
SEL initNewMetaClass_sel;
SEL initNewClass_sel;
SEL registerClass_sel;
CLASS FClass;

/* the implementation of the classes */


void init_Fenix_foundation_kit(CLASS FObject,SEL lookupSelector_sel)
{
 lookupSelector_G_A_sel=(SEL)invoke_method(FObject,lookupSelector_sel,"lookupSelector(G)A");
 if (lookupSelector_G_A_sel)
 {
  lookupClass_sel=(SEL)invoke_method(FObject,lookupSelector_G_A_sel,FOOPY_lookupClass);
  if (lookupClass_sel)
  {
   FClass=(CLASS)invoke_method(FObject,lookupClass_sel,"FClass");
   initNewMetaClass_sel=(SEL)invoke_method(FObject,lookupSelector_G_A_sel,FOOPY_initNewMetaClass);
   initNewClass_sel=(SEL)invoke_method(FObject,lookupSelector_G_A_sel,FOOPY_initNewClass);
   registerClass_sel=(SEL)invoke_method(FObject,lookupSelector_G_A_sel,FOOPY_registerClass);
   if (FClass && initNewMetaClass_sel && initNewClass_sel && registerClass_sel)
   {
    /* now we have all support Classes and selectors. Contruct the meta classes */

    return;
   }
  }
 }
 /* here to test the exeption handling */
 raise_exception(FOOPY_GenericException);
}

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

  some Exception support functions

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

VOID FOOPY_add_to_exception_chain(FOOPY_EXCEPTION_ENTRY *entry)
{
 entry->next=FOOPY_exception_chain;
 FOOPY_exception_chain=entry;
}

VOID FOOPY_set_exception_chain(FOOPY_EXCEPTION_ENTRY *entry)
{
 FOOPY_exception_chain=entry;
} 


/*********************************************************************************************
 the main FOOPY function 

 This is really kludgy and bootstraps the whole runtime. Do not code FOOPY code like this!
 *********************************************************************************************/
FENIX_BASE_CLASS *open_FOOPY_library(SEL* lookup_selector_by_name)
{
 /* first some globals */
 STATIC FENIX_BASE_CLASS *object_class=NULL;
 STATIC SEL              lookup_selector_by_name_sel=NULL; /* the only two things needed to gain access 
														                                                to all features of FOOPY */

 if ((object_class==NULL)||(lookup_selector_by_name_sel==NULL))
 {
  /* the library isn't inited yet. Span the tree! */
  /* NOTE: this is _not_ the way anyone should init classes. 
     This is only a bootstrap of the system using the usual kludgy bootstrap tricks! */
  CLASS shadow_object_class=NULL;
  CLASS meta_object_class=NULL;
  CLASS class_class=NULL;
  CLASS meta_exception_class=NULL;

  /* clear all hashes */
  {
   int i;
   for(i=(1<<CLASS_HASH_SIZE)-1;i>=0;i--)
   {
    class_hash[i]=0;
   }
   for(i=(1<<SELECTOR_HASH_SIZE)-1;i>=0;i--)
   {
    selector_FGID_hash[i]=0;
    selector_name_hash[i]=0;	
   }
  }
  /* start with the root meta class*/

  meta_object_class=class_object_alloc(NULL,0,sizeof(FENIX_BASE_CLASS));
  if (meta_object_class)
  {
   meta_object_class=meta_class_init(meta_object_class);
   if (meta_object_class)
   {
   	struct selector lookup_selector={0,0,NULL,NULL,NULL};
    lookup_selector.FGID=FOOPY_alloc;
    shadow_object_class=(FENIX_BASE_CLASS*)invoke_method_basic(meta_object_class,NULL,&lookup_selector,NULL);
    if (shadow_object_class)
	   {
     /* make a root class instance from the meta class */
     lookup_selector.FGID=FOOPY_init;

     shadow_object_class=(FENIX_BASE_CLASS*)invoke_method_basic(meta_object_class,shadow_object_class,&lookup_selector,NULL);
	    if (shadow_object_class)
	    {
      /* the base class is now successfully inited! */
	     /* now add the selectors */
	     {
#define MAX_SELECTOR 14
       CONST STATIC struct
	      {
        UINT32 FGID;
		      const char   *name_type;
	      } selector_tab[MAX_SELECTOR]=
	      {
		      {FOOPY_dispatch,"dispatch"},
		      {FOOPY_init,"init()L;"},
        {FOOPY_hiddenInit,"hiddenInit(M;)L;"},
		      {FOOPY_alloc,"alloc()*B"},
		      {FOOPY_hiddenAlloc,"hiddenAlloc(M;)*B"},
		      {FOOPY_deinit,"deinit()I"},
		      {FOOPY_dealloc,"dealloc()I"},
		      {FOOPY_lookupClass,"lookupClass(*B)*M"},
		      {FOOPY_lookupSelector,"lookupSelector(*B)A"},
		      {FOOPY_lookupSelector_G_A,"lookupSelector(G)A"},
	       {FOOPY_registerClass,"registerClass(M;)M;"},
		      {FOOPY_registerSelector,"registerSelector(*BGM;)A"},
		      {FOOPY_initNewMetaClass,"initNewMetaClass(*BM;*BUIUI*{G*BRZ})M;"},
		      {FOOPY_initNewClass,"initNewClass(UI*BUI*{G*BRZ})M;"}
	      };
	      struct selector kludge_selector={0,FOOPY_registerSelector,NULL,NULL,NULL};
   	   FOOPY_registerSelector_args the_args={NULL,0,NULL};
	      int i;
	      for(i=MAX_SELECTOR-1;i>=0;i--)
       {
        the_args.name_type=selector_tab[i].name_type;
		      the_args.FGID=selector_tab[i].FGID;
        invoke_method(shadow_object_class,&kludge_selector,&the_args);
	      }
	     }
      /* now add the meta class class and class class, ie the classes that control how classes work */
	     meta_class_class=class_object_alloc(NULL,0,sizeof(FENIX_BASE_CLASS));
      if (meta_class_class)
	     {
       meta_class_class=meta_class_class_init(meta_class_class,meta_object_class);
       if (meta_class_class)
	      {
        lookup_selector.FGID=FOOPY_init;

	       class_class=(FENIX_BASE_CLASS*)invoke_method_basic(meta_class_class,class_class,&lookup_selector,NULL);
	       if (class_class)
	      	{
         lookup_selector.FGID=FOOPY_initNewMetaClass;
         {
	         STATIC FOOPY_initNewClass_method the_method={FOOPY_dispatch,NULL,&meta_exception_class_dispatcher,TRUE};
	         FOOPY_initNewMetaClass_args the_args={"MFException",meta_object_class,"",0,1,&the_method};          
          meta_exception_class=(FENIX_BASE_CLASS*)invoke_method_basic(meta_class_class,NULL,&lookup_selector,&the_args);
         }
 
         if (meta_exception_class)
		       {	
          /* setup dummy exception that is used when we run out of memory */
          lookup_selector.FGID=FOOPY_init;
          Kludge_exception_class=(FENIX_BASE_CLASS*)invoke_method_basic(meta_exception_class,Kludge_exception_class,&lookup_selector,NULL);
          if (Kludge_exception_class)
		        {
           lookup_selector.FGID=FOOPY_init_UI;
           FOOPY_dummy_malloc_exception=(FENIX_BASE_OBJECT*)invoke_method_basic(Kludge_exception_class,&(dummy_malloc_exception.exc),&lookup_selector,FOOPY_MallocException);
		         if (FOOPY_dummy_malloc_exception)
		         {
		          /* register the MFObject, MFClass and MFException meta classes */
            lookup_selector.FGID=FOOPY_registerClass;
		          if (invoke_method(shadow_object_class,&lookup_selector,meta_object_class))
			         {
             if (invoke_method(shadow_object_class,&lookup_selector,meta_class_class))
			          {
			           if (invoke_method(shadow_object_class,&lookup_selector,meta_exception_class))
			           {
               lookup_selector.FGID=FOOPY_lookupSelector_G_A;
            
		             if ((lookup_selector_by_name_sel=
			                 (SEL)invoke_method(shadow_object_class,&lookup_selector,
		                                     FOOPY_lookupSelector)))
		             {
		              object_class=shadow_object_class;
			             /* now the runtime is up and running. We can now safely add whole kits of Classes */
			             init_Fenix_foundation_kit(object_class,lookup_selector_by_name_sel);
			            }
			           }
             }
            }
		         }
		        }
		       }
		      }
	      }
	     }
	    }
	   }
   }
  }
 }
 *lookup_selector_by_name=lookup_selector_by_name_sel;
 return(object_class);
}
