// hashmap.c - implementation of a simple hash map

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

#include "hashmap.h"
#include "hashmap_private.h"
#include "default_error_handling.h"

//------------------------------------------------------------
// stuff needed for handling of unrecoverable errors
static void (*unrecoverable_error_handler_)(int error_type,
                                            void* special) = NULL;
static int unrecoverable_error_ = 0;

//============================================================
// Hashmap access functions

//------------------------------------------------------------
// constructor for a hashmap
//
// triggered errors: OUT_OF_MEMORY
//                   ILLEGAL_ARGUMENTS
Hashmap *newHashmap(unsigned modulus,
                    unsigned (*hash_funct)(
                      void *key,
                      unsigned modulus,
                      void (*error_handler)(int error_type,
                                            void *special)),
                    int (*compare_funct)(
                      void *key1,
                      void *key2,
                      void (*error_handler)(int error_type,
                                            void *special)),
                    void (*error_handler)(int error_type,
                                          void *special))
{
  Hashmap *map = (Hashmap*)malloc(sizeof(Hashmap));

  if (!error_handler)
    error_handler = defaultErrorHandler;
  if (!map)
  {
    error_handler(OUT_OF_MEMORY,"newHashmap");
    return(NULL);
  }
  if ((!hash_funct) || (!compare_funct))
  {
    error_handler(ILLEGAL_ARGUMENTS,"newHashmap");
    free(map);
    return(NULL);
  }
  map->hash_funct = hash_funct;
  map->compare_funct = compare_funct;
  map->modulus = modulus;
  map->entries = malloc(modulus * sizeof(HashmapEntry*));
  if (!map->entries)
  {
    error_handler(OUT_OF_MEMORY,"newHashmap");
    free(map);
    return(NULL);
  }
  return(map);
}

//------------------------------------------------------------
// destructor for a hashmap
//
// triggered errors: ILLEGAL_ARGUMENTS
void deleteHashmap(Hashmap *map,
                   void (*error_handler)(int error_type,
                                         void *special))
{
  HashmapEntry **iterator = NULL;
  HashmapEntry *current = NULL;
  HashmapEntry *next = NULL;

  if (!error_handler)
    error_handler = defaultErrorHandler;
  if (!map)
  {
    error_handler(ILLEGAL_ARGUMENTS,"deleteHashmap");
    return;
  }
  iterator = map->entries;
  while(map->modulus--)
  {
    current = *iterator++;
    if (current)
    {
      while(next = getNextFromHashmapEntry(current,error_handler))
      {
        deleteHashmapEntry(current,error_handler);
        current = next;
      }
      deleteHashmapEntry(current,error_handler);
    }
  }
  free(map->entries);
  free(map);
}

//------------------------------------------------------------
// insert function for a hashmap. Duplicate keys and NULL keys
// are not allowed and will trigger an error
//
// triggered errors: ILLEGAL_ARGUMENTS
//                   DUPLICATE_KEY
void insertIntoHashmap(Hashmap *map,
                       void *key,
                       void *value,
                       void (*key_destructor)(
                         void *key,
                         void (*err_handler)(int error_type,
                                             void *special)),
                       void (*value_destructor)(
                         void *value,
                         void (*err_handler)(int error_type,
                                             void *special)),
                       void (*error_handler)(int error_type,
                                             void *special))
{
  unsigned hash_value = 0;
  HashmapEntry *new_entry = NULL;
  HashmapEntry *current_entry = NULL;

  if (!error_handler)
    error_handler = defaultErrorHandler;
      // NULL values are allowed, not checking this here.
  if ((!map) || (!key))
  {
    error_handler(ILLEGAL_ARGUMENTS,"insertIntoHashmap");
    return;
  }
  unrecoverable_error_handler_ = error_handler;
      // calculate hash value
  hash_value = map->hash_funct(key,map->modulus,
                               unrecoverableError);
  HANDLE_POSSIBLE_UNRECOVERABLE_VOID;
      // check if duplicate key
  current_entry = map->entries[hash_value];
  while(current_entry)
  {
    int comp_result = map->compare_funct(
      key,
      getKeyFromHashmapEntry(current_entry,unrecoverableError),
      unrecoverableError);
    HANDLE_POSSIBLE_UNRECOVERABLE_VOID;
    if (!comp_result)
    {
      error_handler(DUPLICATE_KEY,"insertIntoHashmap");
      return;
    }
    current_entry = getNextFromHashmapEntry(
      current_entry,
      hashmapInternalError);
    HANDLE_POSSIBLE_UNRECOVERABLE_VOID;

  }
      // create entry
  new_entry = newHashmapEntry(key,value,key_destructor,
                              value_destructor,
                              unrecoverableError);
  HANDLE_POSSIBLE_UNRECOVERABLE_VOID;
  current_entry = map->entries[hash_value];
  if (current_entry)
  {
    setNextInHashmapEntry(new_entry,current_entry,
                          hashmapInternalError);
    HANDLE_POSSIBLE_UNRECOVERABLE_VOID;
  }    
  map->entries[hash_value] = new_entry;
  unrecoverable_error_handler_ = NULL;
}

