/* 
   NGObjCClass.m

   Copyright (C) 1998 MDlink online service center, Helge Hess
   All rights reserved.

   Author: Helge Hess (helge@mdlink.de)

   This file is part of the MDlink Object Framework 2 (MOF2)

   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted, provided
   that the above copyright notice appear in all copies and that both that
   copyright notice and this permission notice appear in supporting
   documentation.

   We disclaim all warranties with regard to this software, including all
   implied warranties of merchantability and fitness, in no event shall
   we be liable for any special, indirect or consequential damages or any
   damages whatsoever resulting from loss of use, data or profits, whether in
   an action of contract, negligence or other tortious action, arising out of
   or in connection with the use or performance of this software.
*/
// $Id: NGObjCClass.m,v 1.1 2000/03/20 15:45:34 mguesdon Exp $

#include "NGReflectionCommonPrivate.h"
#include "NGObjCClass.h"
#include "NGObjCInstanceVariable.h"
#include "NGObjCMethod.h"
#include "NGObjCInternals.h"
#include <base/behavior.h>
#import <Foundation/NSMapTable.h>

@interface NGObjCClass(PrivateMethods)

- (id)initWithClass:(Class)_class;

@end

@interface NGObjCInstanceVariable(PrivateMethods)

- (id)initWithClass:(NGObjCClass *)_class internalRepresentation:(void *)_irep;

@end

@implementation NGObjCClass

static NSMapTable *classToClassInfo = NULL;

+ (void)initialize {
  static BOOL isInitialized = NO;
  if (!isInitialized) {
    isInitialized = YES;

    classToClassInfo = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
                                        NSObjectMapValueCallBacks,
                                        512);
  }
}

+ (NGObjCClass *)infoForClass:(Class)_class {
  NGObjCClass *class = nil;

  if (_class == nil)
    return nil;

  class = (NGObjCClass *)NSMapGet(classToClassInfo, (void*)_class);
  if (class == nil) {
    class = [NGObjCClass alloc];
    NSMapInsert(classToClassInfo, (void *)_class, (void *)class);
    [class initWithClass:_class];
    RELEASE(class);
  }
  return class;
}
+ (NGObjCClass *)infoForClassNamed:(NSString *)_className {
  if (_className == nil)
    return nil;

  return [self infoForClass:NSClassFromString(_className)];
}

- (id)initWithClass:(Class)_class {
  if ((self = [super init])) {
    self->peer = _class;

    // methods
    /* generated on-demand now
    if (self->peer->methods) {
      self->methods =
        [_makeMethods(self->peer->methods) mutableCopyWithZone:[self zone]];
    }
    */
  }
  return self;
}

- (void)dealloc {
  if (self->instanceVariables) {
    NSFreeMapTable(self->instanceVariables);
    self->instanceVariables = NULL;
  }

  RELEASE(self->methods);    self->methods    = nil;
  RELEASE(self->superClass); self->superClass = nil;
  RELEASE(self->metaClass);  self->metaClass  = nil;
  RELEASE(self->categories); self->categories = nil;
  RELEASE(self->protocols);  self->protocols  = nil;
  [super dealloc];
}

// accessors

- (Class)peerClass {
  return self->peer;
}
- (NGObjCClass *)superClassInfo {
  if (self->superClass == nil) {
    if (self->peer->super_class) {
      self->superClass = [NGObjCClass infoForClass:self->peer->super_class];
      RETAIN(self->superClass);
    }
  }
  return self->superClass;
}
- (NGObjCClass *)metaClassInfo {
  if (self->metaClass == nil) {
    if (self->peer->class_pointer) {
      self->metaClass = [NGObjCClass infoForClass:self->peer->class_pointer];
      RETAIN(self->metaClass);
    }
  }
  return self->metaClass;
}

- (NSString *)className {
  if (class_is_meta_class(self->peer)) // is meta class
    return [NSString stringWithFormat:@"meta<%s>", self->peer->name];
  else
    return [NSString stringWithCString:self->peer->name];
}
- (NSString *)superClassName {
  return [[self superClassInfo] className];
}
- (NSString *)metaClassName {
  return [[self metaClassInfo] className];
}

