#include "header.h"



long hash_key(char songtitle[], char interpreter[], long hash_max)

{

    unsigned long i, index = 0;

    for (i = 0; i < strlen(songtitle); ++i)

        index = 64 * index + (long)(songtitle[i]);

    for (i = 0; i < strlen(interpreter); ++i)

        index = 64 * index + (long)(interpreter[i]);

    return index % hash_max;

}

hashcontainer_t *create_hash(long hashsize)

{

    unsigned long i;

    hashcontainer_t *new = 0;

    if (!(new = malloc(sizeof(hashcontainer_t))))

    {

        fprintf(stderr, "Memory allocation error in create_hash()");

        exit(-1);

    }

    if (!(new->harrays = calloc(hashsize, sizeof(hasharray_t))))

    {

        fprintf(stderr, "Memory allocation error in create_hash()");

        exit(-1);

    }

    new->hashsize = hashsize;

    for (i = 0; i < hashsize; i++)

    {

        new->harrays[i];

    }

    return new;

}





void delete_hash(hashcontainer_t *hashcontainer)

{

    unsigned long i;

    for (i = 0; i < hashcontainer->hashsize; i++)

    {

        if (hashcontainer->harrays[i].entries)

        {

            free(hashcontainer->harrays[i].entries);

            hashcontainer->harrays[i].entries = 0;

            hashcontainer->harrays[i].num_entries = 0;

        }

    }

    free(hashcontainer->harrays);

    hashcontainer->harrays = 0;

    hashcontainer->hashsize = 0;

}





void insert_entry(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[])

{

    unsigned long index = hash_key(songtitle, interpreter, hashcontainer->hashsize);

    hashcontainer->harrays[index].num_entries++;

    hashcontainer->harrays[index].entries = realloc(hashcontainer->harrays[index].entries, hashcontainer->harrays[index].num_entries * sizeof(hashentry_t));

    strcpy(hashcontainer->harrays[index].entries[hashcontainer->harrays[index].num_entries - 1].songtitle, songtitle);

    strcpy(hashcontainer->harrays[index].entries[hashcontainer->harrays[index].num_entries - 1].interpreter, interpreter);

}





hashentry_t *search_entry(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[])

{

    unsigned long index = hash_key(songtitle, interpreter, hashcontainer->hashsize);

    unsigned long len = hashcontainer->harrays[index].num_entries, i;

    for (i = 0; i < len; i++)

    {

        if ((strcmp(hashcontainer->harrays[index].entries[i].interpreter, interpreter) == 0) && (strcmp(hashcontainer->harrays[index].entries[i].songtitle, songtitle) == 0))

            return (hashcontainer->harrays[index].entries)+i;

    }

    return 0;

}





void delete_entry(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[])

{

    unsigned long index = hash_key(songtitle, interpreter, hashcontainer->hashsize);

    unsigned long len = hashcontainer->harrays[index].num_entries, i;

    for (i = 0; i < len; i++)

    {

        if ((strcmp(hashcontainer->harrays[index].entries[i].interpreter, interpreter) == 0) && (strcmp(hashcontainer->harrays[index].entries[i].songtitle, songtitle) == 0))

        {

            if (i == 0 && len == 1)

            {

                free(hashcontainer->harrays[index].entries);

                hashcontainer->harrays[index].entries = 0;

                hashcontainer->harrays[index].num_entries = 0;

            }

            else

            {

                if (i != len - 1)

                    memcpy(hashcontainer->harrays[index].entries + i, hashcontainer->harrays[index].entries + i + 1, (len - i - 1)* sizeof(hashentry_t));

                hashcontainer->harrays[index].entries = realloc(hashcontainer->harrays[index].entries, --hashcontainer->harrays[index].num_entries * sizeof(hashentry_t));

            }

        }

    }

}





void print_hash(hashcontainer_t *hashcontainer)

{

    unsigned long i, j;

    for (i = 0; i < hashcontainer->hashsize; i++)

    {

        fprintf(stdout, "Hash index %ld\n", i);

        for (j = 0; j < hashcontainer->harrays[i].num_entries; j++)

        {

            fprintf(stdout, "%s\t%s\n", hashcontainer->harrays[i].entries[j].songtitle, hashcontainer->harrays[i].entries[j].interpreter);

        }

        fprintf(stdout, "\n");

    }

}





long count_entries(hashcontainer_t *hashcontainer)

