/* 
   Copyright (C) 1996 
	Ovidiu Predescu <ovidiu@bx.logicnet.ro>
	Mircea Oancea <mircea@jupiter.elcom.pub.ro>

   Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>

   This file is part of the FoundationExtensions library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; see the file COPYING.LIB.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <config.h>

#include <stdio.h>

#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSString.h>

#include <extensions/GCObject.h>
#include <extensions/GarbageCollector.h>
#include <extensions/objc-runtime.h>


@implementation GCObject

+ allocWithZone:(NSZone*)zone
{
    id newObject = [super allocWithZone:zone];
    [GarbageCollector addObject:newObject];
    ((GCObject*)newObject)->gcFlags.refCount = 1;
    return newObject;
}

- copyWithZone:(NSZone*)zone
{
    id newObject = NSCopyObject(self, 0, zone);
    [GarbageCollector addObject:newObject];
    ((GCObject*)newObject)->gcFlags.refCount = 1;
    return newObject;
}

- (oneway void)release
{
#if LIB_FOUNDATION_VERSION
    extern BOOL __autoreleaseEnableCheck;

    // check if retainCount is Ok
    if (__autoreleaseEnableCheck) {
	int toCome = [NSAutoreleasePool autoreleaseCountForObject:self];
	if (toCome && toCome + 1 > [self retainCount]) {
	    NSLog(@"Release[%p] release check for object has %d references and %d pending calls to release in autorelease pools\n", 
		  self, [self retainCount], toCome);
	    return;
	}
    }
#endif /* LIB_FOUNDATION_VERSION */

    if(gcFlags.refCount > 0 && --gcFlags.refCount == 0) {
	[GarbageCollector objectWillBeDeallocated:self];
	[self dealloc];
    }
}

- retain
{
    gcFlags.refCount++;
    return self;
}

- (unsigned int)retainCount
{
    return gcFlags.refCount;
}

- gcSetNextObject:(id)anObject
{
    gcNextObject = anObject;
    return self;
}

- gcSetPreviousObject:(id)anObject
{
    gcPreviousObject = anObject;
    return self;
}

- (id)gcNextObject		{ return gcNextObject; }
- (id)gcPreviousObject		{ return gcPreviousObject; }

- (BOOL)gcAlreadyVisited
{
    return gcFlags.gcVisited;
}

- (void)gcSetVisited:(BOOL)flag
{
    gcFlags.gcVisited = flag;
}

- (void)gcDecrementRefCountOfContainedObjects
{
}

- (BOOL)gcIncrementRefCountOfContainedObjects
{
    if(gcFlags.gcVisited)
	return NO;
    gcFlags.gcVisited = YES;
    return YES;
}

- (BOOL)isGarbageCollectable
{
    return YES;
}

- (void)gcIncrementRefCount
{
   gcFlags.refCount++;
}

- (void)gcDecrementRefCount
{
   gcFlags.refCount--;
}

+ error:(const char *)aString, ...
{
    va_list ap;

    va_start(ap, aString);
    vfprintf(stderr, aString, ap);
    va_end(ap);
    return self;
}

- doesNotRecognize:(SEL)aSelector
{
    return [isa error:"%s does not recognize selector %s\n",
	    object_get_class_name(self),
	    sel_get_name(aSelector)];
}

@end /* GCObject */

@implementation NSObject (GarbageCollecting)

- (BOOL)isGarbageCollectable
{
    return NO;
}

@end /* NSObject (GarbageCollecting) */
