//
// $Id: Iterator.m,v 1.30 2005/05/18 06:46:53 will_mason Exp $
//
// vi: set ft=objc:

/*
 * ObjectiveLib - a library of containers and algorithms for Objective-C
 *
 * Copyright (c) 2004
 * Will Mason
 *
 * Portions:
 *
 * Copyright (c) 1994
 * Hewlett-Packard Company
 *
 * Copyright (c) 1996,1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Copyright (c) 1997
 * Moscow Center for SPARC Technology
 *
 * Copyright (c) 1999 
 * Boris Fomitchev
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * You may contact the author at will_mason@users.sourceforge.net.
 */

#import "Iterator.h"
#import "Macros.h"
#import "Tree.h"
#import "HashTable.h"
#import <stddef.h>

extern OLHashTableNode* __bumpHashTableNode(OLHashTableNode*, OLHashTable*);

@implementation OLIterator

+ (void) advanceIterator: (OLIterator*)itor distance: (int)count
{
    int i;

    if ([itor isKindOfClass: [OLRandomAccessIterator class]])
    {
        [(OLRandomAccessIterator*)itor advanceBy: count];
    }
    else
    {
        if (count < 0)
        {
            if ([itor isKindOfClass: [OLBidirectionalIterator class]])
            {
                for (i = 0; i > count; i--)
                    [(OLBidirectionalIterator*)itor reverse];
            }
        }
        else if ([itor isKindOfClass: [OLForwardIterator class]])
        {
            for (i = 0; i < count; i++)
                [(OLForwardIterator*)itor advance];
        }
    }
}

+ (unsigned) distanceFrom: (OLIterator*)first to: (OLIterator*)last
{
    return [OLIterator distanceFrom: first to: last allocator: [OLAllocator defaultAllocator]];
}

+ (unsigned) distanceFrom: (OLIterator*)first to: (OLIterator*)last allocator: (OLAllocator*)allocator
{
    unsigned i = 0;
    OLForwardIterator* firstCopy;
    Class rndClass = [OLRandomAccessIterator class];
    Class fwdClass = [OLForwardIterator class];

    if ([first isKindOfClass: rndClass] && [last isKindOfClass: rndClass])
    {
        i = [(OLRandomAccessIterator*)last difference: (OLRandomAccessIterator*)first];
    }
    else if ([first isKindOfClass: fwdClass] && [last isKindOfClass: fwdClass])
    {
        firstCopy = [first copyWithAllocator: allocator];
        while (![firstCopy isEqual: last])
        {
            i++;
            [firstCopy advance];
        }
        [allocator deallocateObject: firstCopy];
    }
    return i;
}

- (id) assign: (id)object
{
    return self;
}

- (id) copyWithAllocator: (OLAllocator*)alloc
{
    return nil;
}

- (id) copyWithZone: (NSZone*)zone
{
    return nil;
}

- (id) dereference
{
    return self;
}

- (BOOL) isEqual: (id)object
{
    return  [object isMemberOfClass: [self class]];
}

@end

@implementation OLForwardIterator

- (id) advance
{
    return self;
}

@end

@implementation OLBidirectionalIterator

- (id) reverse
{
    return self;
}

@end

@implementation OLRandomAccessIterator

- (id) advanceBy: (int)count
{
    return self;
}

- (int) difference: (OLRandomAccessIterator*)other
{
    return 0;
}

@end

@implementation OLReverseBidiIterator

- (id) initWithIterator: (OLBidirectionalIterator*)itor
{
    [super init];
    current = OBJ_RETAIN(itor);
    return self;
}

- (void) dealloc
{
    OBJ_RELEASE(current);
    [super dealloc];
}

- (id) advance
{
    [current reverse];
    return self;
}

- (id) assign: (id)object
{
    [current assign: object];
    return self;
}

- (id) copyWithAllocator: (OLAllocator*)allocator
{
    return [[allocator allocateObject: [OLReverseBidiIterator class]]
        initWithIterator: current];
}

- (id) copyWithZone: (NSZone*)zone
{
    return [[OLReverseBidiIterator allocWithZone: zone] initWithIterator: current];
}

