//
//  ANTLRHashMap.m
//  ANTLR
//
// Copyright (c) 2010 Alan Condit
// 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.
// 3. The name of the author may not be used to endorse or promote products
//    derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.

#define SUCCESS (0)
#define FAILURE (-1)

#import "ANTLRHashMap.h"

static NSInteger itIndex;

/*
 * Start of ANTLRHashMap
 */
@implementation ANTLRHashMap

@synthesize Scope;
@synthesize LastHash;

+(id)newANTLRHashMap
{
    return [[ANTLRHashMap alloc] init];
}

+(id)newANTLRHashMapWithLen:(NSInteger)aBuffSize
{
    return [[ANTLRHashMap alloc] initWithLen:aBuffSize];
}

-(id)init
{
    NSInteger idx;
    
    if ((self = [super init]) != nil) {
        fNext = nil;
        Scope = 0;
        ptr = 0;
        BuffSize = HASHSIZE;
        buffer = [[NSMutableData dataWithLength:(NSUInteger)BuffSize * sizeof(id)] retain];
        ptrBuffer = (ANTLRMapElement **) [buffer mutableBytes];
        if ( fNext != nil ) {
            Scope = ((ANTLRHashMap *)fNext)->Scope+1;
            for( idx = 0; idx < BuffSize; idx++ ) {
                ptrBuffer[idx] = ((ANTLRHashMap *)fNext)->ptrBuffer[idx];
            }
        }
        mode = 0;
    }
    return( self );
}

-(id)initWithLen:(NSInteger)aBuffSize
{
    NSInteger idx;
    
    if ((self = [super init]) != nil) {
        fNext = nil;
        BuffSize = aBuffSize;
        Scope = 0;
        ptr = 0;
        buffer = [[NSMutableData dataWithLength:(NSUInteger)BuffSize * sizeof(id)] retain];
        ptrBuffer = (ANTLRMapElement **) [buffer mutableBytes];
        if ( fNext != nil ) {
            Scope = ((ANTLRHashMap *)fNext)->Scope+1;
            for( idx = 0; idx < BuffSize; idx++ ) {
                ptrBuffer[idx] = ((ANTLRHashMap *)fNext)->ptrBuffer[idx];
            }
        }
        mode = 0;
    }
    return( self );
}

-(void)dealloc
{
#ifdef DEBUG_DEALLOC
    NSLog( @"called dealloc in ANTLRHashMap" );
#endif
    ANTLRMapElement *tmp, *rtmp;
    NSInteger idx;

    if ( self.fNext != nil ) {
        for( idx = 0; idx < BuffSize; idx++ ) {
            tmp = ptrBuffer[idx];
            while ( tmp && tmp != [((ANTLRHashMap *)fNext) getptrBufferEntry:idx] ) {
                rtmp = tmp;
                // tmp = [tmp getfNext];
                tmp = (ANTLRMapElement *)tmp.fNext;
                [rtmp release];
            }
        }
    }
    if ( buffer ) [buffer release];
    [super dealloc];
}

- (NSInteger)count
{
    NSInteger aCnt = 0;
    
    for (NSInteger i = 0; i < BuffSize; i++) {
        if ( ptrBuffer[i] != nil ) {
            aCnt++;
        }
    }
    return aCnt;
}
                          
- (NSInteger) size
{
    NSInteger aSize = 0;
    
    for (NSInteger i = 0; i < BuffSize; i++) {
        if ( ptrBuffer[i] != nil ) {
            aSize += sizeof(id);
        }
    }
    return aSize;
}
                                  
                                  
-(void)deleteANTLRHashMap:(ANTLRMapElement *)np
{
    ANTLRMapElement *tmp, *rtmp;
    NSInteger idx;
    
    if ( self.fNext != nil ) {
        for( idx = 0; idx < BuffSize; idx++ ) {
            tmp = ptrBuffer[idx];
            while ( tmp && tmp != (ANTLRLinkBase *)[((ANTLRHashMap *)fNext) getptrBufferEntry:idx] ) {
                rtmp = tmp;
                tmp = [tmp getfNext];
                [rtmp release];
            }
        }
    }
}

-(ANTLRHashMap *)PushScope:(ANTLRHashMap **)map
{
    NSInteger idx;
    ANTLRHashMap *htmp;
    
    htmp = [ANTLRHashMap newANTLRHashMap];
    if ( *map != nil ) {
        ((ANTLRHashMap *)htmp)->fNext = *map;
        [htmp setScope:[((ANTLRHashMap *)htmp->fNext) getScope]+1];
        for( idx = 0; idx < BuffSize; idx++ ) {
            htmp->ptrBuffer[idx] = ((ANTLRHashMap *)htmp->fNext)->ptrBuffer[idx];
        }
    }
    //    gScopeLevel++;
    *map = htmp;
    return( htmp );
}

