// OSPArrayFaultSuppressor.m
// Created by Patrick Middleton on 06-Nov-2006.
// Copyright (c) 2006-7 OneStep Solutions Plc. All rights reserved.

#import <EOAccess/EOAccess.h>
#import "OSPArrayFaultSuppressorRecord.h"
#import "OSPArrayFaultSuppressor.h"

@implementation OSPArrayFaultSuppressor

static BOOL debugEnabled = NO;

+ (void)initialize {
    BOOL aBool = [[NSUserDefaults standardUserDefaults] boolForKey:@"OSPArrayFaultSuppressorDebug"];

    [self setDebugEnabled:aBool];
    if (aBool==NO && [EOAdaptorContext debugEnabledDefault]) NSLog(@"%@ debugging not enabled -- pass -OSPArrayFaultSuppressorDebug YES", NSStringFromClass([self class]));
}

+ (BOOL)debugEnabled { return debugEnabled; }
+ (void)setDebugEnabled:(BOOL)flag { debugEnabled = (flag ? YES : NO); }

- (id)init {
    [super init];

    arrayFaultSuppressorRecordArray = [[NSMutableArray alloc] init];

    return self;
}

- (void)dealloc {
    [arrayFaultSuppressorRecordArray release];

    [super dealloc];
}

- (OSPArrayFaultSuppressorRecord *)suppressMaster:(id)masterObject detail:(id)detailObject masterDetailName:(NSString *)masterDetailName detailToMasterName:(NSString *)detailToMasterName {
    OSPArrayFaultSuppressorRecord *arrayFaultSuppressorRecord = [[[OSPArrayFaultSuppressorRecord alloc] init] autorelease];

    [arrayFaultSuppressorRecord setMasterObject:masterObject];
    [arrayFaultSuppressorRecord setMasterDetailName:masterDetailName];
    [arrayFaultSuppressorRecord setDetailObject:detailObject];
    [arrayFaultSuppressorRecord setDetailToMasterName:detailToMasterName];
    [arrayFaultSuppressorRecord setTripped:NO];
    [arrayFaultSuppressorRecord configure];

    [arrayFaultSuppressorRecordArray addObject:arrayFaultSuppressorRecord];
    return arrayFaultSuppressorRecord;
}

- (NSArray *)objectsToRefaultInEditingContext:(EOEditingContext *)anEc arrayFaultSuppressorRecordArray:(NSMutableArray *)recordArray {
    NSMutableArray *returnValue = [NSMutableArray array];
    NSArray *insertedObjects = [anEc insertedObjects];
    NSArray *updatedObjects = [anEc updatedObjects];
    NSArray *deletedObjects = [anEc deletedObjects];
    int i;
    
    for (i=[arrayFaultSuppressorRecordArray count]-1; i>=0; i--) {
        OSPArrayFaultSuppressorRecord *record = (OSPArrayFaultSuppressorRecord *)[arrayFaultSuppressorRecordArray objectAtIndex:i];
        if ([record editingContext]==anEc && [record tripped]) {
            id masterObject = [record masterObject];
            if (masterObject && ![returnValue containsObject:masterObject]
                && ![insertedObjects containsObject:masterObject]
                && ![updatedObjects containsObject:masterObject]
                && ![deletedObjects containsObject:masterObject]) {
                [returnValue addObject:masterObject];
                if (recordArray) [recordArray addObject:record];
            }
            }
    }

    return returnValue;
}

- (NSArray *)objectsToRefaultInEditingContext:(EOEditingContext *)anEc {
    return [self objectsToRefaultInEditingContext:anEc arrayFaultSuppressorRecordArray:nil];
}

- (void)refaultObjectsInEditingContext:(EOEditingContext *)anEc {
    NSMutableArray *recordArray = [NSMutableArray array];
    NSArray *array = [self objectsToRefaultInEditingContext:anEc arrayFaultSuppressorRecordArray:recordArray];
    NSMutableArray *gidArray = [NSMutableArray array];
    int i;

    for (i=[array count]-1; i>=0; i--) {
        id object = [array objectAtIndex:i];
        EOGlobalID *gid = [anEc globalIDForObject:object];

        [anEc refaultObject:object withGlobalID:gid editingContext:anEc];
    }
    for (i=[recordArray count]-1; i>=0; i--) {
        OSPArrayFaultSuppressorRecord *record = (OSPArrayFaultSuppressorRecord *)[recordArray objectAtIndex:i];
        [record setTripped:NO];
    }

    [anEc invalidateObjectsWithGlobalIDs:gidArray];
}

