/*
FoundationEx.m

Author: Makoto Kinoshita

Copyright 2004 The Shiira Project. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions 
  and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of 
  conditions and the following disclaimer in the documentation and/or other materials provided 
  with the distribution.

THIS SOFTWARE IS PROVIDED BY THE SHIIRA PROJECT ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE SHIIRA PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/

#import "FoundationEx.h"

@implementation NSArray (Recursively)

- (unsigned)_indexOfObjectRecursively:(id)object
{
    unsigned    index;
    index = [self indexOfObject:object];
    if (index != NSNotFound) {
        return index;
    }
    
    // Serach object
    NSEnumerator*   enumerator;
    id              obj;
    enumerator = [self objectEnumerator];
    while (obj = [enumerator nextObject]) {
        // For array
        if ([obj isKindOfClass:[NSMutableArray class]]) {
            index = [obj _indexOfObjectRecursively:object];
            if (index != NSNotFound) {
                return index;
            }
            
            continue;
        }
        
        // For dictionary
        if ([obj isKindOfClass:[NSDictionary class]]) {
            // Find array
            NSEnumerator*   objectEnumerator;
            id              objectInDict;
            objectEnumerator = [obj objectEnumerator];
            while (objectInDict = [objectEnumerator nextObject]) {
                if ([objectInDict isKindOfClass:[NSMutableArray class]]) {
                    index = [objectInDict _indexOfObjectRecursively:object];
                    if (index != NSNotFound) {
                        return index;
                    }
                }
            }
            
            continue;
        }
    }
    
    return NSNotFound;
}

- (unsigned)indexOfObjectRecursively:(id)object
{
    return [self _indexOfObjectRecursively:object];
}

- (unsigned)_indexOfObjectRecursivelyIdenticalTo:(id)object
{
    unsigned    index;
    index = [self indexOfObjectIdenticalTo:object];
    if (index != NSNotFound) {
        return index;
    }
    
    // Serach object
    NSEnumerator*   enumerator;
    id              obj;
    enumerator = [self objectEnumerator];
    while (obj = [enumerator nextObject]) {
        // For array
        if ([obj isKindOfClass:[NSMutableArray class]]) {
            index = [obj _indexOfObjectRecursivelyIdenticalTo:object];
            if (index != NSNotFound) {
                return index;
            }
            
            continue;
        }
        
        // For dictionary
        if ([obj isKindOfClass:[NSDictionary class]]) {
            // Find array
            NSEnumerator*   objectEnumerator;
            id              objectInDict;
            objectEnumerator = [obj objectEnumerator];
            while (objectInDict = [objectEnumerator nextObject]) {
                if ([objectInDict isKindOfClass:[NSMutableArray class]]) {
                    index = [objectInDict _indexOfObjectRecursivelyIdenticalTo:object];
                    if (index != NSNotFound) {
                        return index;
                    }
                }
            }
            
            continue;
        }
    }
    
    return NSNotFound;
}

- (unsigned)indexOfObjectRecursivelyIdenticalTo:(id)object
{
    return [self _indexOfObjectRecursivelyIdenticalTo:object];
}

@end

#pragma mark -

@implementation NSCharacterSet (NewLine)

+ (NSCharacterSet*)newLineCharacterSet
{
    static NSCharacterSet*  _newlineCharacterSet = nil;
    if (!_newlineCharacterSet) {
        unichar     newlineChars[] = {0x000A, 0x000D, 0x0085};
        NSString*   newlineString;
        newlineString = [NSString stringWithCharacters:newlineChars 
                length:sizeof(newlineChars) / sizeof(unichar)];
        _newlineCharacterSet = [[NSCharacterSet characterSetWithCharactersInString:newlineString] retain];
    }
    
    return _newlineCharacterSet;
}

@end

#pragma mark -

@implementation NSDate (ReverseComparison)

- (NSComparisonResult)reverseCompare:(NSDate*)anotherDate
{
    // Compare with another date
    NSComparisonResult  result;
    result = [self compare:anotherDate];
    
    // Reverse result
    return result * -1;
}

@end

#pragma mark -

@implementation NSException (StackTrace)

//
// Refer to http://www.cocoadev.com/index.pl?StackTraces
//

- (void)printStackTrace
{
#if 0
// It requires NSExceptionHandler.framework
    NSString*       stack;
    NSTask*         task;
    NSString*       pid;
    NSMutableArray* args;
    stack = [[self userInfo] objectForKey:NSStackTraceKey];
    task = [[NSTask alloc] init];
    pid = [[NSNumber numberWithInt:getpid()] stringValue];
    args = [NSMutableArray arrayWithCapacity:20];
    
    [args addObject:@"-p"];
    [args addObject:pid];
    [args addObjectsFromArray:[stack componentsSeparatedByString:@"  "]];
    
    // Note: function addresses are separated by double spaces, not a single space.
    [task setLaunchPath:@"/usr/bin/atos"];
    [task setArguments:args];
    [task launch];
    
    [task release];
#endif
}

@end

#pragma mark -

@implementation NSFileManager (UniqueFilePath)

static int  _maxRepeatTime = 999;

- (NSString*)makeUniqueFilePath:(NSString*)filePath
{
    if (![self fileExistsAtPath:filePath]) {
        return filePath;
    }
    
    // Get file name and extension separately
    NSRange     range;
    NSString*   fileName;
    NSString*   extension;
    range = [filePath rangeOfString:@"."];  // Find first '.'
    if (range.location != NSNotFound) {
        fileName = [filePath substringToIndex:range.location];
        extension = [filePath substringFromIndex:range.location + 1];
    }
    else {
        fileName = filePath;
        extension = nil;
    }
    
    // Make new file path
    int i;
    for (i = 1; i < _maxRepeatTime; i++) {
        NSString*   newFilePath;
        if (extension) {
            newFilePath = [NSString stringWithFormat:@"%@-%d.%@", fileName, i, extension];
        }
        else {
            newFilePath = [NSString stringWithFormat:@"%@-%d", fileName, i];
        }
        
        if (![self fileExistsAtPath:newFilePath]) {
            return newFilePath;
        }
    }
    
    return nil;
}

@end

#pragma mark -

@implementation NSMutableArray (DeepCopy)

- (id)initWithArray:(NSArray*)otherArray copyItemsDeeply:(BOOL)flag
{
    if (!flag) {
        return [self initWithArray:otherArray];
    }
    
    // Create mutable array
    self = [self initWithArray:otherArray copyItems:flag];
    if (!self) {
        return nil;
    }
    
    // Check object class
    NSEnumerator*   enumerator;
    id              object;
    int             index;
    enumerator = [self objectEnumerator];
    while (object = [enumerator nextObject]) {
        // For array
        if ([object isKindOfClass:[NSArray class]]) {
            index = [self indexOfObject:object];
            
            // Copy deeply
            object = [[NSMutableArray alloc] initWithArray:object copyItemsDeeply:flag];
            [self replaceObjectAtIndex:index withObject:object];
            
            continue;
        }
        // For dictionary
        if ([object isKindOfClass:[NSDictionary class]]) {
            index = [self indexOfObject:object];
            
            // Copy deeply
            object = [[NSMutableDictionary alloc] initWithDictionary:object copyItemsDeeply:flag];
            [self replaceObjectAtIndex:index withObject:object];
            
            continue;
        }
    }
    
    return self;
}

- (void)insertObjectsFromArray:(NSArray*)otherArray 
        atIndex:(unsigned)index
{
    NSEnumerator*   enumerator;
    id              object;
    enumerator = [otherArray reverseObjectEnumerator];
    while (object = [enumerator nextObject]) {
        [self insertObject:object atIndex:index];
    }
}

@end

@implementation NSMutableArray (Recursively)

- (BOOL)_removeObjectRecursively:(id)object
{
    if ([self indexOfObject:object] != NSNotFound) {
        [self removeObject:object];
        return YES;
    }
    
    // Serach object
    NSEnumerator*   enumerator;
    id              obj;
    enumerator = [self objectEnumerator];
    while (obj = [enumerator nextObject]) {
        // For array
        if ([obj isKindOfClass:[NSMutableArray class]]) {
            if ([obj _removeObjectRecursively:object]) {
                return YES;
            }
            
            continue;
        }
        
        // For dictionary
        if ([obj isKindOfClass:[NSDictionary class]]) {
            // Find array
            NSEnumerator*   objectEnumerator;
            id              objectInDict;
            objectEnumerator = [obj objectEnumerator];
            while (objectInDict = [objectEnumerator nextObject]) {
                if ([objectInDict isKindOfClass:[NSMutableArray class]]) {
                    if ([objectInDict _removeObjectRecursively:object]) {
                        return YES;
                    }
                }
            }
            
            continue;
        }
    }
    
    return NO;
}

- (BOOL)_removeObjectRecursivelyIdenticalTo:(id)object
{
    if ([self indexOfObjectIdenticalTo:object] != NSNotFound) {
        [self removeObjectIdenticalTo:object];
        return YES;
    }
    
    // Serach object
    NSEnumerator*   enumerator;
    id              obj;
    enumerator = [self objectEnumerator];
    while (obj = [enumerator nextObject]) {
        // For array
        if ([obj isKindOfClass:[NSMutableArray class]]) {
            if ([obj _removeObjectRecursivelyIdenticalTo:object]) {
                return YES;
            }
            
            continue;
        }
        
        // For dictionary
        if ([obj isKindOfClass:[NSDictionary class]]) {
            // Find array
            NSEnumerator*   objectEnumerator;
            id              objectInDict;
            objectEnumerator = [obj objectEnumerator];
            while (objectInDict = [objectEnumerator nextObject]) {
                if ([objectInDict isKindOfClass:[NSMutableArray class]]) {
                    if ([objectInDict _removeObjectRecursivelyIdenticalTo:object]) {
                        return YES;
                    }
                }
            }
            
            continue;
        }
    }
    
    return NO;
}

- (void)removeObjectRecursively:(id)object
{
    [self _removeObjectRecursively:object];
}

- (void)removeObjectRecursivelyIdenticalTo:(id)object
{
    [self _removeObjectRecursivelyIdenticalTo:object];
}

- (void)removeObjectsFromArrayRecursively:(NSArray*)otherArray
{
    NSEnumerator*   enumerator;
    id              object;
    enumerator = [otherArray objectEnumerator];
    while (object = [enumerator nextObject]) {
        [self removeObjectRecursively:object];
    }
}

- (void)removeObjectsFromArrayRecursivelyIdenticalTo:(NSArray*)otherArray
{
    NSEnumerator*   enumerator;
    id              object;
    enumerator = [otherArray objectEnumerator];
    while (object = [enumerator nextObject]) {
        [self removeObjectRecursivelyIdenticalTo:object];
    }
}

@end

#pragma mark -

@implementation NSMutableDictionary (DeepCopy)

- (id)initWithDictionary:(NSDictionary*)otherDict copyItemsDeeply:(BOOL)flag
{
    if (!flag) {
        return [self initWithDictionary:otherDict];
    }
    
    // Create mutable dictionary
    self = [self initWithDictionary:otherDict copyItems:flag];
    if (!self) {
        return nil;
    }
    
    // Check object class
    NSEnumerator*   enumerator;
    id              key;
    enumerator = [self keyEnumerator];
    while (key = [enumerator nextObject]) {
        id  object;
        object = [self objectForKey:key];
        
        // For array
        if ([object isKindOfClass:[NSArray class]]) {
            // Copy deeply
            object = [[NSMutableArray alloc] initWithArray:object copyItemsDeeply:flag];
            [self setObject:object forKey:key];
            
            continue;
        }
        // For dictionary
        if ([object isKindOfClass:[NSDictionary class]]) {
            // Copy deeply
            object = [[NSMutableDictionary alloc] initWithDictionary:object copyItemsDeeply:flag];
            [self setObject:object forKey:key];
            
            continue;
        }
    }
    
    return self;
}

@end

#pragma mark -

@implementation NSString (Compare)

- (BOOL)isEqualToURLStringWithoutScheme:(NSString*)URLString
{
    // Compare only path
    NSRange     range;
    NSString*   pathA = self;
    range = [self rangeOfString:@"://"];
    if (range.location != NSNotFound) {
        pathA = [self substringFromIndex:range.location + range.length];
    }
    
    NSString*   pathB = URLString;
    range = [URLString rangeOfString:@"://"];
    if (range.location != NSNotFound) {
        pathB = [URLString substringFromIndex:range.location + range.length];
    }
    
    return [pathA isEqualToString:pathB];
}

@end