- (BOOL)isRootClass {
  return ([self superClassInfo] == nil) ? YES : NO;
}
- (BOOL)isMetaClass {
  return class_is_meta_class(self->peer) ? YES : NO;
}
- (BOOL)isResolved {
  return CLS_ISRESOLV(self->peer);
}
- (BOOL)isInitialized {
  return CLS_ISINITIALIZED(self->peer);
}

- (void)setClassNumber:(unsigned short)_number {
  CLS_SETNUMBER(self->peer, _number);
}
- (unsigned short)classNumber {
  return CLS_GETNUMBER(self->peer);
}

- (void)setClassVersion:(long)_version {
  class_set_version(self->peer, _version);
}
- (int)classVersion {
  return class_get_version(self->peer);
}
- (unsigned)instanceSize {
  return class_get_instance_size(self->peer);
}

#if LIB_FOUNDATION_BOEHM_GC
- (void *)gcObjectType {
  return class_get_gc_object_type(self->peer);
}
#endif

- (NSArray *)categories {
  return self->categories;
}
- (NSArray *)protocols {
  return self->protocols;
}

- (NSArray *)methods {
  if (self->methods)
    return self->methods;
  else if (self->peer->methods)
    return _makeMethods(self->peer->methods);
  else
    return nil;
}
- (NSArray *)classMethods {
  return [[self metaClassInfo] methods];
}

- (NSArray *)subclasses {
  NSMutableArray *subclasses = nil;
  Class          subclass    = NULL;

  for (subclass = self->peer->subclass_list;
       subclass;
       subclass = subclass->sibling_class) {

    if (subclasses == nil)
      subclasses = [[NSMutableArray allocWithZone:[self zone]] init];

    [subclasses addObject:[NGObjCClass infoForClass:subclass]];
  }

  return AUTORELEASE(subclasses);
}
- (BOOL)hasSubclasses {
  return self->peer->subclass_list ? YES : NO;
}

- (void)getInstanceVariables:(NSMutableDictionary *)_vars {
  if (![self isRootClass])
    [[self superClassInfo] getInstanceVariables:_vars];

  // load instance variables
  if ((self->peer->ivars != NULL) && (self->instanceVariables == NULL)) {
    struct objc_ivar_list *ivars = self->peer->ivars;
    int pos;
      
    if (ivars->ivar_count > 0) {
      self->instanceVariables =
        NSCreateMapTable(NSObjectMapKeyCallBacks,
                         NSObjectMapValueCallBacks,
                         ivars->ivar_count);
    }

    for (pos = 0; pos < ivars->ivar_count; pos++) {
      NGObjCInstanceVariable *ivar = nil;
      Ivar_t internalIvar = &(ivars->ivar_list[pos]);

      ivar = [[NGObjCInstanceVariable allocWithZone:[self zone]]
                                      initWithClass:self
                                      internalRepresentation:internalIvar];
      NSMapInsert(self->instanceVariables,
                  [NSString stringWithCString:internalIvar->ivar_name],
                  ivar);
      RELEASE(ivar); ivar = nil;
    }
  }
  
  if (self->instanceVariables) {
    NSMapEnumerator        vars;
    NSString               *name = NULL;
    NGObjCInstanceVariable *ivar = nil;

    vars = NSEnumerateMapTable(self->instanceVariables);

    while (NSNextMapEnumeratorPair(&vars, (void **)&name, (void **)&ivar))
      [_vars setObject:ivar forKey:[ivar variableName]];
  }
}
- (NSDictionary *)instanceVariables {
  NSMutableDictionary *vars = [[NSMutableDictionary alloc] init];
  [self getInstanceVariables:vars];
  return AUTORELEASE(vars);
}

#define IVAR_SIZE_ROUND(V, A) \
  ({ typeof(V) __v=(V); typeof(A) __a=(A); \
     __a*((__v+__a-1)/__a); })
