//
// $Id: HashMap.m,v 1.12 2005/05/18 06:46:52 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 "HashMap.h"
#import "HashTable.h"
#import "ContainerCoder.h"
#import "Constants.h"
#import <Foundation/NSCoder.h>

@implementation OLHashMap

+ (id) hashMap
{
    OL_BEGIN_AUTO_CTOR(OLHashMap)
        init
    OL_END_AUTO_CTOR;
}

+ (id) hashMapFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OL_BEGIN_AUTO_CTOR(OLHashMap)
        initFrom: first to: last
    OL_END_AUTO_CTOR;
}

+ (id) hashMapWithHashMap: (OLHashMap*)right
{
    OL_BEGIN_AUTO_CTOR(OLHashMap)
        initWithHashMap: right
    OL_END_AUTO_CTOR;
}

- (id) init
{
    return [self initWithTableSize: 100];
}

- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    [self init];
    [self insertFrom: first to: last];
    return self;
}

- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last tableSize: (unsigned)size
{
    [self initWithTableSize: size];
    [self insertFrom: first to: last];
    return self;
}

- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last tableSize: (unsigned)size keyEqual: (NSObject<OLBoolBinaryFunction>*)eq
{
    [self initWithTableSize: size keyEqual: eq];
    [self insertFrom: first to: last];
    return self;
}

- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last tableSize: (unsigned)size keyEqual: (NSObject<OLBoolBinaryFunction>*)eq allocator: (OLAllocator*)alloc
{
    [self initWithTableSize: size keyEqual: eq allocator: alloc];
    [self insertFrom: first to: last];
    return self;
}

- (id) initWithCoder: (NSCoder*)decoder
{
    OLAllocator* alloc = [OLContainerCoder decodeAllocatorWithCoder: decoder];
    NSObject<OLBoolBinaryFunction>* eq;
    id* array;
    OLArrayIterator* first;
    OLArrayIterator* last;
    unsigned count;
    unsigned tableSize;

    [super init];
    if ([decoder respondsToSelector: @selector(allowsKeyedCoding)] &&
        [decoder allowsKeyedCoding])
    {
        eq = [decoder decodeObjectForKey: KEYEQUAL_KEY];
        tableSize = [decoder decodeIntForKey: TABLE_SIZE_KEY];
    }
    else
    {
        eq = [decoder decodeObject];
        [decoder decodeValueOfObjCType: @encode(unsigned) at: &tableSize];
    }
    table = [[alloc allocateObject: [OLHashTableMap class]]
        initWithSize: tableSize equalFunc: eq allocator: alloc];
    array = [OLContainerCoder decodeWithCoder: decoder allocator: alloc returnedCount: &count];
    first = [[alloc allocateObject: [OLArrayIterator class]]
        initWithPointer: array];
    last = [[alloc allocateObject: [OLArrayIterator class]]
        initWithPointer: array + count];
    [self insertFrom: first to: last];
    [alloc deallocateBytes: array];
    [alloc deallocateObject: first];
    [alloc deallocateObject: last];
    return self;
}

- (id) initWithHashMap: (OLHashMap*)right
{
    [super init];
    table = [[[right allocator] allocateObject: [OLHashTableMap class]]
        initWithHashTable: right->table];
    return self;
}

- (id) initWithTableSize: (unsigned)size
{
    OLAllocator* alloc = [OLAllocator defaultAllocator];
    OLEqualTo* eq = [[alloc allocateObject: [OLEqualTo class]] init];

    [self initWithTableSize: size keyEqual: eq allocator: alloc];
    [alloc deallocateObject: eq];
    return self;
}

- (id) initWithTableSize: (unsigned)size keyEqual: (NSObject<OLBoolBinaryFunction>*)eq
{
    return [self initWithTableSize: size keyEqual: eq allocator: [OLAllocator defaultAllocator]];
}

- (id) initWithTableSize: (unsigned)size keyEqual: (NSObject<OLBoolBinaryFunction>*)eq allocator: (OLAllocator*)alloc
{
    [super init];
    table = [[alloc allocateObject: [OLHashTableMap class]]
        initWithSize: size equalFunc: eq allocator: alloc];
    return self;
}

- (void) dealloc
{
    [[table allocator] deallocateObject: table];
    [super dealloc];
}