- (Class)targetClassFor:(id)object {
    Class returnValue = Nil;

    if ([EOFault isFault:object]) returnValue = [EOFault targetClassForFault:object];
    else returnValue = [object class];
    
    return returnValue;
}

- (NSArray *)objectsForQualifier:(EOQualifier *)qualifier usingArrayFaultSuppressorRecord:(OSPArrayFaultSuppressorRecord *)record {
    NSArray *returnValue = nil;
    id object = [record detailObject];

    if (object) {
        EOEditingContext *ec = [record editingContext];
        Class targetClass = [self targetClassFor:object];
        NSMutableArray *candidates = [NSMutableArray array];
        NSArray *registeredObjects = [ec registeredObjects];
        int i;

        if ([OSPArrayFaultSuppressor debugEnabled]) NSLog(@"-[%@ %@] %s:%d object is %@, targetClass=%@", [self description], NSStringFromSelector(_cmd), __FILE__, __LINE__,
                                                          [object eoShallowDescription], targetClass ? NSStringFromClass(targetClass) : @"nil");
        for (i=[registeredObjects count]-1; i>=0; i--) {
            id anObject = [registeredObjects objectAtIndex:i];
            if (targetClass==[self targetClassFor:anObject]) [candidates addObject:anObject];
        }
        if ([candidates count]) returnValue = [candidates filteredArrayUsingQualifier:qualifier];
    } else {
        if ([OSPArrayFaultSuppressor debugEnabled]) NSLog(@"-[%@ %@] %s:%d object is nil", [self description], NSStringFromSelector(_cmd), __FILE__, __LINE__);
    }
    if (!returnValue) returnValue = [NSArray array];
    if ([OSPArrayFaultSuppressor debugEnabled]) NSLog(@"-[%@ %@] %s:%d returning %@", [self description], NSStringFromSelector(_cmd), __FILE__, __LINE__, [returnValue description]);
    return returnValue;
}

- (NSArray *)editingContext:(EOEditingContext *)editingContext shouldFetchObjectsDescribedByFetchSpecification:(EOFetchSpecification *)fetchSpecification {
    NSArray *returnValue = nil;

    EOQualifier *qualifier = [fetchSpecification qualifier];

    if (qualifier && [qualifier isKindOfClass:[EOKeyValueQualifier class]] && [(EOKeyValueQualifier *)qualifier selector]==EOQualifierOperatorEqual) {
        NSString *entityName = [fetchSpecification entityName];
        NSString *key = [(EOKeyValueQualifier *)qualifier key];
        id value = [(EOKeyValueQualifier *)qualifier value];
        int i;

        for (i=[arrayFaultSuppressorRecordArray count]-1; i>=0 && returnValue==nil; i--) {
            OSPArrayFaultSuppressorRecord *record = (OSPArrayFaultSuppressorRecord *)[arrayFaultSuppressorRecordArray objectAtIndex:i];
            if (editingContext==[record editingContext] && [entityName isEqualToString:[record detailEntityName]]
                && value==[record masterObject] && [key isEqualToString:[record detailToMasterName]]) {
                [record setTripped:YES];
                returnValue = [self objectsForQualifier:qualifier usingArrayFaultSuppressorRecord:record];
            }
        }
    }
    if ([OSPArrayFaultSuppressor debugEnabled]) NSLog(@"-[%@ %@] %s:%d returning %@", [self description], NSStringFromSelector(_cmd), __FILE__, __LINE__, [returnValue description]);
    return returnValue;
}

- (NSArray *)arrayFaultSuppressorRecordArray { return [NSArray arrayWithArray:arrayFaultSuppressorRecordArray]; }
- (void)addArrayFaultSuppressorRecord:(OSPArrayFaultSuppressorRecord *)aRecord {
    if (![arrayFaultSuppressorRecordArray containsObject:aRecord]) {
        [arrayFaultSuppressorRecordArray addObject:aRecord];
        [aRecord setTripped:NO];
    }
}

- (void)removeArrayFaultSuppressorRecord:(OSPArrayFaultSuppressorRecord *)aRecord { [arrayFaultSuppressorRecordArray removeObject:aRecord]; }

@end
