#include "config.h"
#include "saphire_hash.h"
#include "saphire_debug.h"
#include <stdio.h>
#include <string.h>

///////////////////////////////////////////////////////////////////////
// 内部関数
///////////////////////////////////////////////////////////////////////
static hash_it* hash_it_new(char* key, void* item, hash_it* coll_it, hash_it* next_it) 
{
   hash_it* obj = (hash_it*)MALLOC(sizeof(hash_it));

   obj->mKey = STRDUP(key);

   obj->mItem = item;
   obj->mCollisionIt = coll_it;

   obj->mNextIt = next_it;   

   return obj;
}
   
static void hash_it_release(hash_it* self)
{
   FREE(self->mKey);
      
   FREE(self);
}
         
///////////////////////////////////////////////////////////////////////
// 初期化
///////////////////////////////////////////////////////////////////////
#ifdef MDEBUG

hash_obj* hash_new_debug(int size, const char* fname, int line, const char* func_name)
{
   hash_obj* self = (hash_obj*)CheckMemLeak_Malloc(sizeof(hash_obj), fname, line, func_name);
   
   self->mTableSize = size;
   self->mTable = (hash_it**)MALLOC(sizeof(hash_it*) * size);
   memset(self->mTable, 0, sizeof(hash_it*)*size);

   self->mEntryIt = NULL;

   self->mCounter = 0;

   return self;
}

#else

hash_obj* hash_new(int size)
{
   hash_obj* self = (hash_obj*)MALLOC(sizeof(hash_obj));
   
   self->mTableSize = size;
   self->mTable = (hash_it**)MALLOC(sizeof(hash_it*) * size);
   memset(self->mTable, 0, sizeof(hash_it*)*size);

   self->mEntryIt = NULL;

   self->mCounter = 0;

   return self;
}

#endif

///////////////////////////////////////////////////////////////////////
// 解放
///////////////////////////////////////////////////////////////////////
void hash_delete(hash_obj* self)
{
   hash_it* it = self->mEntryIt;

   while(it) {
      hash_it* next_it = it->mNextIt;
      hash_it_release(it);
      it = next_it;
   }
   
   FREE(self->mTable);
   
   FREE(self);
}

///////////////////////////////////////////////////////////////////////
// ハッシュアイテムを返す
///////////////////////////////////////////////////////////////////////
static unsigned int get_hash_value(hash_obj* self, char* key)
{
   unsigned int i = 0;
   while(*key) {
      i += *key;
      key++;
   }
   return i % self->mTableSize;
}
   
void hash_put(hash_obj* self, char* key, void* item)
{
    unsigned int hash_key = get_hash_value(self, key);
    
    hash_it* it = self->mTable[ hash_key ];
    while(it) {
        if(strcmp(key, it->mKey) == 0) {
            it->mItem = item;
            return;
        }
        it = it->mCollisionIt;
    }
    
    hash_it* new_it
        = hash_it_new(key, item, self->mTable[hash_key], self->mEntryIt);
    
    self->mTable[hash_key] = new_it;
    self->mEntryIt = new_it;
}

///////////////////////////////////////////////////////////////////////
// ハッシュアイテムを削除
///////////////////////////////////////////////////////////////////////
static void erase_from_list(hash_obj* self, hash_it* rit)
{
   if(rit == self->mEntryIt) {
      self->mEntryIt = rit->mNextIt;
   }
   else {
      hash_it* it = self->mEntryIt;

      while(it->mNextIt) {
         if(it->mNextIt == rit) {
            it->mNextIt = it->mNextIt->mNextIt;
            break;
         }
            
         it = it->mNextIt;
      }
   }
}
         
void hash_erase(hash_obj* self, char* key)
{
   const unsigned int hash_value = get_hash_value(self, key);
   hash_it* it = self->mTable[hash_value];
   
   if(it == NULL)
      ;
   else if(strcmp(it->mKey, key) == 0) {
      hash_it* it2 = self->mEntryIt;   
      self->mTable[ hash_value ] = it->mCollisionIt;

      erase_from_list(self, it);
      
      hash_it_release(it);
   }
   else {
      hash_it* prev_it = it;
      it = it->mCollisionIt;
      while(it) {
         if(strcmp(it->mKey, key) == 0) {
            prev_it->mCollisionIt = it->mCollisionIt;
            erase_from_list(self, it);
            hash_it_release(it);
            
            break;
         }
         
         prev_it = it;
         it = it->mCollisionIt;
      }
   }
}

