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

#include "NGReflectionCommonPrivate.h"
#include "NGObjCInstanceVariable.h"
#include "NGObjCClass.h"
#include "NGObjCInternals.h"

static inline const char *skipVarName(const char *_type) {
  if (*_type == '"') { // skip var name
    for (_type++; *_type++ != '"';)
      ;
  }
  return _type;
}

@implementation NGObjCInstanceVariable

+ (id)ivarWithInternalRepresentation:(void *)_irep
  ownsRepresentation:(BOOL)_ownsRepresentation
{
  return AUTORELEASE([[self alloc] initWithInternalRepresentation:_irep
                                   ownsRepresentation:_ownsRepresentation]);
}
- (id)initWithInternalRepresentation:(void *)_irep
  ownsRepresentation:(BOOL)_ownsRepresentation
{
  if ((self = [super init])) {
    self->internalRepresentation = _irep;
    self->type = skipVarName(((Ivar_t)self->internalRepresentation)->ivar_type);
    self->ownsRepresentation = _ownsRepresentation;
  }
  return self;
}
- (id)initWithName:(NSString *)_name type:(NSString  *)_type {
  if ((self = [super init])) {
    self->internalRepresentation = objc_malloc(sizeof(Ivar_t));
    memset(self->internalRepresentation, 0, sizeof(Ivar_t));
    ((Ivar_t)self->internalRepresentation)->ivar_name = strdup([_name cString]);
    self->type = ((Ivar_t)self->internalRepresentation)->ivar_type =
      strdup([_type cString]);
    self->ownsRepresentation = YES;
  }
  return self;
}

- (id)init {
  RELEASE(self);
  NSLog(@"do not use init with NGObjCInstanceVariable !");
  return nil;
}

- (id)initWithClass:(NGObjCClass *)_class internalRepresentation:(void *)_irep {
  if ((self = [super init])) {
    self->class = RETAIN(_class);
    self->internalRepresentation = _irep;
    self->type = skipVarName(((Ivar_t)self->internalRepresentation)->ivar_type);
    self->ownsRepresentation = NO;
  }
  return self;
}

#if !LIB_FOUNDATION_BOEHM_GC
- (void)dealloc {
  if ((self->ownsRepresentation) && (self->internalRepresentation != NULL)) {
    objc_free(self->internalRepresentation);
    self->internalRepresentation = NULL;
  }
  RELEASE(self->class);
  [super dealloc];
}
#endif

// accessors

- (NSString *)variableName {
  return [NSString stringWithCString:
                     ((Ivar_t)self->internalRepresentation)->ivar_name];
}
- (NSString *)variableType {
  return [NSString stringWithCString:
                     ((Ivar_t)self->internalRepresentation)->ivar_type];
}
- (int)offset {
  return ((Ivar_t)self->internalRepresentation)->ivar_offset;
}

- (NGObjCClass *)variableClass {
  return self->class;
}

#ifdef LIB_FOUNDATION_BOEHM_GC
- (void)setGCVisibility:(BOOL)_visibility {
  class_ivar_set_gcinvisible([self->class peerClass],
                             ((Ivar_t)self->internalRepresentation)->ivar_name,
                             _visibility);
}
- (BOOL)isGCInvisible {
  return ivar_get_gcinvisible(self->internalRepresentation);
}
#endif

// ivar type

- (BOOL)isPrimitiveType {
  const char *t = objc_skip_type_qualifiers(self->type);

  switch (*t) {
    case _C_CHR:
    case _C_UCHR:
    case _C_SHT:
    case _C_USHT:
    case _C_INT:
    case _C_UINT:
    case _C_LNG:
    case _C_ULNG:
    case _C_FLT:
    case _C_DBL:
#ifdef _C_LNG_LNG
    case _C_LNG_LNG:
#endif
#ifdef _C_ULNG_LNG
    case _C_ULNG_LNG:
#endif
      return YES;
    
    default:
      return NO;
  }
}

- (BOOL)isPointerType {
  const char *t = objc_skip_type_qualifiers(self->type);

  switch (*t) {
    case _C_ID:
    case _C_CLASS:
    case _C_SEL:
    case _C_PTR:
    case _C_CHARPTR:
    case _C_ATOM:
      return YES;

    default:
      return NO;
  }
}
- (BOOL)isObjectType {
  const char *t = objc_skip_type_qualifiers(self->type);

  return ((*t == _C_ID) || (*t == _C_CLASS));
}
- (BOOL)isClassType {
  return (*(objc_skip_type_qualifiers(self->type)) == _C_CLASS);
}
- (NSString *)objectType {
  const char *t = objc_skip_type_qualifiers(self->type);

  if (*t == _C_ID) {
    t++;
    if (*t == '"') { // class name follows
      const char *start = t + 1;

      do {
        t++;
      }
      while ((*t != '"') && (*t != '\0'));

      return [NSString stringWithCString:start
                       length:(t - start)];
    }
  }
  return nil;
}

- (BOOL)isUnionType {
  const char *t = objc_skip_type_qualifiers(self->type);
  return (*t == _C_UNION_B);
}
- (BOOL)isStructureType {
  const char *t = objc_skip_type_qualifiers(self->type);
  return (*t == _C_STRUCT_B);
}
- (BOOL)isArrayType {
  const char *t = objc_skip_type_qualifiers(self->type);
  return (*t == _C_ARY_B);
}

