//
// anyRemote
// a wi-fi or bluetooth remote for your PC.
//
// Copyright (C) 2006-2016 Mikhail Fedotov <anyremote@mail.ru>
//
// 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 3 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 St, Fifth Floor, Boston, MA  02110-1301  USA
//

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

#include "common.h"
#include "hash.h"
#include "list.h"

//
// Hash related wrappers
//

#ifdef USE_GLIB

#include <glib.h>

HashTable* hashNew(DestroyCallback valueDestroyFunc)
{
     return g_hash_table_new_full(g_str_hash, g_str_equal, free, valueDestroyFunc);
}

void* hashFind(HashTable* hash, const char *key)
{
    if (hash && key) {
        return g_hash_table_lookup(hash, key);
    }
    return NULL;
}

void hashReplace(HashTable* hash, const char* key, void *value)
{
    if (hash && key) {
        g_hash_table_replace(hash, strdup(key), value);
    }
}

int hashRemove(HashTable* hash, const char* key)
{
    if (hash && key) {
        if (g_hash_table_remove(hash, key)) {
	        return RC_OK;
	}
    }
    return RC_NOK;
}

void hashDestroy(HashTable* hash)
{
    if (hash) {
        g_hash_table_destroy(hash);
    }
}

void hashForeach(HashTable* hash, HashForeachFunc func, void *data)
{
    if (hash) {
        g_hash_table_foreach(hash, func, data);
    }
}

#else

static unsigned int str2hash(const char *str)
{
    unsigned int hash = 0;
    for(; *str; str++) {
        hash = 31*hash + *str;
    }
    return hash;
}

static void* get(struct StrHashTable *table, const char *key)
{
    unsigned int bucket = str2hash(key)%NR_BUCKETS;
    struct StrHashNode *node = table->buckets[bucket];
    
    while (node) {
        if (strcmp(key,node->key) == 0) {
            return node->value;
        }
	node = node->next;
    }
    return NULL;
}

static int insert(struct StrHashTable *table, char *key, void *value)
{
    
    unsigned int bucket = str2hash(key)%NR_BUCKETS;
    //printf("insert %s to %d\n", key, bucket);

    struct StrHashNode **tmp = &(table->buckets[bucket]);
    while (*tmp) {
        if (strcmp(key,(*tmp)->key) == 0) {
            break;
        }
	tmp = &((*tmp)->next);
    }
    
    struct StrHashNode *node = NULL;
    
    if (*tmp) {
        //printf("insert %s remove old\n", key);
        free((*tmp)->key);
 
	if(table->free_value != NULL) {
            table->free_value((*tmp)->value);
        }
	node = *tmp;
    } else {
        node = malloc(sizeof(struct StrHashNode));
        if(node == NULL) {
            return -1;
        }
	node->next = NULL;
        *tmp = node;
    }
    node->key   = key;
    node->value = value;

    return 0;
}

static int removeNode(struct StrHashTable *table, const char *key)
{
    unsigned int bucket = str2hash(key)%NR_BUCKETS;
    
    struct StrHashNode *tmp;
    struct StrHashNode *tmp_prev;
    
    tmp_prev = NULL;
    tmp = table->buckets[bucket];
    while (tmp) {
        if (strcmp(key,tmp->key) == 0) {
            break;
        }
	tmp_prev = tmp;
	tmp = tmp->next;
    }
    
    if(tmp) {
    
        free(tmp->key);

	if(table->free_value != NULL) {
            table->free_value(tmp->value);
        }
	
	if (tmp_prev) {
	    tmp_prev->next = tmp->next;
	} else {  // remove first
	    table->buckets[bucket] = tmp->next;
	}
	
	free(tmp);
	
	return RC_OK;
    }

    return RC_NOK;
}

static void destroyData(struct StrHashTable *table)
{
    int i = 0;
    for (;i<NR_BUCKETS;i++) {

	struct StrHashNode *tmp = table->buckets[i];
	
	while (tmp) {
	    
            free(tmp->key);

	    if(table->free_value != NULL) {
                table->free_value(tmp->value);
            }
            
	    struct StrHashNode *t = tmp;
	   
	    tmp = tmp->next;
	    
	    free(t);
	}
    }
}

static void foreach(struct StrHashTable *table, HashForeachFunc func, void *data)
{
    int i = 0;
    for (;i<NR_BUCKETS;i++) {

	struct StrHashNode *tmp = table->buckets[i];   
	if (!tmp) continue;
	
	//printf("foreach %d\n", i);
	
	while (tmp) {
	    if (tmp->key && tmp->value) {
	        func(tmp->key, tmp->value, data);
	    //} else {
	    //    printf("foreach %d corrupted hash data\n", i);
	    }
	    tmp = tmp->next;
	}
    }
}

HashTable * hashNew(DestroyCallback valueDestroyFunc)
{
    HashTable * hash = malloc(sizeof(struct StrHashTable));
    
    int i = 0;
    for (;i<NR_BUCKETS;i++) {
    	hash->buckets[i] = NULL;
    }
    hash->free_value = valueDestroyFunc;

    return hash;
}

void* hashFind(HashTable* hash, const char *key)
{
    if (hash && key) {
        return get(hash, key);
    }
    return NULL;
}

void hashReplace(HashTable* hash, const char* key, void *value)
{
    if (hash && key) {
        insert(hash, strdup(key), value);
    }
}

int hashRemove(HashTable* hash, const char* key)
{
    if (hash && key) {
        return removeNode(hash, key);
    }
    return RC_NOK;
}

void hashDestroy(HashTable* hash)
{
    if (hash) {
        destroyData(hash);
	free(hash);
	hash = NULL;
    }
}

void hashForeach(HashTable* hash, HashForeachFunc func, void *data)
{
    if (hash) {
        foreach(hash, func, data);
    }
}

#endif