+(Class)createClassWithName:(NSString*)_name
			 superClassName:(NSString*)_superName
					  iVars:(NSArray*)_iVars
{
  Class newClass=NULL; 
  Class classSuperClass=NSClassFromString(_superName);
  if (classSuperClass)
	{
	  int ivarsize=0;
	  newClass = objc_calloc(2,sizeof(struct objc_class));
	  memset(newClass,0,sizeof(struct objc_class)*2);
	  newClass->class_pointer = &newClass[1];
	  newClass->super_class = (Class)strdup([_superName cString]);
	  newClass->class_pointer->super_class = classSuperClass;
	  newClass->name = strdup([_name cString]);
	  newClass->class_pointer->name = strdup([_name cString]);
	  newClass->version = 0;
	  newClass->class_pointer->version = 0;
	  newClass->info = _CLS_CLASS;
	  newClass->class_pointer->info = _CLS_META;
	  ivarsize = classSuperClass->instance_size;
	  if ([_iVars count])
		{
		  struct objc_ivar* ivar=NULL;
		  int iVarIndex=0;
		  int iVarsCount=[_iVars count];
		  newClass->ivars = (struct objc_ivar_list*)objc_malloc(sizeof(struct objc_ivar_list) + (iVarsCount-1)*sizeof(struct objc_ivar));
		  memset(newClass->ivars,0,sizeof(struct objc_ivar_list) + (iVarsCount-1)*sizeof(struct objc_ivar));
		  newClass->ivars->ivar_count = iVarsCount;
		  ivar = newClass->ivars->ivar_list;
		  for (iVarIndex=0;iVarIndex<iVarsCount;iVarIndex++)
			{
			  int	align=0;
			  NGObjCInstanceVariable* iVarObject=[_iVars objectAtIndex:iVarIndex];
			  ivar->ivar_name = strdup([[iVarObject variableName]cString]);
			  ivar->ivar_type = strdup([[iVarObject variableType]cString]);
			  align = objc_alignof_type(ivar->ivar_type); // pad to alignment
			  ivarsize = IVAR_SIZE_ROUND(ivarsize, align);
			  ivar->ivar_offset = ivarsize;
			  ivarsize += objc_sizeof_type(ivar->ivar_type);
			  ivar++;
			};
		};
	  newClass->instance_size = ivarsize;
	  newClass->class_pointer->instance_size = classSuperClass->class_pointer->instance_size;
	};
  return newClass;
};


-(BOOL)addMethods:(NSArray*)_methods
{
  return [self addMethods:_methods
			   isInstanceMethods:YES];
};

-(BOOL)addClassMethods:(NSArray*)_methods
{
  return [self addMethods:_methods
			   isInstanceMethods:NO];
};

extern objc_mutex_t __objc_runtime_mutex;
-(BOOL)addMethods:(NSArray*)_methods
isInstanceMethods:(BOOL)_instance
{
    MethodList	*methodList = NULL;
    NSAutoreleasePool	*arp=nil;
    BOOL	ok = YES;
	NGObjCMethod* method=nil;
    arp = [NSAutoreleasePool new];
    NS_DURING
      {
		int count = [_methods count];
		if (count > 0)
		  {
			int	extra = sizeof(struct objc_method) * (count - 1);
			int methodIndex=0;
			methodList = objc_calloc(1, sizeof(MethodList) + extra);
			memset(methodList,0,sizeof(MethodList) + extra);
			methodList->method_count = count;
			for(methodIndex=0;methodIndex<count;methodIndex++)
			  {
				char	*types = NULL;
				char    *name=NULL;
				method=[_methods objectAtIndex:methodIndex];
				types = strdup([[method methodTypes] cString]);
				name = strdup([[method methodName] cString]);
				methodList->method_list[methodIndex].method_name=(SEL)name;
				methodList->method_list[methodIndex].method_types=types;
				if (_instance)
				  methodList->method_list[methodIndex].method_imp = [peer instanceMethodForSelector:[method selector]];
				else
				  methodList->method_list[methodIndex].method_imp = [peer methodForSelector:[method selector]];
			  };
		  };
	  }
    NS_HANDLER
      {
		ok = NO;
      }
    NS_ENDHANDLER;
    [arp release];

    if (ok)
	  {
		Class _class=peer;
		if (!_instance)
		  _class = (Class)peer->class_pointer;	/* Use meta class.	*/
		objc_mutex_lock(__objc_runtime_mutex);
		behavior_class_add_methods(_class, methodList);
		objc_mutex_unlock(__objc_runtime_mutex);
		if (_instance)
		  {
			//Force Reload of methods
			RELEASE(self->methods);
			self->methods    = nil;
		  }
		else
		  {
			//Force Reload of methods
			if ([self metaClassInfo]->methods)
			  {
				RELEASE([self metaClassInfo]->methods);
				[self metaClassInfo]->methods    = nil;
			  };
		  };
	  };
    return ok;
};

