/*
 * Copyright (c) 1999-2007 Apple Inc.  All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
	Object.m
	Copyright 1988-1996 NeXT Software, Inc.
*/

#include "objc-private.h"

#undef id
#undef Class

typedef struct objc_class *Class;
typedef struct objc_object *id;

#if __OBJC2__

@implementation Object

+ (id)initialize
{
    return self; 
}

+ (id)class
{
    return self;
}

-(id) retain
{
    return _objc_rootRetain(self);
}

-(void) release
{
    _objc_rootRelease(self);
}

-(id) autorelease
{
    return _objc_rootAutorelease(self);
}

+(id) retain
{
    return self;
}

+(void) release
{
}

+(id) autorelease
{
    return self;
}


@end


// __OBJC2__
#else
// not __OBJC2__

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <malloc/malloc.h>

#include "Object.h"
#include "Protocol.h"
#include "objc-runtime.h"


// Error Messages
static const char
	_errShouldHaveImp[] = "should have implemented the '%s' method.",
	_errShouldNotImp[] = "should NOT have implemented the '%s' method.",
	_errLeftUndone[] = "method '%s' not implemented",
	_errBadSel[] = "method %s given invalid selector %s",
	_errDoesntRecognize[] = "does not recognize selector %c%s";


@implementation Object


+ (id)initialize
{
	return self; 
}

- (id)awake
{
	return self; 
}

+ (id)poseAs: aFactory
{ 
	return class_poseAs(self, aFactory); 
}

+ (id)new
{
	id newObject = (*_alloc)((Class)self, 0);
	Class metaClass = self->ISA();
	if (class_getVersion(metaClass) > 1)
	    return [newObject init];
	else
	    return newObject;
}

+ (id)alloc
{
	return (*_zoneAlloc)((Class)self, 0, malloc_default_zone()); 
}

+ (id)allocFromZone:(void *) z
{
	return (*_zoneAlloc)((Class)self, 0, z); 
}

- (id)init
{
    return self;
}

- (const char *)name
{
	return class_getName(isa); 
}

+ (const char *)name
{
	return class_getName((Class)self); 
}

- (unsigned)hash
{
	return (unsigned)(((uintptr_t)self) >> 2);
}

- (BOOL)isEqual:anObject
{
	return anObject == self; 
}

- (id)free
{ 
	return (*_dealloc)(self); 
}

+ (id)free
{
	return nil; 
}

- (id)self
{
	return self; 
}


-(id)class
{
	return (id)isa; 
}

+ (id)class
{
	return self;
}

- (void *)zone
{
	void *z = malloc_zone_from_ptr(self);
	return z ? z : malloc_default_zone();
}

+ (id)superclass
{ 
	return self->superclass; 
}

- (id)superclass
{ 
	return isa->superclass; 
}

+ (int) version
{
	return class_getVersion((Class)self);
}

+ (id)setVersion: (int) aVersion
{
        class_setVersion((Class)self, aVersion);
	return self;
}

- (BOOL)isKindOf:aClass
{
	Class cls;
	for (cls = isa; cls; cls = cls->superclass) 
		if (cls == (Class)aClass)
			return YES;
	return NO;
}

- (BOOL)isMemberOf:aClass
{
	return isa == (Class)aClass;
}

- (BOOL)isKindOfClassNamed:(const char *)aClassName
{
	Class cls;
	for (cls = isa; cls; cls = cls->superclass) 
		if (strcmp(aClassName, class_getName(cls)) == 0)
			return YES;
	return NO;
}

- (BOOL)isMemberOfClassNamed:(const char *)aClassName
{
	return strcmp(aClassName, class_getName(isa)) == 0;
}

+ (BOOL)instancesRespondTo:(SEL)aSelector
{
	return class_respondsToMethod((Class)self, aSelector);
}

- (BOOL)respondsTo:(SEL)aSelector
{
	return class_respondsToMethod(isa, aSelector);
}

- (id)copy
{
	return [self copyFromZone: [self zone]];
}

- (id)copyFromZone:(void *)z
{
	return (*_zoneCopy)(self, 0, z); 
}

- (IMP)methodFor:(SEL)aSelector
{
	return class_lookupMethod(isa, aSelector);
}

+ (IMP)instanceMethodFor:(SEL)aSelector
{
	return class_lookupMethod(self, aSelector);
}

- (id)perform:(SEL)aSelector
{ 
	if (aSelector)
		return ((id(*)(id, SEL))objc_msgSend)(self, aSelector); 
	else
		return [self error:_errBadSel, sel_getName(_cmd), aSelector];
}

- (id)perform:(SEL)aSelector with:anObject
{
	if (aSelector)
		return ((id(*)(id, SEL, id))objc_msgSend)(self, aSelector, anObject); 
	else
		return [self error:_errBadSel, sel_getName(_cmd), aSelector];
}

- (id)perform:(SEL)aSelector with:obj1 with:obj2
{
	if (aSelector)
		return ((id(*)(id, SEL, id, id))objc_msgSend)(self, aSelector, obj1, obj2); 
	else
		return [self error:_errBadSel, sel_getName(_cmd), aSelector];
}

- (id)subclassResponsibility:(SEL)aSelector
{
	return [self error:_errShouldHaveImp, sel_getName(aSelector)];
}

- (id)notImplemented:(SEL)aSelector
{
	return [self error:_errLeftUndone, sel_getName(aSelector)];
}

- (id)doesNotRecognize:(SEL)aMessage
{
	return [self error:_errDoesntRecognize, 
		class_isMetaClass(isa) ? '+' : '-', sel_getName(aMessage)];
}

