//This code is copyright Andrew Story (Kaslai/Aslai) All rights reserved.
//This and derivative works can be distributed as seen fit,
//so long as this notice remains unchanged and present

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdarg.h>
#include "hash_internal.h"




stringList::stringList()
{
    v = 0;
    value = 0;
    t = (char*) malloc( 0 );
    next = 0;
    last = this;
    parent = this;
    writing = false;
}
void stringList::remAll()
{
    if( next != 0 )
        next->remAll();
    delete this;
}
stringList* stringList::push( char* input, char* val )
{
    while( writing );
    writing = true;
    stringList* nex = new stringList;
    if( notstring ){
        nex->v = val;
    } else {
        nex->v = (char*) malloc( strlen( val ) + 1 );
        strcpy( nex->v, val );
    }
    nex->value = (char*) malloc( strlen( input ) + 1 );
    strcpy( nex->value, input );
    parent->last->next = nex;
    parent->last = nex;
    nex->parent = parent;
    writing = false;
    return nex;
}
 stringList::~stringList()
{
    if( value != 0               ) free( value );
    if( v != 0 && notstring == 0 ) free( v );
}
hashTable::hashTable()
{
    notstring = false;
    track = (int*) malloc( 0 );
    writing = false;
    load = 0;
    size = 100;
    data = (stringList**) malloc( size * sizeof( stringList* ) );
    for( int i = 0; i < size; data[i++] = 0 );
    resize( size );
}
hashTable::hashTable( bool notstrin )
{
    notstring = notstrin;
    track = (int*) malloc( 0 );
    writing = false;
    load = 0;
    size = 100;
    data = (stringList**) malloc( size * sizeof( stringList* ) );
    for( int i = 0; i < size; data[i++] = 0 );
    resize( size );
}
void hashTable::constructor( bool notstrin ){
    notstring = notstrin;
    track = (int*) malloc( 0 );
    writing = false;
    load = 0;
    size = 100;
    printf("hahaGLOOP");
    data = (stringList**) malloc( size * sizeof( stringList* ) );
    for( int i = 0; i < size; data[i++] = 0 );
    resize( size );
}

hashTable::~hashTable()
{
    free( track );
    for( int i = 0; i < size; i++ ){
        if( data[i] != 0 ){
            data[i]->remAll();
        }
    }
    free( data );
}
unsigned int hashTable::hash( char* input)
{
    unsigned int ret = 1;
    for( int i = 0; input[i] != 0; i ++ )
    {ret = ( ret * input[i] + (i * i) ) % size;}
    printf("Size: %i\n", size );
    ret %= size;
    return ret;
}
unsigned int hashTable::hashMem( char* input, int len)
{
    unsigned int ret = 1;
    for( int i = 0; i < len; i ++ )
    {ret = ( ret * input[i] + (i * i) ) % size;}
    return ret;
}
void hashTable::freeValue( char* input )
{
    while( writing );
    writing = true;
    unsigned int place = hash( input );
    if( data[place] == 0 ){
        return;
    }
    else
    {
        load --;
        stringList* tmp = data[place];
        stringList* tmpprev = data[place];

        do {
            if( strcmp( tmp->value, input ) == 0 )
            {
                if( tmp == data[place] ){
                    if(  tmp->next != 0 )
                        {tmp->value = tmp->next->value; stringList* nxt = tmp->next->next; delete tmp->next; tmp->next = nxt; }
                    else {delete tmp; data[place] = 0; } }
                else { tmpprev->next = tmp->next; delete tmp; }
            }
            tmpprev = tmp;
            tmp = tmp->next;
        } while( tmp != 0 );
    }
    writing = false;
}
void hashTable::setValue( char* input, char* val )
{
    if( input[0] == 0 ) return;
    while( writing );
    writing = true;
    load ++;
    unsigned int place = hash( input );
    track[place]++;
    if( data[place] == 0 ){
        data[place] = new stringList;
        data[place]->notstring = notstring;

        data[place]->value = (char*) malloc( strlen( input ) + 1 );
        strcpy( data[place]->value, input );
        if( notstring ){
            data[place]->v = val;
        } else {
            data[place]->v = (char*) malloc( strlen( val ) + 1 );
            strcpy( data[place]->v, val );
        }
    }
    else
    {
        data[place]->push( input, val );
    }
    writing = false;
}
char* hashTable::lookup( char* input )
{
    unsigned int place = hash( input );
    if( data[place] == 0 ) return "";
    else
    {

        stringList* tmp = data[place];
        while( tmp != 0 ) {
            if( strcmp( tmp->value, input ) == 0 ) return tmp->v;
            tmp = tmp->next;
        }
    }
    return "";
}
void hashTable::resize( int newSize )
{
    stringList** oldData = data;
    int oldSize = size;
    size = newSize;
    data = (stringList**) malloc( size * sizeof( stringList* ) );
    memset( data, 0, size*sizeof( stringList* ) );

    for( int i = 0; i < oldSize; i++)
    {
        if( oldData[i] != 0 )
        {
            stringList* tmp = oldData[i];
            while( tmp != 0 )
            {

                setValue( tmp->value, tmp->v );
                tmp = tmp->next;
            }
            oldData[i]->remAll();
        }
    }
    free( track );
    track = (int*) malloc( size * sizeof( int ) );
    for( int i = 0; i < size; track[i++] = 0 );
    free( oldData );
}