//------------------------------------------------------------
// function to get an element from a hashmap with a given key.
//
// triggered errors: ILLEGAL_ARGUMENTS
//                   ELEMENT_NOT_EXISTING
void *getFromHashmap(Hashmap *map,
                     void *key,
                     void (*error_handler)(int error_type,
                                           void *special))
{
  unsigned hash_value = 0;
  HashmapEntry *entry = NULL;
  void *ret = NULL;

  if (!error_handler)
    error_handler = defaultErrorHandler;
  if ((!map) || (!key))
  {
    error_handler(ILLEGAL_ARGUMENTS,"getFromHashmap");
    return(NULL);
  }
  unrecoverable_error_handler_ = error_handler;
      // calculate hash value
  hash_value = map->hash_funct(key,map->modulus,
                               unrecoverableError);
  HANDLE_POSSIBLE_UNRECOVERABLE_PTR;
  entry = map->entries[hash_value];
  while(entry)
  {
    int comp_result = map->compare_funct(
      key,
      getKeyFromHashmapEntry(entry,unrecoverableError),
      unrecoverableError);
    HANDLE_POSSIBLE_UNRECOVERABLE_PTR;
    if (!comp_result)
      break;
    entry = getNextFromHashmapEntry(entry,
                                    hashmapInternalError);
    HANDLE_POSSIBLE_UNRECOVERABLE_PTR;
  }
  if (!entry)
  {
    error_handler(ELEMENT_NOT_EXISTING,"getFromHashmap");
    return(NULL);
  }
  ret = getValueFromHashmapEntry(entry,
                                 unrecoverable_error_handler_);
  unrecoverable_error_handler_ = NULL;
  return(ret);
}

//------------------------------------------------------------
// function to delete an element in a hashmap with a given key.
//
// triggered errors: ILLEGAL_ARGUMENTS
//                   ELEMENT_NOT_EXISTING
void deleteFromHashmap(Hashmap *map,
                       void *key,
                       void (*error_handler)(int error_type,
                                             void *special))
{
  unsigned hash_value = 0;
  HashmapEntry *current = NULL;
  HashmapEntry *previous = NULL;

  if (!error_handler)
    error_handler = defaultErrorHandler;
  if ((!map) || (!key))
  {
    error_handler(ILLEGAL_ARGUMENTS,"deleteFromHashmap");
    return;
  }
  unrecoverable_error_handler_ = error_handler;
      // calculate hash value
  hash_value = map->hash_funct(key,map->modulus,
                               unrecoverableError);
  HANDLE_POSSIBLE_UNRECOVERABLE_VOID;
  current = map->entries[hash_value];
  while(current)
  {
    int comp_result = map->compare_funct(
      key,
      getKeyFromHashmapEntry(
        current,unrecoverableError),
      unrecoverableError);
    HANDLE_POSSIBLE_UNRECOVERABLE_VOID;
    if (!comp_result)
      break;
    previous = current;
    current = getNextFromHashmapEntry(current,
                                      hashmapInternalError);
    HANDLE_POSSIBLE_UNRECOVERABLE_VOID;
  }
  if (current)
  {
    HashmapEntry *next = getNextFromHashmapEntry(
                            current,
                            hashmapInternalError);
    HANDLE_POSSIBLE_UNRECOVERABLE_VOID;
    if (previous)
    {
      setNextInHashmapEntry(previous,next,
                            hashmapInternalError);
      HANDLE_POSSIBLE_UNRECOVERABLE_VOID;
    }
    else
      map->entries[hash_value] = next;
  }
  else
    error_handler(ELEMENT_NOT_EXISTING,"deleteFromHashmap");
  unrecoverable_error_handler_ = NULL;
}

//============================================================
// HashmapEntry access functions


