// Copyright 2000 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#import "OFWeakRetainConcreteImplementation.h"

#import <Foundation/Foundation.h>
#import <objc/objc-class.h>
#import <OmniBase/OmniBase.h>

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OmniFoundation/OFWeakRetainConcreteImplementation.m,v 1.3 2000/10/28 00:50:58 kc Exp $")

@implementation OFWeakRetainConcreteImplementationHelper

#define INVALID_COUNT INT_MAX

- init;
{
    if (![super init])
        return nil;
    lock = [[NSLock alloc] init];
    return self;
}

- (void)dealloc;
{
    [lock release];
    [super dealloc];
}

// API

- (void)incrementWeakRetainCount;
{
    [lock lock];
    if (count != INVALID_COUNT)
        count++;
#ifdef DEBUG_WEAK_RETAIN
    NSLog(@"-[%@ incrementWeakRetainCount]: count=%d", OBShortObjectDescription(self), count);
#endif
    [lock unlock];
}

- (void)decrementWeakRetainCount;
{
    [lock lock];
    if (count != INVALID_COUNT)
        count--;
#ifdef DEBUG_WEAK_RETAIN
    NSLog(@"-[%@ decrementWeakRetainCount]: count=%d", OBShortObjectDescription(self), count);
#endif
    [lock unlock];
}

- (void)releaseReferencedObject:(id)referencedObject;
    // This releases the referenced object via -releaseFromWeakRetainHelper, and if the only remaining retains are due to weak retains it calls -invalidateWeakRetains
{
    NSException *raisedException = nil;
    BOOL shouldInvalidate;

    [self retain]; // Make sure we don't release our instance variables 'til we're done with them
    [lock lock];
    NS_DURING {
        shouldInvalidate = count != INVALID_COUNT && count != 0 && [referencedObject retainCount] - 1 == count;
#ifdef DEBUG_WEAK_RETAIN
        NSLog(@"-[%@ releaseReferencedObject:%@] (retainCount=%d, count=%d, shouldInvalidate=%@)", OBShortObjectDescription(self), OBShortObjectDescription(referencedObject), [referencedObject retainCount], count, shouldInvalidate ? @"YES" : @"NO");
#endif
        if (shouldInvalidate) {
            // Defer our release until after we've invalidated the weak retains, and make sure nobody else gets it into their heads to also call -invalidateWeakRetains
            count = INVALID_COUNT;
        } else {
            // Release within the lock so that if we switch threads we won't have two threads doing releases after both decided they didn't need to invalidate the object.
            [referencedObject releaseFromWeakRetainHelper];
        }
    } NS_HANDLER {
        raisedException = localException;
    } NS_ENDHANDLER;
    [lock unlock];
    [self release]; // We no longer need our instance variables

    if (raisedException != nil)
        [raisedException raise];

    if (shouldInvalidate) {
        [referencedObject invalidateWeakRetains];
        [referencedObject releaseFromWeakRetainHelper]; // OK, the object can go away now
    }
}

@end

@implementation NSObject (OFWeakRetain)

static NSMutableSet *warnedClasses = nil;

- (void)incrementWeakRetainCount;
    // Not thread-safe, but this is debugging code
{
    if (warnedClasses == nil)
        warnedClasses = [[NSMutableSet alloc] init];

    if (![warnedClasses containsObject:isa]) {
        [warnedClasses addObject:isa];
        NSLog(@"%@ does not implement the OFWeakRetain protocol", NSStringFromClass(isa));
    }
}

- (void)decrementWeakRetainCount;
{
}

@end