// value

- (BOOL)setValue:(void *)_value inObject:(id)_object {
  const char *t = objc_skip_type_qualifiers(self->type);
  void *address;

  if (_object == nil) {
    // invalid object ..
    return NO;
  }

  address = (char *)_object + ((Ivar_t)self->internalRepresentation)->ivar_offset;
  NSAssert(address, @"invalid address for instance variable ..");

  switch (*t) {
    case _C_ID:       // '@'
      *(id *)address = *(id *)_value;
      break;
      
    case _C_CLASS:    // '#'
      *(Class *)address = *(Class *)_value;
      break;

    case _C_SEL:      // ':'
      *(SEL *)address = *(SEL *)_value;
      break;

    case _C_CHR:      // 'c'
    case _C_UCHR:     // 'C'
      *(char *)address = *(char *)_value;
      break;

    case _C_SHT:      // 's'
    case _C_USHT:     // 'S'
      *(unsigned short *)address = *(unsigned short *)_value;
      break;

    case _C_INT:      // 'i'
    case _C_UINT:     // 'I'
      *(unsigned int *)address = *(unsigned int *)_value;
      break;

    case _C_LNG:      // 'l'
    case _C_ULNG:     // 'L'
      *(unsigned long *)address = *(unsigned long *)_value;
      break;

#ifdef _C_LNG_LNG
    case _C_LNG_LNG:  // 'q'
      *(long long *)address = *(long long *)_value;
      break;
#endif
#ifdef _C_ULNG_LNG
    case _C_ULNG_LNG: // 'Q'
      *(unsigned long long *)address = *(unsigned long long *)_value;
      break;
#endif

    case _C_FLT:      // 'f'
      *(float *)address = *(float *)_value;
      break;

    case _C_DBL:      // 'd'
      *(double *)address = *(double *)_value;
      break;

    case _C_PTR:      // '^'
      *(void **)address = *(void **)_value;
      break;
      
    case _C_CHARPTR:  // '*'
      *(char **)address = *(char **)_value;
      break;

    default:
      NSLog(@"unsupported type ..");
      return NO;
  }
  return YES;
}
- (BOOL)getValue:(void *)_value inObject:(id)_object {
  const char *t = objc_skip_type_qualifiers(self->type);
  void *address;

  if (_object == nil) {
    // invalid object ..
    return NO;
  }

  address = (char *)_object + ((Ivar_t)self->internalRepresentation)->ivar_offset;
  NSAssert(address, @"invalid address for instance variable ..");

  switch (*t) {
    case _C_ID:       // '@'
      *(id *)_value = *(id *)address;
      break;
      
    case _C_CLASS:    // '#'
      *(Class *)_value = *(Class *)address;
      break;

    case _C_SEL:      // ':'
      *(SEL *)_value = *(SEL *)address;
      break;

    case _C_CHR:      // 'c'
    case _C_UCHR:     // 'C'
      *(char *)_value = *(char *)address;
      break;

    case _C_SHT:      // 's'
    case _C_USHT:     // 'S'
      *(unsigned short *)_value = *(unsigned short *)address;
      break;

    case _C_INT:      // 'i'
    case _C_UINT:     // 'I'
      *(unsigned int *)_value = *(unsigned int *)address;
      break;

    case _C_LNG:      // 'l'
    case _C_ULNG:     // 'L'
      *(unsigned long *)_value = *(unsigned long *)address;
      break;

#ifdef _C_LNG_LNG
    case _C_LNG_LNG:  // 'q'
      *(long long *)_value = *(long long *)address;
      break;
#endif
#ifdef _C_ULNG_LNG
    case _C_ULNG_LNG: // 'Q'
      *(unsigned long long *)_value = *(unsigned long long *)address;
      break;
#endif

    case _C_FLT:      // 'f'
      *(float *)_value = *(float *)address;
      break;

    case _C_DBL:      // 'd'
      *(double *)_value = *(double *)address;
      break;

    case _C_PTR:      // '^'
      *(void **)_value = *(void **)address;
      break;
      
    case _C_CHARPTR:  // '*'
      *(char **)_value = *(char **)address;
      break;

    default:
      NSLog(@"unsupported type ..");
      return NO;
  }
  return YES;
}

// ObjC

- (NSString *)typeAsObjectiveC {
  return NGTypeAsObjectiveCString(self->type);
}

// description

- (NSString *)description {
  NSMutableString *d = [[NSMutableString alloc] init];

  [d appendFormat:@"<%s[0x%08X]: name=%@", self->isa->name, (unsigned)self,
       [self variableName]];
  
  if ([self isObjectType]) {
    NSString *ot = [self objectType];

    ot = (ot == nil)
      ? (id)([self isClassType] ? @"Class" : @"id")
      : [ot stringByAppendingString:@" *"];
    [d appendFormat:@" type=(%@)", ot];
  }
  else
    [d appendFormat:@" type='%@'", [self variableType]];

  [d appendString:@">"];

  return AUTORELEASE(d);
}

@end