//------------------------------------------------------------
// constructor for a hashmap entry
//
// triggered errors: ILLEGAL_ARGUMENTS
//                   OUT_OF_MEMORY
HashmapEntry *newHashmapEntry(
  void *key,
  void *value,
  void (*key_destructor)(
    void *key,
    void (*err_handler)(int error_type,void *special)),
  void (*value_destructor)(
    void *value,
    void (*err_handler)(int error_type,void *special)),
  void (*error_handler)(int error_type,void *special))
{
  HashmapEntry *entry = malloc(sizeof(HashmapEntry));

  if (!error_handler)
    error_handler = defaultErrorHandler;

  if (!entry)
  {
    error_handler(OUT_OF_MEMORY,"newHashmapEntry");
    return(NULL);
  }

  if (!key)
  {
    error_handler(ILLEGAL_ARGUMENTS,"newHashmapEntry");
    free(entry);
    return(NULL);
  }
  entry->key = key;
  entry->value = value;
  entry->key_destructor = key_destructor;
  entry->value_destructor = value_destructor;
  entry->next = NULL;
  return(entry);
}

//------------------------------------------------------------
// destructor for a hashmap entry
//
// triggered errors: ILLEGAL_ARGUMENTS
void deleteHashmapEntry(HashmapEntry *entry,
                        void (*error_handler)(int error_type,
                                              void *special))
{
  if (!error_handler)
    error_handler = defaultErrorHandler;

  if (!entry)
  {
    error_handler(ILLEGAL_ARGUMENTS,"deleteHashmapEntry");
    return;
  }
  if (entry->key_destructor)
    entry->key_destructor(entry->key,error_handler);
  if ((entry->value) && (entry->value_destructor))
    entry->value_destructor(entry->value,error_handler);
  free(entry);
  return;
}

//------------------------------------------------------------
// access function to read the key from an entry of a hashmap
//
// triggered errors: ILLEGAL_ARGUMENTS
void *getKeyFromHashmapEntry(HashmapEntry *entry,
                             void (*error_handler)(int error_type,
                                                   void *special))
{
  if (!entry)
  {
    error_handler(ILLEGAL_ARGUMENTS,"getKeyFromHashmapEntry");
    return(NULL);
  }
  return(entry->key);
}

//------------------------------------------------------------
// access function to read the value from an entry of a hashmap
//
// triggered errors: ILLEGAL_ARGUMENTS
void *getValueFromHashmapEntry(HashmapEntry *entry,
                               void (*error_handler)(int error_type,
                                                     void *special))
{
  if (!entry)
  {
    error_handler(ILLEGAL_ARGUMENTS,"getValueFromHashmapEntry");
    return(NULL);
  }
  return(entry->value);
}

//------------------------------------------------------------
// access function to read the pointer to the next element that
// has the same hash-value.
//
// triggered errors: ILLEGAL_ARGUMENTS
void *getNextFromHashmapEntry(HashmapEntry *entry,
                              void (*error_handler)(int error_type,
                                                    void *special))
{
  if (!entry)
  {
    error_handler(ILLEGAL_ARGUMENTS,"getNextFromHashmapEntry");
    return(NULL);
  }
  return(entry->next);
}
//------------------------------------------------------------
// access function to write the pointer to the next element that
// has the same hash-value.
//
// triggered errors: ILLEGAL_ARGUMENTS
void setNextInHashmapEntry(HashmapEntry *entry,
                           HashmapEntry *next,
                           void (*error_handler)(int error_type,
                                                 void *special))
{
  if (!entry)
  {
    error_handler(ILLEGAL_ARGUMENTS,"setNextInHashmapEntry");
    return;
  }
  entry->next = next;
}


//------------------------------------------------------------
// handler function that is called when an unrecoverable error
// occurs. 
void unrecoverableError(int error_type,
                        void *special)
{
  if (!unrecoverable_error_handler_)
    defaultErrorHandler(
      INTERNAL_ERROR,
      "unrecoverable_error_handler_ not set in hashmap.c\n");
  else
    unrecoverable_error_handler_(error_type,special);
  unrecoverable_error_ = error_type;
}

//------------------------------------------------------------
// handler function for internal errors
void hashmapInternalError(int error_type,
                          void *special)
{
  if (!unrecoverable_error_handler_)
    defaultErrorHandler(
      INTERNAL_ERROR,
      "hashmap.c\n");
  else
    unrecoverable_error_handler_(INTERNAL_ERROR,"hashmap.c");
  unrecoverable_error_ = error_type;
}