-(ANTLRHashMap *)PopScope:(ANTLRHashMap **)map
{
    NSInteger idx;
    ANTLRMapElement *tmp;
    ANTLRHashMap *htmp;
    
    htmp = *map;
    if ( (*map)->fNext != nil ) {
        *map = (ANTLRHashMap *)htmp->fNext;
        for( idx = 0; idx < BuffSize; idx++ ) {
            if ( htmp->ptrBuffer[idx] == nil ||
                htmp->ptrBuffer[idx] == (*map)->ptrBuffer[idx] ) {
                break;
            }
            tmp = htmp->ptrBuffer[idx];
            /*
             * must deal with parms, locals and labels at some point
             * can not forget the debuggers
             */
            htmp->ptrBuffer[idx] = [tmp getfNext];
            [tmp release];
        }
        *map = (ANTLRHashMap *)htmp->fNext;
        //        gScopeLevel--;
    }
    return( htmp );
}

#ifdef USERDOC
/*
 *  HASH        hash entry to get index to table
 *  NSInteger hash( ANTLRHashMap *self, char *s );
 *
 *     Inputs:  char *s             string to find
 *
 *     Returns: NSInteger                 hashed value
 *
 *  Last Revision 9/03/90
 */
#endif
-(NSInteger)hash:(NSString *)s       /*    form hash value for string s */
{
    NSInteger hashval;
    const char *tmp;
    
    tmp = [s cStringUsingEncoding:NSASCIIStringEncoding];
    for( hashval = 0; *tmp != '\0'; )
        hashval += *tmp++;
    self->LastHash = hashval % BuffSize;
    return( self->LastHash );
}

#ifdef USERDOC
/*
 *  FINDSCOPE  search hashed list for entry
 *  ANTLRHashMap *findscope( ANTLRHashMap *self, NSInteger scope );
 *
 *     Inputs:  NSInteger       scope -- scope level to find
 *
 *     Returns: ANTLRHashMap   pointer to ptrBuffer of proper scope level
 *
 *  Last Revision 9/03/90
 */
#endif
-(ANTLRHashMap *)findscope:(NSInteger)scope
{
    if ( self->Scope == scope ) {
        return( self );
    }
    else if ( fNext ) {
        return( [((ANTLRHashMap *)fNext) findscope:scope] );
    }
    return( nil );              /*   not found      */
}

#ifdef USERDOC
/*
 *  LOOKUP  search hashed list for entry
 *  ANTLRMapElement *lookup( ANTLRHashMap *self, char *s, NSInteger scope );
 *
 *     Inputs:  char     *s          string to find
 *
 *     Returns: ANTLRMapElement  *           pointer to entry
 *
 *  Last Revision 9/03/90
 */
#endif
-(id)lookup:(NSString *)s Scope:(NSInteger)scope
{
    ANTLRMapElement *np;
    
    for( np = self->ptrBuffer[[self hash:s]]; np != nil; np = [np getfNext] ) {
        if ( [s isEqualToString:[np getName]] ) {
            return( np );        /*   found it       */
        }
    }
    return( nil );              /*   not found      */
}

#ifdef USERDOC
/*
 *  INSTALL search hashed list for entry
 *  NSInteger install( ANTLRHashMap *self, ANTLRMapElement *sym, NSInteger scope );
 *
 *     Inputs:  ANTLRMapElement    *sym   -- symbol ptr to install
 *              NSInteger         scope -- level to find
 *
 *     Returns: Boolean     TRUE   if installed
 *                          FALSE  if already in table
 *
 *  Last Revision 9/03/90
 */
#endif
-(ANTLRMapElement *)install:(ANTLRMapElement *)sym Scope:(NSInteger)scope
{
    ANTLRMapElement *np;
    
    np = [self lookup:[sym getName] Scope:scope ];
    if ( np == nil ) {
        [sym retain];
        [sym setFNext:self->ptrBuffer[ self->LastHash ]];
        self->ptrBuffer[ self->LastHash ] = sym;
        return( self->ptrBuffer[ self->LastHash ] );
    }
    return( nil );            /*   not found      */
}

#ifdef USERDOC
/*
 *  RemoveSym  search hashed list for entry
 *  NSInteger RemoveSym( ANTLRHashMap *self, char *s );
 *
 *     Inputs:  char     *s          string to find
 *
 *     Returns: NSInteger      indicator of SUCCESS OR FAILURE
 *
 *  Last Revision 9/03/90
 */
#endif
-(NSInteger)RemoveSym:(NSString *)s
{
    ANTLRMapElement *np, *tmp;
    NSInteger idx;
    
    idx = [self hash:s];
    for ( tmp = self->ptrBuffer[idx], np = self->ptrBuffer[idx]; np != nil; np = [np getfNext] ) {
        if ( [s isEqualToString:[np getName]] ) {
            tmp = [np getfNext];             /* get the next link  */
            [np release];
            return( SUCCESS );            /* report SUCCESS     */
        }
        tmp = [np getfNext];              //  BAD!!!!!!
    }
    return( FAILURE );                    /*   not found      */
}