- (id) dereference
{
    id ref = [[current reverse] dereference];
    [current advance];
    return ref;
}

- (BOOL) isEqual: (id)object
{
    return [super isEqual: object] &&
           [current isEqual: ((OLReverseBidiIterator*)object)->current];
}

- (id) reverse
{
    [current advance];
    return self;
}

@end

@implementation OLReverseRandomIterator

- (id) advanceBy: (int)count
{
    if ([current isKindOfClass: [OLRandomAccessIterator class]])
        [(OLRandomAccessIterator*)current advanceBy: -count];
    return self;
}

- (id) copyWithAllocator: (OLAllocator*)allocator
{
    return [[allocator allocateObject: [OLReverseRandomIterator class]]
        initWithIterator: current];
}

- (id) copyWithZone: (NSZone*)zone
{
    return [[OLReverseRandomIterator allocWithZone: zone] initWithIterator: current];
}

- (BOOL) isEqual: (id)object
{
    return [super isEqual: object] &&
           [current isEqual: ((OLReverseRandomIterator*)object)->current];
}

@end

@implementation OLBackInsertIterator

- (id) initWithBackInserter: (id<OLBackInserter>)cnr
{
    [super init];
    container = cnr;
    return self;
}

- (id) assign: (id)object
{
    [container pushBack: object];
    return self;
}

- (id) copyWithAllocator: (OLAllocator*)allocator
{
    return [[allocator allocateObject: [OLBackInsertIterator class]]
        initWithBackInserter: container];
}

- (id) copyWithZone: (NSZone*)zone
{
    return [[OLBackInsertIterator allocWithZone: zone] initWithBackInserter: container];
}

- (BOOL) isEqual: (id)object
{
    return [super isEqual: object] &&
           [container isEqual: ((OLBackInsertIterator*)object)->container];
}

@end

@implementation OLFrontInsertIterator

- (id) initWithFrontInserter: (id<OLFrontInserter>)cnr
{
    [super init];
    container = cnr;
    return self;
}

- (id) assign: (id)object
{
    [container pushFront: object];
    return self;
}

- (id) copyWithAllocator: (OLAllocator*)allocator
{
    return [((OLFrontInsertIterator*)[allocator allocateObject: [OLFrontInsertIterator class]])
        initWithFrontInserter: container];
}

- (id) copyWithZone: (NSZone*)zone
{
    return [((OLFrontInsertIterator*)[OLFrontInsertIterator allocWithZone: zone])
        initWithFrontInserter: container];
}

- (BOOL) isEqual: (id)object
{
    return [super isEqual: object] &&
           [container isEqual: ((OLFrontInsertIterator*)object)->container];
}


@end

@implementation OLInsertIterator

- (id) initWithInserter: (id<OLInserter>)cnr iterator: (OLForwardIterator*)itor
{
    [super init];
    container = cnr;
    iterator = OBJ_RETAIN(itor);
    return self;
}

- (void) dealloc
{
    OBJ_RELEASE(iterator);
    [super dealloc];
}

- (id) assign: (id)object
{
    OLForwardIterator* newItor = (OLForwardIterator*)[container insertAt: iterator value: object];

    OBJ_RELEASE(iterator);
    iterator = OBJ_RETAIN(newItor);
    [iterator advance];
    return self;
}

- (id) copyWithAllocator: (OLAllocator*)allocator
{
    return [[allocator allocateObject: [OLInsertIterator class]]
        initWithInserter: container iterator: iterator];
}

- (id) copyWithZone: (NSZone*)zone
{
    return [[OLInsertIterator allocWithZone: zone]
        initWithInserter: container iterator: iterator];
}

- (BOOL) isEqual: (id)object
{
    return [super isEqual: object] &&
           [container isEqual: ((OLInsertIterator*)object)->container] &&
           [iterator isEqual: ((OLInsertIterator*)object)->iterator];
}

@end

@implementation OLAssociativeIterator

- (id) advance
{
    node = [OLTreeNode increment: node];
    return self;
}

- (id) assign: (id)object
{
    if (node->object != object)
    {
        OBJ_RELEASE(node->object);
        node->object = OBJ_RETAIN(object);
    }
    return self;
}

