/***************************************************************************
 *   Copyright (C) 2000-2004 by                                            *
 *   Jean-Christophe Hoelt <jeko@ios-software.com>                         *
 *   Guillaume Borios <gyom@ios-software.com>                              *
 *                                                                          *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include "goomsl_hash.h"
#include <string.h>
#include <stdlib.h>

static GoomHashEntry *entry_new(const char *key, HashValue value) {

  int len = strlen(key);
    GoomHashEntry *entry = (GoomHashEntry*)malloc(sizeof(GoomHashEntry));

    entry->key = (char *)malloc(len+1);
    memcpy(entry->key,key,len+1);
    entry->value = value;
    entry->lower = NULL;
    entry->upper = NULL;

    return entry;
}

static void entry_free(GoomHashEntry *entry) {
    if (entry!=NULL) {
        entry_free(entry->lower);
        entry_free(entry->upper);
        free(entry->key);
        free(entry);
    }
}

static void entry_put(GoomHashEntry *entry, const char *key, HashValue value) {
    int cmp = strcmp(key,entry->key);
    if (cmp==0) {
        entry->value = value;
    }
    else if (cmp > 0) {
        if (entry->upper == NULL)
            entry->upper = entry_new(key,value);
        else
            entry_put(entry->upper, key, value);
    }
    else {
        if (entry->lower == NULL)
            entry->lower = entry_new(key,value);
        else
            entry_put(entry->lower, key, value);
    }
}

static HashValue *entry_get(GoomHashEntry *entry, const char *key) {

    int cmp;
    if (entry==NULL)
        return NULL;
    cmp = strcmp(key,entry->key);
    if (cmp > 0)
        return entry_get(entry->upper, key);
    else if (cmp < 0)
        return entry_get(entry->lower, key);
    else
        return &(entry->value);
}

GoomHash *goom_hash_new(void) {
    GoomHash *_this = (GoomHash*)malloc(sizeof(GoomHash));
    _this->root = NULL;
  _this->number_of_puts = 0;
    return _this;
}

void goom_hash_free(GoomHash *_this) {
    entry_free(_this->root);
    free(_this);
}

void goom_hash_put(GoomHash *_this, const char *key, HashValue value) {
  _this->number_of_puts += 1;
    if (_this->root == NULL)
        _this->root = entry_new(key,value);
    else
        entry_put(_this->root,key,value);
}

HashValue *goom_hash_get(GoomHash *_this, const char *key) {
  if (_this == NULL) return NULL;
    return entry_get(_this->root,key);
}

void goom_hash_put_int(GoomHash *_this, const char *key, int i) {
    HashValue value;
    value.i = i;
    goom_hash_put(_this,key,value);
}

void goom_hash_put_float(GoomHash *_this, const char *key, float f) {
    HashValue value;
    value.f = f;
    goom_hash_put(_this,key,value);
}

void goom_hash_put_ptr(GoomHash *_this, const char *key, void *ptr) {
    HashValue value;
    value.ptr = ptr;
    goom_hash_put(_this,key,value);
}

/* FOR EACH */

static void _goom_hash_for_each(GoomHash *_this, GoomHashEntry *entry, GH_Func func)
{
  if (entry == NULL) return;
  func(_this, entry->key, &(entry->value));
  _goom_hash_for_each(_this, entry->lower, func);
  _goom_hash_for_each(_this, entry->upper, func);
}

void goom_hash_for_each(GoomHash *_this, GH_Func func) {
  _goom_hash_for_each(_this, _this->root, func);
}

int goom_hash_number_of_puts(GoomHash *_this) {
  return _this->number_of_puts;
}
