//
//  AMutableDictionary.m
//  ST4
//
//  Created by Alan Condit on 4/18/11.
//  Copyright 2011 Alan Condit. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "AMutableDictionary.h"
#import "ACBTree.h"

@implementation AMutableDictionary

@synthesize root;
@synthesize nodes_av;
@synthesize nodes_inuse;
@synthesize nxt_nodeid;
//@synthesize count;
@synthesize data;
@synthesize ptrBuffer;

+ (AMutableDictionary *) newDictionary
{
    return [[AMutableDictionary alloc] init];
}

/** dictionaryWithCapacity
 *  capacity is meaningless to ACBTree because
 *  capacity is automatically increased
 */
+ (AMutableDictionary *) dictionaryWithCapacity
{
    return [[AMutableDictionary alloc] init];
}

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
        nxt_nodeid = 0;
        count = 0;
        root = [ACBTree newNodeWithDictionary:self];
        root.nodeType = LEAF;
        root.numrecs = 0;
        root.updtd = NO;
        root.lnodeid = 1;
        root.lnode = nil;
        root.rnodeid = 0xffff;
        root.rnode = nil;
    }
    return self;
}

/** initWithCapacity
 *  capacity is meaningless to ACBTree because
 *  capacity is automatically increased
 */
- (id) initWithCapacity:(NSUInteger)numItems
{
    self = [super init];
    if (self) {
        // Initialization code here.
        nxt_nodeid = 0;
        count = 0;
        root = [ACBTree newNodeWithDictionary:self];
        root.nodeType = LEAF;
        root.numrecs = 0;
        root.updtd = NO;
        root.lnodeid = 1;
        root.lnode = nil;
        root.rnodeid = 0xffff;
        root.rnode = nil;
    }
    return self;
}

- (void) dealloc
{
#ifdef DEBUG_DEALLOC
    NSLog( @"called dealloc in AMutableDictionary" );
#endif
    if ( data ) [data release];
    if ( root ) [root release];
    [super dealloc];
}

- (id) objectForKey:(id)aKey
{
    id obj = nil;
    ACBTree *node;
    ACBKey *kp;
    NSInteger ret;
    BOOL mustRelease = NO;

    if ( [aKey isKindOfClass:[NSString class]] ) {
        kp = [ACBKey newKeyWithKStr:aKey];
        mustRelease = YES;
    }
    else if ( [aKey isKindOfClass:[ACBKey class]] ) {
        kp = aKey;
        //ACBKey *akey = [ACBKey newKey:aKey];
    }
    else {
        @throw [NSException exceptionWithName:NSInvalidArgumentException
                                       reason:[NSString stringWithFormat:@"What kind of key is this? %@", aKey]
                                     userInfo:nil];
        return nil; // not a key that I know how to deal with
    }
    node = [root search:kp.key];
    if ( node != nil ) {
        ret = [node searchnode:kp.key match:YES];
        if ( ret >= 0 && ret < node.numkeys ) {
            obj = node.btNodes[ret];
            if ( obj == [NSNull null] ) {
                obj = nil;
            }
        }
    }
    if ( mustRelease ) [kp release];
    return obj;
}

- (void) setObject:(id)obj forKey:(id)aKey
{
    ACBKey *kp;
    BOOL mustRelease = NO;
    if ( [aKey isKindOfClass:[NSString class]] ) {
        kp = [ACBKey newKeyWithKStr:aKey];
        mustRelease = YES;
    }
    else if ( [aKey isKindOfClass:[ACBKey class]] ) {
        kp = (ACBKey *)aKey;
    }
    else {
        @throw [NSException exceptionWithName:NSInvalidArgumentException
                                       reason:[NSString stringWithFormat:@"What kind of key is this? %@", aKey]
                                     userInfo:nil];
    }
    if ( [root search:kp.key] == nil ) {
        if ( obj == nil ) {
            obj = [NSNull null];
        }
        root = [root insertkey:kp value:obj];
        [kp retain];
        [obj retain];
        kp.recnum = count++;
    }
    else {
        if ( mustRelease ) [kp release];
        @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"key alreadyExists" userInfo:nil];
    }
    return;
}

- (BOOL) isEqual:(id)object
{
    return [super isEqual:object];
}

- (void) removeObjectForKey:(id)aKey
{
    if ( [root deletekey:aKey] == SUCCESS )
        count--;
}

- (NSUInteger) count
{
    return count;
}

- (NSArray *) allKeys
{
    NSUInteger cnt = [root keyWalkLeaves];
    return [NSArray arrayWithObjects:ptrBuffer count:cnt];
}

- (NSArray *) allValues
{
    NSUInteger cnt = [root objectWalkLeaves];
    return [NSArray arrayWithObjects:ptrBuffer count:cnt];
}

- (ArrayIterator *) keyEnumerator
{
    return [ArrayIterator newIterator:[self allKeys]];
}

- (ArrayIterator *) objectEnumerator
{
    return [ArrayIterator newIterator:[self allValues]];
}

// This is where all the magic happens.
// You have two choices when implementing this method:
// 1) Use the stack based array provided by stackbuf. If you do this, then you must respect the value of 'len'.
// 2) Return your own array of objects. If you do this, return the full length of the array returned until you run out of objects, then return 0. For example, a linked-array implementation may return each array in order until you iterate through all arrays.
// In either case, state->itemsPtr MUST be a valid array (non-nil). This sample takes approach #1, using stackbuf to store results.
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
{
    NSUInteger cnt = 0;
    // This is the initialization condition, so we'll do one-time setup here.
    // Ensure that you never set state->state back to 0, or use another method to detect initialization
    // (such as using one of the values of state->extra).
    if (state->state == 0) {
        // We are not tracking mutations, so we'll set state->mutationsPtr to point into one of our extra values,
        // since these values are not otherwise used by the protocol.
        // If your class was mutable, you may choose to use an internal variable that is updated when the class is mutated.
        // state->mutationsPtr MUST NOT be NULL.
        state->mutationsPtr = &state->extra[0];
        [self.root objectWalkLeaves];
    }
    // Now we provide items, which we track with state->state, and determine if we have finished iterating.
    if (state->state < self.count) {
        // Set state->itemsPtr to the provided buffer.
        // Alternate implementations may set state->itemsPtr to an internal C array of objects.
        // state->itemsPtr MUST NOT be NULL.
        state->itemsPtr = stackbuf;
        // Fill in the stack array, either until we've provided all items from the list
        // or until we've provided as many items as the stack based buffer will hold.
        while((state->state < self.count) && (cnt < len)) {
            // For this sample, we generate the contents on the fly.
            // A real implementation would likely just be copying objects from internal storage.
            stackbuf[cnt++] = ptrBuffer[state->state++];
        }
        // state->state = ((cnt < len)? cnt : len);
    }
    else
    {
        // We've already provided all our items, so we signal we are done by returning 0.
        cnt = 0;
    }
    return cnt;
}

- (void) clear
{
    if ( count ) [self removeAllObjects];
}

- (void) removeAllObjects
{
    if ( [self count] > 0 ) {
        // root = [ACBTree newNodeWithDictionary:self];
        NSArray *list = [self allKeys];
        for ( NSInteger i = [self count] - 1; i >= 0; i-- ) {
            [self removeObjectForKey:[list objectAtIndex:i]];
        }
        root.nodeid = 0;
        nxt_nodeid = 1;
    }
}

- (NSInteger) nextNodeId
{
    return nxt_nodeid++;
}

- (NSArray *) toKeyArray
{
    return nil;
}

- (NSArray *) toValueArray
{
    return nil;
}

@end