{

    unsigned long i, j, count = 0;

    for (i = 0; i < hashcontainer->hashsize; i++)

    {

        for (j = 0; j < hashcontainer->harrays[i].num_entries; j++)

        {

                count++;

        }

    }

    return count;

}





long count_entries_with_songtitle(hashcontainer_t *hashcontainer, char songtitle[])

{

    unsigned long i, j, count = 0;

    for (i = 0; i < hashcontainer->hashsize; i++)

    {

        for (j = 0; j < hashcontainer->harrays[i].num_entries; j++)

        {

            if (strcmp(hashcontainer->harrays[i].entries[j].songtitle, songtitle) == 0)

                count++;

        }

    }

    return count;

}





void insert_entry_ext_exp(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[], long(*hashkey_gen)(hashentry_t *entry, long hash_max))

{

    hashentry_t *new = malloc(sizeof(hashentry_t));

    strcpy(new->songtitle, songtitle);

    strcpy(new->interpreter, interpreter);

    unsigned long index = hashkey_gen(new, hashcontainer->hashsize);



    hashcontainer->harrays[index].num_entries++;

    hashcontainer->harrays[index].entries = realloc(hashcontainer->harrays[index].entries, hashcontainer->harrays[index].num_entries * sizeof(hashentry_t));

    memcpy(hashcontainer->harrays[index].entries + hashcontainer->harrays[index].num_entries - 1, new, sizeof(hashentry_t));

}





void insert_entry_ext(hashcontainer_t *hashcontainer, hashentry_t *new, long(*hashkey_gen)(hashentry_t *entry, long hash_max))

{

    unsigned long index = hashkey_gen(new, hashcontainer->hashsize);



    hashcontainer->harrays[index].num_entries++;

    hashcontainer->harrays[index].entries = realloc(hashcontainer->harrays[index].entries, hashcontainer->harrays[index].num_entries * sizeof(hashentry_t));

    memcpy(hashcontainer->harrays[index].entries + hashcontainer->harrays[index].num_entries - 1, new, sizeof(hashentry_t));

}





hashentry_t *search_entry_ext(hashcontainer_t *hashcontainer, char songtitle[], char interpreter[], long(*hashkey_gen)(hashentry_t *entry, long hash_max))

{

    hashentry_t *new = malloc(sizeof(hashentry_t));

    strcpy(new->songtitle, songtitle);

    strcpy(new->interpreter, interpreter);

    unsigned long index = hashkey_gen(new, hashcontainer->hashsize);



    unsigned long i;

    for (i = 0; i < hashcontainer->harrays[index].num_entries; i++)

    {

        if ((strcmp(hashcontainer->harrays[index].entries[i].interpreter, interpreter) == 0) && (strcmp(hashcontainer->harrays[index].entries[i].songtitle, songtitle) == 0))

            return (hashcontainer->harrays[index].entries)+i;

    }

    return 0;

}





void rehash1(hashcontainer_t *hashcontainer, long(*hashkey_gen)(hashentry_t *entry, long hash_max), long hash_max)

{

    hashcontainer_t *new = create_hash(hash_max);

    unsigned long i, j;

    for (i = 0; i < hashcontainer->hashsize; i++)

    {

        for (j = 0; j < hashcontainer->harrays[i].num_entries; j++)

        {

            insert_entry_ext(new, hashcontainer->harrays[i].entries + j, hashkey_gen);

        }

    }

    delete_hash(hashcontainer);

    hashcontainer->harrays = new->harrays;

    hashcontainer->hashsize = new->hashsize;

}





void rehash2(hashcontainer_t *hashcontainer, long(*hashkey_gen)(hashentry_t *entry, long hash_max), long hash_max)

{

    unsigned long i, j, count = 0, len = count_entries(hashcontainer);

    hashentry_t temp[len];

    for (i = 0; i < hashcontainer->hashsize; i++)

    {

        for (j = 0; j < hashcontainer->harrays[i].num_entries; j++)

        {

            strcpy(temp[count].songtitle, hashcontainer->harrays[i].entries[j].songtitle);

            strcpy(temp[count].interpreter, hashcontainer->harrays[i].entries[j].interpreter);

            count++;

        }

    }

    delete_hash(hashcontainer);

    hashcontainer_t *new = create_hash(hash_max);

    for (i = 0; i < len; i++)

    {

        insert_entry_ext(new, &temp[i], hashkey_gen);

    }

    hashcontainer->harrays = new->harrays;

    hashcontainer->hashsize = new->hashsize;

    free(new);

}