///////////////////////////////////////////////////////////////////////
// ハッシュアイテムをクリア
///////////////////////////////////////////////////////////////////////
void hash_clear(hash_obj* self)
{
   int i;
   int max;
   hash_it* it = self->mEntryIt;

   while(it) {
      hash_it* next_it = it->mNextIt;
      hash_it_release(it);
      it = next_it;
   }

   memset(self->mTable, 0, sizeof(hash_it*)*self->mTableSize);
   
   self->mEntryIt = NULL;
}

///////////////////////////////////////////////////////////////////////
// ハッシュアイテムを置き換える
///////////////////////////////////////////////////////////////////////
void hash_replace(hash_obj* self, char* key, void* item)
{
   hash_it* it = self->mTable[get_hash_value(self, key)];

   if(it) {
      do {
         if(strcmp(it->mKey, key) == 0) {
            it->mItem = item;
            break;
         }
   
         it = it->mCollisionIt;
      } while(it);
   }
}

///////////////////////////////////////////////////////////////////////
// ハッシュテーブルを表示
///////////////////////////////////////////////////////////////////////
void hash_show(hash_obj* self, char* fname)
{
   char tmp[4096];
   int i;
   int max;
   hash_it* it;
   xstrncpy(tmp, "", 4096);
   
   max = self->mTableSize;
   for(i=0; i<max; i++) {
      snprintf(tmp + strlen(tmp), 4096-strlen(tmp), "table[%d]: ", i);
      
      it = self->mTable[i];
      while(it) {
         snprintf(tmp + strlen(tmp), 4096-strlen(tmp), "item[\"%s\"], ", it->mKey);
         it = it->mCollisionIt;
      }
    
    xstrncat(tmp, "\n", 4096);
   }
    
    FILE* f = fopen(fname, "w");
    fprintf(f, "%s", tmp);
    fclose(f);
}

///////////////////////////////////////////////////////////////////////
// ハッシュアイテムを返す
///////////////////////////////////////////////////////////////////////
void* hash_item(hash_obj* self, char* key)
{
   hash_it* it = self->mTable[ get_hash_value(self, key) ];

   while(it) {
      if(strcmp(key, it->mKey) == 0) return it->mItem;
      it = it->mCollisionIt;
   }

   return NULL;
}

///////////////////////////////////////////////////////////////////////
// ハッシュアイテムのアドレスを返す
///////////////////////////////////////////////////////////////////////
void* hash_item_addr(hash_obj* self, char* key)
{
   hash_it* it = self->mTable[ get_hash_value(self, key) ];
   while(it) {
      if(strcmp(key, it->mKey) == 0) return &it->mItem;
      it = it->mCollisionIt;
   }

   return NULL;
}
   
///////////////////////////////////////////////////////////////////////
// 引数のアイテムのキーを返す
///////////////////////////////////////////////////////////////////////
char* hash_key(hash_obj* self, void* item)
{
   hash_it* it = self->mEntryIt;

   while(it) {
      if(it->mItem == item) return it->mKey;
      it = it->mNextIt;
   }

   return NULL;
}

///////////////////////////////////////////////////////////////////////
// アイテムの数を返す
///////////////////////////////////////////////////////////////////////
int hash_count(hash_obj* self)
{
   int result = 0;
   hash_it* it = self->mEntryIt;

   while(it) {
      result++;
      it = it->mNextIt;
   }
               
   return result;
}   

hash_it* hash_loop_begin(hash_obj* self) { return self->mEntryIt; }
void* hash_loop_item(hash_it* it) { return it->mItem; }
char* hash_loop_key(hash_it* it) { return it->mKey; }
hash_it* hash_loop_next(hash_it* it) { return it->mNextIt; }