- (id) copyWithAllocator: (OLAllocator*)alloc
{
    OLAssociativeIterator* copy = [[alloc allocateObject: [OLAssociativeIterator class]] init];

    copy->node = node;
    return copy;
}

- (id) copyWithZone: (NSZone*)zone
{
    OLAssociativeIterator* copy = [[OLAssociativeIterator allocWithZone: zone] init];

    copy->node = node;
    return copy;
}

- (id) dereference
{
    return node->object;
}

- (BOOL) isEqual: (id)object
{
    return [super isEqual: object] &&
           node == ((OLAssociativeIterator*)object)->node;
}

- (id) reverse
{
    node = [OLTreeNode decrement: node];
    return self;
}

@end

@implementation OLArrayIterator

+ (OLPair*) pairWithPointer: (id*)p1 andPointer: (id*)p2
{
    return [OLArrayIterator pairWithPointer: p1 andPointer: p2
        allocator: [OLAllocator defaultAllocator]];
}

+ (OLPair*) pairWithPointer: (id*)p1 andPointer: (id*)p2 allocator: alloc
{
    OLArrayIterator* i1 = [[alloc allocateObject: [OLArrayIterator class]]
        initWithPointer: p1];
    OLArrayIterator* i2 = [[alloc allocateObject: [OLArrayIterator class]]
        initWithPointer: p2];
    OLPair* p = [[alloc allocateObject: [OLPair class]]
        initWithFirst: i1 second: i2];

    [alloc deallocateObject: i1];
    [alloc deallocateObject: i2];
    return [alloc autorelease: p];
}

+ (OLPair*) pairWithPointer: (id*)p distance: (unsigned)count
{
    return [OLArrayIterator pairWithPointer: p andPointer: p + count
        allocator: [OLAllocator defaultAllocator]];
}

+ (OLPair*) pairWithPointer: (id*)p distance: (unsigned)count allocator: alloc
{
    return [OLArrayIterator pairWithPointer: p andPointer: p + count
        allocator: alloc];
}

- (id) initWithPointer: (id*)ptr
{
    [super init];
    current = ptr;
    return self;
}

- (id) advance
{
    current++;
    return self;
}

- (id) advanceBy: (int)count
{
    current += count;
    return self;
}

- (id) assign: (id)object
{
    if (*current != object)
    {
        OBJ_RELEASE(*current);
        *current = OBJ_RETAIN(object);
    }
    return self;
}

- (id) copyWithAllocator: (OLAllocator*)allocator
{
    return [[allocator allocateObject: [OLArrayIterator class]]
        initWithPointer: current];
}

- (id) copyWithZone: (NSZone*)zone
{
    return [[OLArrayIterator allocWithZone: zone] initWithPointer: current];
}

- (id) dereference
{
    return *current;
}

- (int) difference: (OLRandomAccessIterator*)other
{
    int diff = 0;

    if ([other isMemberOfClass: [OLArrayIterator class]])
        diff = current - ((OLArrayIterator*)other)->current;
    return diff;
}

- (BOOL) isEqual: (id)object
{
    return [super isEqual: object] &&
           current == ((OLArrayIterator*)object)->current;
}

- (id) reverse
{
    current--;
    return self;
}

@end

@implementation OLHashIterator

- (id) advance
{
    current = __bumpHashTableNode(current, table);
    return self;
}

- (id) assign: (id)object
{
    if (current->value != object)
    {
        OBJ_RELEASE(current->value);
        current->value = OBJ_RETAIN(object);
    }
    return self;
}

- (id) copyWithAllocator: (OLAllocator*)alloc
{
    OLHashIterator* i = [[alloc allocateObject: [OLHashIterator class]] init];

    i->table = table;
    i->current = current;
    return i;
}

- (id) copyWithZone: (NSZone*)zone
{
    OLHashIterator* i = [[OLHashIterator allocWithZone: zone] init];

    i->table = table;
    i->current = current;
    return i;
}

- (id) dereference
{
    return current->value;
}

- (BOOL) isEqual: (id)object
{
    return [super isEqual: object] &&
           table == ((OLHashIterator*)object)->table &&
           current == ((OLHashIterator*)object)->current;
}

@end

