// HSEOUtilities
// Created by howie@rivers on 1999-11-28
// match a given array of values on a to-many key.
#import "HSEOUtilities.h"

#import <EOAccess/EOUtilities.h>
#import <EOControl/EOFault.h>

@implementation EOEditingContext (HSEOUtilities)
- (NSArray *)objectsMatchingAllValues: (NSArray *)values         // match ALL of these values
                               forKey: (NSString *)key           // for this key
                          entityNamed: (NSString *)entityName    // on this entity
{
	int i=0, numMatches=0;
	int valueCount=[values count];
	EOQualifier *andQualif;
	NSMutableArray *qualifiers = [NSMutableArray array];
	NSArray *matches;
	
	// trigger a fetch on the first value
	matches = [self objectsMatchingValue:[values objectAtIndex:0] forKey:key entityNamed:entityName];
	
	for( i=1; i<valueCount; i++ )
	{
		[qualifiers addObject: [[[EOKeyValueQualifier alloc] initWithKey:key
			operatorSelector:EOQualifierOperatorContains
			value:[values objectAtIndex:i]] autorelease]];
	}
	
	andQualif = [[[EOAndQualifier alloc] initWithQualifierArray:qualifiers] autorelease];
	return [matches filteredArrayUsingQualifier:andQualif];
}

//---------------------------------------------------------------------------------
- (NSArray *)objectsMatchingValues: (NSArray *)values         // match these values
                            forKey: (NSString *)key           // for this key
                       entityNamed: (NSString *)entityName    // on this entity
                   requiredMatches: (int)minMatches           // must match at least # values
{
	int i=0, numMatches=0;
	int valueCount=[values count];
	NSArray *matches;
	NSMutableArray *returnArray = [NSMutableArray array];
	
	// trigger a fetch on the first value
	matches = [self objectsMatchingValue:[values objectAtIndex:0] forKey:key entityNamed:entityName];
	numMatches = [matches count];
	
	// cycle through the fetched objects and search for matches
	for( i=0; i < numMatches; i++ )
	{
		int v=0;
		int yesMatches=1, noMatches=0;
		NSArray *matchArray = [[matches objectAtIndex:i] valueForKey:key];
		
		for( v=1; v<valueCount; v++ )
		{
			if( [matchArray containsObject:[values objectAtIndex:v]] )
			{
				yesMatches++;
			}
			else
			{
				noMatches++;
			}
		}

		// if we match everything, we're good to go.
		if( noMatches == 0 || yesMatches >= minMatches )
		{
			[returnArray addObject:[matches objectAtIndex:i]];
		}
	}
	
	return returnArray;
}

- (void)invalidateObjectsOfClass:(Class)aClass
{
	int c=[[self registeredObjects] count];
	int i=0;
	NSMutableArray *objectsToInvalidate = [NSMutableArray array];
	
	for( i=0; i<c; i++ )
	{
		id object = [[self registeredObjects] objectAtIndex:i];
		
		// dont trigger a fault when invalidating since that's just stupid.
		if( ![EOFault isFault:object] && [object isMemberOfClass:aClass] )
		{
			[objectsToInvalidate addObject:[self globalIDForObject:object]];
		}
	}
	
	[self invalidateObjectsWithGlobalIDs:objectsToInvalidate];
}

@end

@implementation EOQualifier (HSEOUtilities)
// goes along with the other HSCheckboxMatrix convenience methods...
+ (EOQualifier *)andQualifierToMatchAllValues: (NSArray *)values forKey:(NSString *)key
{
    NSMutableArray *qualifiers = [NSMutableArray array];
    int i=0;
    int c=[values count];

    for( i=0; i<c; i++ )
    {
        EOQualifier *qual = [[[EOKeyValueQualifier alloc] initWithKey:key
            operatorSelector:EOQualifierOperatorContains
            value:[values objectAtIndex:i] autorelease]];

        [qualifiers addObject:qual];
    }

    return [[[EOAndQualifier alloc] initWithQualifierArray:qualifiers] autorelease];
}
@end

@implementation NSObject (HSMultipleObjects)
- (void)removeObjects: (NSArray *)objects fromBothSidesOfRelationshipWithKey: (NSString *)key
{
	int i=0;
	int c=[objects count];
	
	for( i=0; i<c; i++ )
	{
		[self removeObject: [objects objectAtIndex:i] fromBothSidesOfRelationshipWithKey:key];
	}
}

- (void)addObjects: (NSArray *)objects toBothSidesOfRelationshipWithKey: (NSString *)key
{
	int i=0;
	int c=[objects count];
	
	for( i=0; i<c; i++ )
	{
		[self addObject: [objects objectAtIndex:i] toBothSidesOfRelationshipWithKey:key];
	}
}

- (void)invalidateObjectInEditingContext
{
    if( [self editingContext] )
    {
	// we're not passed an editingcontext due to this object possibly residing in a diff ec.
        [[self editingContext] invalidateObjectsWithGlobalIDs:[NSArray arrayWithObjects:[[self editingContext] globalIDForObject:self], nil]];
    }
}
@end

@implementation NSArray (HSMultipleObjects)
- (void)addObject: (id)object toBothSidesOfRelationshipWithKey:(NSString *)key
{
	int i=0;
	int c=[self count];
	
	for( i=0; i<c; i++ )
	{
		[[self objectAtIndex:i] addObject:object toBothSidesOfRelationshipWithKey:key];
	}
	
}

- (void)addObjects: (NSArray *)objects toBothSidesOfRelationshipWithKey:(NSString *)key
{
	int i=0;
	int c=[self count];
	
	for( i=0; i<c; i++ )
	{
		[[self objectAtIndex:i] addObjects:objects toBothSidesOfRelationshipWithKey:key];
	}
}

- (void)removeObjects: (NSArray *)objects fromBothSidesOfRelationshipWithKey: (NSString *)key
{
	int i=0;
	int c=[self count];

	for( i=0; i<c; i++ )
	{
		[[self objectAtIndex:i] removeObjects:objects fromBothSidesOfRelationshipWithKey:key];
	}
}

- (void)removeObject: (id)object fromBothSidesOfRelationshipWithKey: (NSString *)key
{
	int i=0;
	int c=[self count];

	for( i=0; i<c; i++ )
	{
		[[self objectAtIndex:i] removeObject:object fromBothSidesOfRelationshipWithKey:key];
	}
}

- (void)invalidateObjectsInEditingContext
{
   int i=0;
   int c=[self count];
   
   for( i=0; i<c; i++ )
   {
       // we're not passed an editing context for A Good Reason: these objects may be in different ec's.
       [[self objectAtIndex:i] invalidateObjectInEditingContext];
   }
}
@end

@implementation NSNumber (HSBoolExtension)
+ (NSNumber *)numberWithYesNoValue: (NSString *)aValue
{
	if( aValue )
	{
		char *stringValue = [aValue cString];
		
		if( stringValue[0] == 'Y' || stringValue[0] == 'y' )
		{
			return [NSNumber numberWithBool:YES];
		}
		else
		{
			return [NSNumber numberWithBool:NO];
		}
		
	}
	return [NSNumber numberWithBool:NO];
}
@end
