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

#include "NGReflectionCommonPrivate.h"
#include "NGObjCInternals.h"
#include "NGObjCMethod.h"
#include "NGObjCCategory.h"
#import <objc/Protocol.h>
#import <Foundation/Foundation.h>

@interface NGObjCMethod(PrivateMethods)
- (id)initWithInternalRepresentation:(void *)_irep;
@end

MethodList_t _makeMethodList(NSArray *_methods) {
  MethodList_t methodList  = NULL;
  int          methodCount = [_methods count];

  if (methodCount == 0)
    return NULL;

  methodList = objc_malloc(sizeof(struct objc_method_list) +
                           sizeof(struct objc_method) * (methodCount - 1));
  NSCAssert(methodList, @"Could not get enough memory ..");
  if (methodList) {
    register int pos;
    
    methodList->method_next  = NULL;
    methodList->method_count = methodCount;

    for (pos = 0; pos < methodCount; pos++) {
      NGObjCMethod *method        = [_methods objectAtIndex:pos];
      NSString     *types         = [method methodTypes];
      Method_t     internalMethod = &(methodList->method_list[pos]);

      internalMethod->method_name = [method selector];

      internalMethod->method_types = objc_malloc([types cStringLength] + 1);
      NSCAssert(internalMethod->method_name, @"Could not get enough memory ..");
      [types getCString:(char *)internalMethod->method_types];

      internalMethod->method_imp = [method methodImplementation];
    }
  }

  return methodList;
}

struct objc_protocol_list *_makeProtocolList(NSArray *_protocols) {
  struct objc_protocol_list *protocols = NULL;
  int protocolCount = [_protocols count];

  if (protocolCount == 0)
    return NULL;

  protocols = objc_malloc(sizeof(struct objc_protocol_list) +
                          sizeof(Protocol) * (protocolCount - 1));
  NSCAssert(protocols, @"Could not get enough memory ..");
  if (protocols) {
    register int pos;

    protocols->next  = NULL;
    protocols->count = protocolCount;

    for (pos = 0; pos < protocolCount; pos++) {
      Protocol *protocol         = [_protocols objectAtIndex:pos];
      Protocol *internalProtocol = protocols->list[pos];

      memcpy(internalProtocol, protocols, sizeof(protocol));
    }
  }
  return protocols;
}

NSArray *_makeMethods(MethodList_t _methods) {
  NSMutableArray *methods = nil;

  if (_methods == NULL)
    return nil;

  methods = [[NSMutableArray alloc] initWithCapacity:_methods->method_count];

  while (_methods) {
    int i;

    for (i = 0; i < _methods->method_count; i++) {
      Method_t     internalMethod = &(_methods->method_list[i]);
      NGObjCMethod *method        = nil;

      method = [[NGObjCMethod allocWithZone:[methods zone]]
                              initWithInternalRepresentation:internalMethod];
      [methods addObject:method];
      RELEASE(method); method = nil;
    }

    _methods = _methods->method_next; // next method list
  }

  return AUTORELEASE(methods);
}

Category_t _makeCategory(NSString *_name, Class _class,
                         NSArray *_instanceMethods,
                         NSArray *_classMethods,
                         NSArray *_protocols) {
  Category_t category = NULL;

  category = objc_calloc(1, sizeof(Category_t));
  if (category) {
    category->category_name = objc_malloc([_name cStringLength] + 1);
    [_name getCString:(void *)category->category_name];
    
    category->class_name = _class->name;

    category->instance_methods = _makeMethodList(_instanceMethods);
    category->class_methods    = _makeMethodList(_classMethods);
    category->protocols        = _makeProtocolList(_protocols);
  }
  return category;
}

#ifdef LIB_FOUNDATION_BOEHM_GC
BOOL ivar_get_gcinvisible(Ivar_t _ivar) {
  const char *type = _ivar->ivar_type;

  NSCAssert(type, @"invalid type field in instance variable ..");

  if (*type == '"') { // skip var name
    for (type++; *type++ != '"';)
      ;
  }
  return (*type == _C_GCINVISIBLE) ? YES : NO;
}
#endif

NSString *NGTypeAsObjectiveCString(const char *_type) {
  switch (*_type) {
    case _C_ID: { // '@'
      const char *t = _type + 1;
      if (*t == '"') {
        const char *start = t + 1;
        do {
          t++;
        }
        while ((*t != '"') && (*t != '\0'));
        
        return [[NSString stringWithCString:start
                          length:(t - start)]
                          stringByAppendingString:@" *"];
      }
      else
        return @"id";
    }
      
    case _C_CLASS:    return @"Class";
    case _C_SEL:      return @"SEL";
    case _C_CHR:      return @"char";
    case _C_UCHR:     return @"unsigned char";
    case _C_SHT:      return @"short";
    case _C_USHT:     return @"unsigned short";
    case _C_INT:      return @"int";
    case _C_UINT:     return @"unsigned int";
    case _C_LNG:      return @"long";
    case _C_ULNG:     return @"unsigned long";
#ifdef _C_LNG_LNG
    case _C_LNG_LNG:  return @"long long";
#endif
#ifdef _C_ULNG_LNG
    case _C_ULNG_LNG: return @"unsigned long long";
#endif
    case _C_FLT:      return @"float";
    case _C_DBL:      return @"double";
    case _C_VOID:     return @"void";

    case _C_CHARPTR:  return @"char *";

    case _C_PTR:
      return [NSString stringWithFormat:@"%@ *",
                         NGTypeAsObjectiveCString(_type + 1)];

    case _C_STRUCT_B: {
      NSString *structName = nil;
      const char *t = _type + 1;
      
      if (*t == '?')
        structName = @"?";
      else {
        const char *beg = t;
        while ((*t != '=') && (*t != '\0') && (*t != _C_STRUCT_E))
          t++;
        structName = [NSString stringWithCString:beg length:(t - beg)];
      }

      return [NSString stringWithFormat:@"struct %@ {...}", structName];
    }
      
    default:
      return [NSString stringWithFormat:@"%s", _type];
  }
}
