/*
 *  Copyright 2009 OpenSourceStewardshipFoundation.org
 *  Licensed under GNU General Public License version 2
 *
 *
 * Author: seanhalle@yahoo.com
 */


#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#include "PrivateHash.h"


 HashTable *
makeHashTable( int numHashSlots, FreeEntryContentFnPtr freeFn )
 { HashTable * retTable;
   retTable = VMS__malloc( sizeof( HashTable ) );

   retTable->freeEntryContentFn = freeFn;
   
   retTable->entries    = VMS__malloc( numHashSlots * sizeof(HashEntry *) );
   retTable->numEntries = 0;
   retTable->tableSz    = numHashSlots;

   nullOutTablesArray( retTable );

   return retTable;
 }

 void
doubleTableSize( HashTable *table )
 { int i, oldTableSz, newTableSz;
   HashEntry *entry, *nextEntry, **oldEntries, **newEntries;

   oldTableSz = table->tableSz;
   oldEntries = table->entries;

   newTableSz = 2 * oldTableSz + 1;
   newEntries = VMS__malloc( newTableSz * sizeof(HashEntry *) );

   table->tableSz    = newTableSz;
   table->entries    = newEntries;
   table->numEntries = 0; //about to add them all back!

      // move all the entries from old to new
   for( i=0; i < oldTableSz; i++ )
    { if( oldEntries[i] != NULL )
       { entry = oldEntries[i];
         while( entry != NULL )
          { nextEntry = entry->next;
            entry->next = NULL;
            putEntryIntoTable( entry, table ); //does not allocate anything
            entry = nextEntry;
          }
       }
    }
 }

void
nullOutTablesArray( HashTable *table )
 { int i, tableSz;
   tableSz = table->tableSz;
   HashEntry ** entries = table->entries;
   for( i = 0; i < tableSz; i++ )
      entries[ i ] = NULL;
 }

unsigned int
hashThisKey( char *s, int hashSz )
 { unsigned int h = 0;

   for( ; *s != 0; s++ )
      h = *s + h*31;
   return h % hashSz;
 }

/*Need this to be separated out, for use in both getParam and putParam
 */
HashEntry *
getEntryFromTable( char *key, HashTable * table )
 {  unsigned int
   hashIndex = hashThisKey( key, table->tableSz );
    HashEntry*
   hashEntry = table->entries[ hashIndex ];
   for( ; hashEntry != NULL; hashEntry = hashEntry->next )
    { if( strcmp( hashEntry->key, key ) == 0 )  return hashEntry;
    }
   return NULL;
 }

void *
getValueFromTable( char *key, HashTable * table )
 { HashEntry *entry;
   entry = getEntryFromTable( key, table );
   if( entry == NULL ) return NULL;

   return entry->content;
 }


/*If key already has a value, clobber the old one and replace it
 */
 int
addValueIntoTable( char* key, void *content, HashTable *table )
 { unsigned int hashIdx;
   HashEntry* hashEntry;

   hashEntry = getEntryFromTable( key, table );
   if( hashEntry == NULL )
    { hashIdx = hashThisKey( key, table->tableSz );
      hashEntry = (HashEntry*) VMS__malloc( sizeof( HashEntry ) );
            if( hashEntry == NULL )  return 0;
      hashEntry->key = strdup( key ); //TODO: figure out soln for incr Sz
            if( hashEntry->key == NULL ) return 0;
      hashEntry->next = (table->entries)[hashIdx];
      (table->entries)[hashIdx] = hashEntry;
      table->numEntries += 1;
      if( table->tableSz < table->numEntries ) doubleTableSize( table );
    }
   else
    { (*(table->freeEntryContentFn))( hashEntry->content );
    }
   hashEntry->content = content;
   return 1;
 }

 int
putEntryIntoTable( HashEntry *entry, HashTable *table )
 { unsigned int hashIdx;
   HashEntry* testEntry;

   testEntry = getEntryFromTable( entry->key, table );
   if( testEntry == NULL )
    { hashIdx = hashThisKey( entry->key, table->tableSz );
      entry->next = (table->entries)[hashIdx];
      (table->entries)[hashIdx] = entry;
      table->numEntries += 1;
      if( table->tableSz < table->numEntries ) doubleTableSize( table );
    }
   else
    { (*(table->freeEntryContentFn))( testEntry->content );
      testEntry->content = entry->content;
      entry->content = NULL;
      freeHashEntryUsing( entry, table );
    }
   return 1;
 }


 bool8
deleteEntryFromTable( char *key, HashTable *table )
 { HashEntry  *hashEntry;
   HashEntry **addrOfHashEntryPtr;
   unsigned int hashIndex;

   hashIndex = hashThisKey( key, table->tableSz );
   addrOfHashEntryPtr = &( table->entries[ hashIndex ] );
   hashEntry = *addrOfHashEntryPtr;
   while( hashEntry != NULL )
    { if( strcmp( hashEntry->key, key ) == 0 )
       {
         *addrOfHashEntryPtr = hashEntry->next;
         //TODO: Free the contents of entry?
         freeHashEntryButNotContent( hashEntry );
         table->numEntries -= 1;
         return TRUE;
       }
      addrOfHashEntryPtr = &( hashEntry->next );
      hashEntry = *addrOfHashEntryPtr;
    }
   return FALSE;
 }

 
/* debugging function displays the hashtable in (key.value) pairs
*/
/*void hashTableToString( HashTable * table )
 { int i;
   HashEntry *t;
   for( i = 0; i < table->tableSz; i++ )
    { t = entries[i];
      if( t == NULL )
         strcat_m( retStr, &"()" );
      else
       { strcat_m( retStr, &"(" );
         for( ; t != NULL; t = t->next )
          { strcat_m( retStr, &" " );
            strcat_m( retStr, t->key );
            strcat_m( retStr, &"." );
            strcat_m( retStr, paramToString( t->param ) );
            strcat_m( retStr, &" " );
          }
         strcat_m( retStr, &")" );
       }
    }
 }
*/

/*
 void
setFnToFreeEntryContent( HashTable *table, FreeEntryContentFnPtr fnPtr )
 {
   table->freeEntryContentFn = fnPtr;
 }
*/

void
freeHashTable( HashTable *table )
 { int i;
   HashEntry *hashEntry, *temp, **entries;

   entries = table->entries;
   for( i=0; i < table->tableSz; i++ )
    { if( entries[i] != NULL )
       { hashEntry = entries[i];
         while( hashEntry != NULL )
          {
            temp = hashEntry->next;
            freeHashEntryUsing( hashEntry, table );
            hashEntry = temp;
          }
       }
    }
 }

void
freeHashEntryUsing( HashEntry *entry, HashTable *table )
 {
   if( entry->content != NULL )
      (*(table->freeEntryContentFn))( entry->content );
   VMS__free( entry->key ); //was VMS__malloc'd above, so free it
   VMS__free( entry );
 }

void
freeHashEntryButNotContent( HashEntry *entry )
 {
   VMS__free( entry->key ); //was VMS__malloc'd above, so free it
   VMS__free( entry );
 }