// description

- (NSDictionary *)propertyList {
  NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
  id tmp = nil;

  [dict setObject:[self className] forKey:@"name"];
  [dict setObject:[NSNumber numberWithInt:[self classVersion]] forKey:@"version"];
  [dict setObject:[NSNumber numberWithInt:[self classNumber]]  forKey:@"number"];
  [dict setObject:[NSNumber numberWithInt:[self instanceSize]]
        forKey:@"instanceSize"];

  if (![self isMetaClass]) {
    [dict setObject:[self metaClassName] forKey:@"metaClassName"];
    [dict setObject:@"NO" forKey:@"isMetaClass"];
  }
  else
    [dict setObject:@"YES" forKey:@"isMetaClass"];
  
  if (![self isRootClass]) {
    [dict setObject:[self superClassName] forKey:@"superClassName"];
    [dict setObject:@"NO" forKey:@"isRootClass"];
  }
  else
    [dict setObject:@"YES" forKey:@"isRootClass"];

  if ([self->categories count] > 0)
    [dict setObject:self->categories forKey:@"categories"];
  if ([self->protocols count] > 0)
    [dict setObject:self->protocols forKey:@"protocols"];
  if ([self->methods count] > 0)
    [dict setObject:self->methods forKey:@"methods"];

  if ([(tmp = [self subclasses]) count] > 0)
    [dict setObject:tmp forKey:@"subclasses"];
  if ([(tmp = [self instanceVariables]) count] > 0)
    [dict setObject:tmp forKey:@"ivars"];

  return AUTORELEASE(dict);
}

- (NSString *)description {
  NSMutableString *d = [[NSMutableString alloc] initWithCapacity:80];
  
  [d appendFormat:@"<%s[0x%08X]: ", self->isa->name, (unsigned)self];

  if ([self isRootClass])
    [d appendString:@"#R"];
  else if ([self isMetaClass])
    [d appendString:@"#M"];
  else
    [d appendString:@"#C"];
  [d appendFormat:@"[%03i] ", [self classNumber]];

  [d appendFormat:@"peer=0x%08X name=%@", (unsigned)self->peer, [self className]];

  if ([self isMetaClass]) {
    if (self->peer->class_pointer) {
      [d appendFormat:@" isa=%@<0x%08X>",
           [self metaClassName],
           (unsigned)self->peer->class_pointer];
    }
    
    [d appendFormat:@" super=%@<0x%08X>",
         [self superClassName],
         (unsigned)self->peer->super_class];
  }
  else {
    [d appendFormat:@" isa=%@", [self metaClassName]];
    if ([self superClassInfo])
      [d appendFormat:@" super=%@", [self superClassName]];
  }
  
  //[d appendFormat:@" info=0x%08X number=%i", self->peer->info, [self classNumber]];
  
  if (self->categories)
    [d appendFormat:@" categories=%@", self->categories];
  if (self->protocols)
    [d appendFormat:@" protocols=%@", self->protocols];
  //if (self->methods)
  //  [d appendFormat:@" methods=%@", self->methods];
  
  [d appendString:@">"];
  return AUTORELEASE(d);
}

@end
