#ifndef _HASH_TABLE_H
#define _HASH_TABLE_H

#include <stdio.h>
#include <stdlib.h>

// a HashTable object stores pointers to objects of class Object
// The objects don't belong to the HashTable, so if you delete a
// HashTable which has objects in it, they won't automatically be deleted
// 
// Each Object is associated with a HashKey
// The HashKey class must provide the following methods
//
//   unsigned long HashValue()
//   int operator ==(HashKey &, HashKey &)
//
// Object's are looked up/inserted/deleted by HashKey's
//
// Efficiency issues:
//
// a copy of the hash key is stored along with a pointer to the object
// for each association in the hash table
// so try and make hash keys small
// if your hash key's are large objects, store them inside the object
// and parameterize HashTable with a pointer to the HashKey object
// instead of the whole object, e.g.
//
//   HashTable<Task, TaskId *> tasks;
//
// there's really no other way to do this unless you make keys part of
// objects but that isn't flexible enough

template<class Object, class HashKey>
class HashTableElem
{
public:
  HashTableElem(Object *Obj, HashKey Key, HashTableElem *Next) :
  obj(Obj), key(Key), next(Next) {}
  ~HashTableElem() {}

  Object *obj;
  HashKey key;
  HashTableElem *next;
};

#define THashTableElem       HashTableElem<Object, HashKey>

template<class Object, class HashKey>
class HashTable
{
public:

  HashTable(int Size) : size(Size) {
    table = (THashTableElem**)(new char[size*sizeof(THashTableElem*)]);
    for (int i = 0; i < size; ++i) table[i] = NULL;
  }
  ~HashTable() {
    THashTableElem *ptr;
    for (int i = 0; i < size; ++i)
      while ((ptr = table[i]) != NULL) {
	table[i] = ptr->next;
	delete ptr;
      }
    delete [] table;
  }

  // finds an object associated with key
  // returns NULL if the key wasn't found
  Object *Find(HashKey key) {
    THashTableElem **prev, *ptr = IFind(key, prev);
    return ptr == NULL ? NULL : ptr->obj;
  }

  // inserts obj with the hashkey key, returns true if successful
  // fails if the key already exists
  int Insert(Object *obj, HashKey key) {
    THashTableElem **prev;
    if (IFind(key, prev) != NULL) return 0;
    *prev = new HashTableElem<Object, HashKey>(obj, key, NULL);
    return 1;
  }

  // deletes the object associated with key from the hash table
  // returns the object deleted, if it didn't exist return NULL
  Object *Delete(HashKey key) {
    THashTableElem **prev, *ptr;
    if ((ptr = IFind(key, prev)) == NULL) return NULL;
    *prev = ptr->next;
    Object *obj = ptr->obj;
    delete ptr;
    return obj;
  }

  int Count() {
    int n = 0;
    THashTableElem *ptr;
    for (int i = 0; i < size; ++i) {
      ptr = table[i];
      while (ptr != NULL) { ++n; ptr = ptr->next; }
    }
    return n;
  } 

private:
  
  THashTableElem **table;
  int size;

  // internal find routine
  THashTableElem *IFind(HashKey key, THashTableElem **(&Prev)) {
    unsigned long hash = key.HashValue() % size;
    THashTableElem **prev = &(table[hash]), *ptr;
    while ((ptr = *prev) != NULL && !(key == ptr->key))
      prev = &(ptr->next);
    Prev = prev;
    return ptr;
  }

};

#endif
