/* 
   NGObjCModule.m

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

   Author: Helge Hess (helge@mdlink.de)
           Manuel Guesdon (mguesdon@sbuilders.com)

   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: NGObjCModule.m,v 1.1 2000/03/20 15:45:34 mguesdon Exp $

#include "NGReflectionCommonPrivate.h"
#include "NGObjCModule.h"
#include "NGObjCCategory.h"

NSString *NGObjCModuleDidExecuteNotificationName =
  @"NGObjCModuleDidExecuteNotification";

@interface NGObjCModule(PrivateMethods)

- (id)initWithName:(NSString *)_name;

@end

@implementation NGObjCModule

static NSMutableDictionary *nameToModule = nil;

+ (void)initialize {
  static BOOL isInitialized = NO;
  if (!isInitialized) {
    isInitialized = YES;
    nameToModule = [[NSMutableDictionary alloc] initWithCapacity:256];
  }
}

+ (id)moduleWithName:(NSString *)_name {
  NGObjCModule *module = nil;

  if (_name == nil)
    return nil;

  module = [nameToModule objectForKey:_name];
  if (module == nil) {
    module = [[self alloc] initWithName:_name];
    if (module) {
      [nameToModule setObject:module forKey:_name];
      RELEASE(module);
    }
  }
  return module;
}
+ (BOOL)doesModuleExist:(NSString *)_name {
  return [nameToModule objectForKey:_name] ? YES : NO;
}

- (id)initWithName:(NSString *)_name {
  if ((self = [super init])) {
    self->name    = RETAIN(_name);
    self->version = OBJC_VERSION;
  }
  return self;
}
- (id)init {
  return [self initWithName:nil];
}

- (void)dealloc {
  RELEASE(self->name);            self->name            = nil;
  RELEASE(self->classes);         self->classes         = nil;
  RELEASE(self->categories);      self->categories      = nil;
  RELEASE(self->staticInstances); self->staticInstances = nil;
  [super dealloc];
}

// accessors

- (NSString *)moduleName {
  return AUTORELEASE([self->name copy]);
}
- (unsigned long)moduleVersion {
  return self->version;
}

- (NSEnumerator *)moduleClasses {
  return [self->classes objectEnumerator];
}
- (NSEnumerator *)moduleCategories {
  return [self->categories objectEnumerator];
}

- (BOOL)wasExecuted {
  return (self->internalRepresentation != nil) ? YES : NO;
}

- (void)addClass:(Class)_class {
  if (self->classes == nil)
    self->classes = [[NSMutableArray allocWithZone:[self zone]] init];
  [self->classes addObject:_class];
}
- (void)addCategory:(NGObjCCategory *)_category {
  if (self->categories == nil)
    self->categories = [[NSMutableArray allocWithZone:[self zone]] init];
  [self->categories addObject:_category];
}

// adding to runtime

- (BOOL)executeWithClassArray:(Class *)_classes {
  Module_t module = NULL;

  if ([self wasExecuted]) {
    NSLog(@"module was already executed ..");
    return NO;
  }

  module = objc_malloc(sizeof(Module));
  NSCAssert(module, @"could not allocate Module structure ..");
  memset(module, 0, sizeof(Module));
  
  if (module) {
    unsigned long numberOfDefSlots;
    unsigned      numClassesBase = [self->classes         count];
    unsigned      numClasses     = 0;
    unsigned      numCategories  = [self->categories      count];
    unsigned      numStatics     = [self->staticInstances count];
    unsigned      numSupClasses  = 0;

    for (numSupClasses = 0;
         (_classes != NULL) && (_classes[numSupClasses]);
         numSupClasses++)
      ;
    numClasses = numClassesBase + numSupClasses;
    
    module->version = self->version;
    module->size    = sizeof(*module);
    module->name    = [self->name cString];

    numberOfDefSlots =  1; // trailing NULL
    numberOfDefSlots += numClasses;    // Class pointer
    numberOfDefSlots += numCategories; // Category pointer
    numberOfDefSlots += numStatics;    // Static instance pointer

    module->symtab = objc_malloc(sizeof(Symtab) +
                                 numberOfDefSlots * sizeof(void *));
    NSCAssert(module->symtab, @"could not allocate symbol table ..");
    if (module->symtab) {
      Symtab_t symtab = module->symtab;
      
      // references selectors
      symtab->sel_ref_cnt = 0;
      symtab->refs        = NULL; // ptr to array of 'struct objc_selector'
      
      symtab->cls_def_cnt = numClasses;
      symtab->cat_def_cnt = numCategories;

      { // fill class slots
        register int i;

        for (i = 0; i < numClasses; i++) {
          symtab->defs[i] = (i < numClassesBase)
              ? [self->classes objectAtIndex:i]
              : _classes[i - numClassesBase];
        }
      }
      { // fill category slots
        register int i;
        
        for (i = 0; i < numCategories; i++) {
          NGObjCCategory *category = [self->categories objectAtIndex:i];
          symtab->defs[numClasses + i] = [category internalRepresentation];
        }
      }
      { // fill static instances slots
        register int i, offset = numClasses + numCategories;

        for (i = 0; i < numStatics; i++) {
          id staticInfo = [self->staticInstances objectAtIndex:i];
          symtab->defs[offset + i] = [staticInfo internalRepresentation];
        }
      }

      symtab->defs[numberOfDefSlots - 1] = NULL;

      self->internalRepresentation = module;

      // now execute module
      {
        void __objc_exec_class(Module_t module); // is thread-safe

        __objc_exec_class(module);
        
        [[NSNotificationCenter defaultCenter]
                               postNotificationName:
                                 NGObjCModuleDidExecuteNotificationName
                               object:self];
      }
    }
  }
  return YES;
}

- (BOOL)execute {
  return [self executeWithClassArray:NULL];
}

// description

- (NSString *)description {
  return [NSString stringWithFormat:
                     @"<%@[0x%08X]: name=%@ version=%i classes=%@ categories=%@>",
                     [self class], (unsigned)self,
                     self->name, self->version,
                     self->classes,
                     self->categories
                   ];
}

@end