- (void) assignKey: (id)key value: (id)value
{
    [table assignUniqueKey: key value: value];
}

- (OLHashIterator*) begin
{
    return [table begin];
}

- (void) clear
{
    [table clear];
}

- (id) copyWithAllocator: (OLAllocator*)alloc
{
    return [[alloc allocateObject: [OLHashMap class]]
        initWithHashMap: self];
}

- (id) copyWithZone: (NSZone*)zone
{
    return [[OLHashMap allocWithZone: zone] initWithHashMap: self];
}

- (unsigned) count: (id)key
{
    return [table count: key];
}

- (BOOL) empty
{
    return [table empty];
}

- (void) encodeWithCoder: (NSCoder*)encoder
{
    OLHashIterator* first = [table beginImpl];
    unsigned tableSize = [table tableSize];

    [OLContainerCoder encodeAllocator: [table allocator] withCoder: encoder];
    if ([encoder respondsToSelector: @selector(allowsKeyedCoding)] &&
        [encoder allowsKeyedCoding])
    {
        [encoder encodeObject: [table keyEqual] forKey: KEYEQUAL_KEY];
        [encoder encodeInt: tableSize forKey: TABLE_SIZE_KEY];
    }
    else
    {
        [encoder encodeObject: [table keyEqual]];
        [encoder encodeValueOfObjCType: @encode(unsigned) at: &tableSize];
    }
    [OLContainerCoder encodeFrom: first count: [table size] withCoder: encoder
        allocator: [table allocator]];
    [[table allocator] deallocateObject: first];
}

- (OLHashIterator*) end
{
    return [table end];
}

- (OLPair*) equalRange: (id)key
{
    return [table equalRange: key];
}

- (void) erase: (OLHashIterator*)where
{
    [table erase: where];
}

- (void) eraseFrom: (OLHashIterator*)first to: (OLHashIterator*)last
{
    [table eraseFrom: first to: last];
}

- (unsigned) eraseKey: (id)key
{
    return [table eraseKey: key];
}

- (OLHashIterator*) find: (id)key
{
    return [table find: key];
}

- (OLAllocator*) allocator
{
    return [table allocator];
}

- (id) insert: (OLPair*)keyValue
{
    return [table insertUnique: keyValue];
}

- (void) insertFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    [table insertUniqueFrom: first to: last];
}

- (id) insertKey: (id)key value: (id)val
{
    OLPair* p = [[[table allocator] allocateObject: [OLPair class]]
        initWithFirst: key second: val];
    id r = [self insert: p];

    [[table allocator] deallocateObject: p];
    return r;
}

- (BOOL) isEqual: (id)object
{
    return [object isKindOfClass: [OLHashMap class]] &&
           [((OLHashMap*)object)->table isEqualUnique: table];
}

- (NSObject<OLBoolBinaryFunction>*) keyEqual
{
    return [table keyEqual];
}

- (unsigned) maxSize
{
    return [table maxSize];
}

- (unsigned) size
{
    return [table size];
}

- (void) swap: (OLHashMap*)right
{
    [table swap: right->table];
}

- (id) valueForKey: (id)key
{
    return [table findValue: key];
}

@end

@implementation OLHashMultiMap

+ (id) hashMultiMap
{
    OL_BEGIN_AUTO_CTOR(OLHashMultiMap)
        init
    OL_END_AUTO_CTOR;
}

+ (id) hashMultiMapFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OL_BEGIN_AUTO_CTOR(OLHashMultiMap)
        initFrom: first to: last
    OL_END_AUTO_CTOR;
}

+ (id) hashMultiMapWithHashMap: (OLHashMap*)right
{
    OL_BEGIN_AUTO_CTOR(OLHashMultiMap)
        initWithHashMap: right
    OL_END_AUTO_CTOR;
}

- (void) assignKey: (id)key value: (id)value
{
    [table assignEqualKey: key value: value];
}

- (id) insert: (OLPair*)keyValue
{
    return [table insertEqual: keyValue];
}

- (void) insertFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    [table insertEqualFrom: first to: last];
}

- (BOOL) isEqual: (id)object
{
    return [object isKindOfClass: [OLHashMultiMap class]] &&
           [((OLHashMultiMap*)object)->table isEqualNonUnique: table];
}

- (id) valueForKey: (id)key
{
    return nil;
}

@end