stringList& hashTable::operator[] (const char* input){
    //while( writing );
    //writing = true;

    if( load+1 > size ){
        resize( size*2 );
    }
    unsigned int place = hash( (char*)input );
    if( data[place] == 0 ){

        load ++;
        track[place]++;
        data[place] = new stringList;
        data[place]->notstring = notstring;

        data[place]->value = (char*) malloc( strlen( input ) + 1 );
        strcpy( data[place]->value, input );
        if( notstring ){
            data[place]->v = 0;
        }else{
            data[place]->v = (char*) malloc( 1 );
            data[place]->v[0] = 0;
        }

        writing = false;
        return *data[place];
    }
    else
    {
        stringList* tmp = data[place];
        while( tmp != 0 ) {
            if( strcmp( tmp->value, input ) == 0 ) break;
            tmp = tmp->next;
        }
        if( tmp == 0 ){
            writing = false;
            return *(data[place]->push( (char*)input, "" ));
        }
        writing = false;
        return *tmp;
    }
    writing = false;
}

stringList& stringList::operator=( const char* a ){
    if( notstring ){
        v = (char*)a;
    }
    else{
        v = (char*) realloc( v, strlen( a )+1);
        strcpy( v, a );
    }

    return *this;
}
stringList& stringList::operator=( int a ){
    v = (char*)a;
    return *this;
}
stringList& stringList::operator=( void* a ){
    v = (char*)a;
    return *this;
}


bool stringList::operator==( char* a ){
    if( notstring ){
        return ((int) v == (int) a );
    }
    if( strcmp( v, a ) == 0 ) return 1;
    return 0;
}
bool stringList::operator==( stringList& a ){
    if( notstring ){
        return ((int) v == (int) a.v );
    }
    if( strcmp( v, a.v ) == 0 ) return 1;
    return 0;
}
char* stringList::operator*(){
    return v;
}
stringList::operator char*(){
    return v;
}
stringList::operator const char*(){
    return v;
}
stringList::operator int(){
    return (int)v;
}
stringList::operator const int(){
    return (int)v;
}
stringList::operator int*(){
    return (int*)&v;
}
stringList::operator const int*(){
    return (int*)&v;
}
hashTable& hashTable::operator=(hashTable& ht )
{
    delete this;
    notstring = ht.notstring;
    track = (int*) malloc( 0 );
    writing = false;
    load = ht.load;
    size = ht.size;
    data = (stringList**) malloc( size * sizeof( stringList* ) );
    for( int i = 0; i < size; data[i++] = 0 );
    resize( size );
    for( int i=0; i < ht.size; i ++ ){
        if( ht.data[i] != 0 ){
            stringList* tmp = (ht.data[i]);
            while( tmp != 0 ){
                if( data[i] == 0 ){
                    track[i]++;
                    data[i] = new stringList;
                    data[i]->notstring = notstring;

                    data[i]->value = (char*) malloc( strlen( tmp->value ) + 1 );
                    strcpy( data[i]->value, tmp->value );
                    if( notstring ){
                        data[i]->v = tmp->v;
                    }else{
                        data[i]->v = (char*) malloc( strlen( tmp->v ) + 1 );
                        strcpy( data[i]->v, tmp->v );
                    }
                }
                else{
                    data[i]->push( tmp->value, tmp->v );
                }
                tmp = tmp->next;
            }
        }
    }
}