-(void)delete_chain:(ANTLRMapElement *)np
{
    if ( [np getfNext] != nil )
        [self delete_chain:[np getfNext]];
    [np dealloc];
}

#ifdef DONTUSEYET
-(NSInteger)bld_symtab:(KW_TABLE *)toknams
{
    NSInteger i;
    ANTLRMapElement *np;
    
    for( i = 0; *(toknams[i].name) != '\0'; i++ ) {
        // install symbol in ptrBuffer
        np = [ANTLRMapElement newANTLRMapElement:[NSString stringWithFormat:@"%s", toknams[i].name]];
        //        np->fType = toknams[i].toknum;
        [self install:np Scope:0];
    }
    return( SUCCESS );
}
#endif

-(ANTLRMapElement *)getptrBufferEntry:(NSInteger)idx
{
    return( ptrBuffer[idx] );
}

-(ANTLRMapElement **)getptrBuffer
{
    return( ptrBuffer );
}

-(void)setptrBuffer:(ANTLRMapElement *)np Index:(NSInteger)idx
{
    if ( idx < BuffSize ) {
        [np retain];
        ptrBuffer[idx] = np;
    }
}

-(NSInteger)getScope
{
    return( Scope );
}

-(void)setScopeScope:(NSInteger)i
{
    Scope = i;
}

- (ANTLRMapElement *)getTType:(NSString *)name
{
    return [self lookup:name Scope:0];
}

/*
 * works only for maplist indexed not by name but by TokenNumber
 */
- (ANTLRMapElement *)getNameInList:(NSInteger)ttype
{
    ANTLRMapElement *np;
    NSInteger aTType;

    aTType = ttype % BuffSize;
    for( np = self->ptrBuffer[aTType]; np != nil; np = [np getfNext] ) {
        if ( [(NSNumber *)np.node integerValue] == ttype ) {
            return( np );        /*   found it       */
        }
    }
    return( nil );              /*   not found      */
}

- (ANTLRLinkBase *)getName:(NSString *)name
{
    return [self lookup:name Scope:0]; /*  nil if not found      */    
}

- (void)putNode:(NSString *)name TokenType:(NSInteger)ttype
{
    ANTLRMapElement *np;
    
    // install symbol in ptrBuffer
    np = [ANTLRMapElement newANTLRMapElementWithName:[NSString stringWithString:name] Type:ttype];
    //        np->fType = toknams[i].toknum;
    [self install:np Scope:0];
}

- (NSInteger)getMode
{
    return mode;
}

- (void)setMode:(NSInteger)aMode
{
    mode = aMode;
}

- (void) addObject:(id)aRule
{
    NSInteger idx;

    idx = [self count];
    if ( idx >= BuffSize ) {
        idx %= BuffSize;
    }
    ptrBuffer[idx] = aRule;
}

/* this may have to handle linking into the chain
 */
- (void) insertObject:(id)aRule atIndex:(NSInteger)idx
{
    if ( idx >= BuffSize ) {
        idx %= BuffSize;
    }
    if ( aRule != ptrBuffer[idx] ) {
        if ( ptrBuffer[idx] ) [ptrBuffer[idx] release];
        [aRule retain];
    }
    ptrBuffer[idx] = aRule;
}

- (id)objectAtIndex:(NSInteger)idx
{
    if ( idx >= BuffSize ) {
        idx %= BuffSize;
    }
    return ptrBuffer[idx];
}

/* this will never link into the chain
 */
- (void) setObject:(id)aRule atIndex:(NSInteger)idx
{
    if ( idx >= BuffSize ) {
        idx %= BuffSize;
    }
    if ( aRule != ptrBuffer[idx] ) {
        if ( ptrBuffer[idx] ) [ptrBuffer[idx] release];
        [aRule retain];
    }
    ptrBuffer[idx] = aRule;
}

- (void)putName:(NSString *)name Node:(id)aNode
{
    ANTLRMapElement *np;
    
    np = [self lookup:name Scope:0 ];
    if ( np == nil ) {
        np = [ANTLRMapElement newANTLRMapElementWithName:name Node:aNode];
        if ( ptrBuffer[LastHash] )
            [ptrBuffer[LastHash] release];
        [np retain];
        np.fNext = ptrBuffer[ LastHash ];
        ptrBuffer[ LastHash ] = np;
    }
    return;    
}

- (NSEnumerator *)objectEnumerator
{
#pragma mark fix this its broken
    NSEnumerator *anEnumerator;

    itIndex = 0;
    return anEnumerator;
}

- (BOOL)hasNext
{
    if (self && [self count] < BuffSize-1) {
        return YES;
    }
    return NO;
}

- (ANTLRMapElement *)nextObject
{
    if (self && itIndex < BuffSize-1) {
        return ptrBuffer[itIndex];
    }
    return nil;
}

@synthesize BuffSize;
@synthesize count;
@synthesize ptr;
@synthesize ptrBuffer;
@synthesize buffer;
@end