- (id)error:(const char *)aCStr, ... 
{
	va_list ap;
	va_start(ap,aCStr); 
	(*_error)(self, aCStr, ap); 
	_objc_error (self, aCStr, ap);	/* In case (*_error)() returns. */
	va_end(ap);
        return nil;
}

- (void) printForDebugger:(void *)stream
{
}

- (id)write:(void *) stream
{
	return self;
}

- (id)read:(void *) stream
{
	return self;
}

- (id)forward: (SEL) sel : (marg_list) args
{
    return [self doesNotRecognize: sel];
}

/* this method is not part of the published API */

- (unsigned)methodArgSize:(SEL)sel
{
    Method	method = class_getInstanceMethod((Class)isa, sel);
    if (! method) return 0;
    return method_getSizeOfArguments(method);
}

- (id)performv: (SEL) sel : (marg_list) args
{
    unsigned	size;

    // Messages to nil object always return nil
    if (! self) return nil;

    // Calculate size of the marg_list from the method's
    // signature.  This looks for the method in self
    // and its superclasses.
    size = [self methodArgSize: sel];

    // If neither self nor its superclasses implement
    // it, forward the message because self might know
    // someone who does.  This is a "chained" forward...
    if (! size) return [self forward: sel: args];

    // Message self with the specified selector and arguments
    return objc_msgSendv (self, sel, size, args); 
}

/* Testing protocol conformance */

- (BOOL) conformsTo: (Protocol *)aProtocolObj
{
  return [(id)isa conformsTo:aProtocolObj];
}

+ (BOOL) conformsTo: (Protocol *)aProtocolObj
{
  Class cls;
  for (cls = self; cls; cls = cls->superclass)
    {
      if (class_conformsToProtocol(cls, aProtocolObj)) return YES;
    }
  return NO;
}


/* Looking up information for a method */

- (struct objc_method_description *) descriptionForMethod:(SEL)aSelector
{
  Class cls;
  struct objc_method_description *m;

  /* Look in the protocols first. */
  for (cls = isa; cls; cls = cls->superclass)
    {
      if (cls->ISA()->version >= 3)
        {
	  struct objc_protocol_list *protocols = 
              (struct objc_protocol_list *)cls->protocols;
  
	  while (protocols)
	    {
	      int i;

	      for (i = 0; i < protocols->count; i++)
		{
		  Protocol *p = protocols->list[i];

		  if (class_isMetaClass(cls))
		    m = [p descriptionForClassMethod:aSelector];
		  else
		    m = [p descriptionForInstanceMethod:aSelector];

		  if (m) {
		      return m;
		  }
		}
  
	      if (cls->ISA()->version <= 4)
		break;
  
	      protocols = protocols->next;
	    }
	}
    }

  /* Then try the class implementations. */
    for (cls = isa; cls; cls = cls->superclass) {
        void *iterator = 0;
	int i;
        struct objc_method_list *mlist;
        while ( (mlist = class_nextMethodList( cls, &iterator )) ) {
            for (i = 0; i < mlist->method_count; i++)
                if (mlist->method_list[i].method_name == aSelector) {
		    m = (struct objc_method_description *)&mlist->method_list[i];
                    return m;
		}
        }
    }
  return 0;
}

+ (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSelector
{
  Class cls;

  /* Look in the protocols first. */
  for (cls = self; cls; cls = cls->superclass)
    {
      if (cls->ISA()->version >= 3)
        {
	  struct objc_protocol_list *protocols = 
              (struct objc_protocol_list *)cls->protocols;
  
	  while (protocols)
	    {
	      int i;

	      for (i = 0; i < protocols->count; i++)
		{
		  Protocol *p = protocols->list[i];
		  struct objc_method_description *m;

		  if ((m = [p descriptionForInstanceMethod:aSelector]))
		    return m;
		}
  
	      if (cls->ISA()->version <= 4)
		break;
  
	      protocols = protocols->next;
	    }
	}
    }

  /* Then try the class implementations. */
    for (cls = self; cls; cls = cls->superclass) {
        void *iterator = 0;
	int i;
        struct objc_method_list *mlist;
        while ( (mlist = class_nextMethodList( cls, &iterator )) ) {
            for (i = 0; i < mlist->method_count; i++)
                if (mlist->method_list[i].method_name == aSelector) {
		    struct objc_method_description *m;
		    m = (struct objc_method_description *)&mlist->method_list[i];
                    return m;
		}
        }
    }
  return 0;
}


/* Obsolete methods (for binary compatibility only). */

+ (id)superClass
{
	return [self superclass];
}

- (id)superClass
{
	return [self superclass];
}

- (BOOL)isKindOfGivenName:(const char *)aClassName
{
	return [self isKindOfClassNamed: aClassName];
}

- (BOOL)isMemberOfGivenName:(const char *)aClassName
{
	return [self isMemberOfClassNamed: aClassName];
}

- (struct objc_method_description *) methodDescFor:(SEL)aSelector
{
  return [self descriptionForMethod: aSelector];
}

+ (struct objc_method_description *) instanceMethodDescFor:(SEL)aSelector
{
  return [self descriptionForInstanceMethod: aSelector];
}

- (id)findClass:(const char *)aClassName
{
	return objc_lookUpClass(aClassName);
}

- (id)shouldNotImplement:(SEL)aSelector
{
	return [self error:_errShouldNotImp, sel_getName(aSelector)];
}


@end

#endif
