#include "hash.h"

/* "operator new + copy ctor" */
static int newNode(Hash* h, Node** nPtr, const void* ptr){

	*nPtr = (Node*)malloc(sizeof(Node));
	if(!*nPtr)
		return E_NOMEM;
	memset(*nPtr, 0, sizeof(Node));

	(*nPtr)->ptr = h->newFn();
	if( !(*nPtr)->ptr ){
		free (*nPtr);
		*nPtr = NULL;
		return E_NOMEM;
	}

	h->cpFn((*nPtr)->ptr, ptr);
	(*nPtr)->next = NULL;
return 0;
}


/* "operator new + asignment" */
static int newNodeAsgn(Hash* h, Node** nPtr, void* ptr){

	*nPtr = (Node*)malloc(sizeof(Node));
	if(!*nPtr)
		return E_NOMEM;
	memset(*nPtr, 0, sizeof(Node));

	(*nPtr)->ptr = ptr;
	(*nPtr)->next = NULL;
return 0;
}


int upsertHash(Hash* h, const void* ptr){
Node* fNode;
int stat;	

	if( !h || !ptr || !h->hFn || !h->eqFn || !h->cpFn )
		return E_BADINPUT;

	h->curHash = h->hFn(ptr);
	fNode = h->arr[h->curHash];

	if(!fNode){
		if( (stat = newNode(h, &h->arr[h->curHash], ptr)) )
			return stat;
		return 0;
	}	
	else{
		while(1){

			/* Update the node being found */
			if( !h->eqFn(fNode->ptr, ptr) ){
				h->cpFn(fNode->ptr, ptr);
				return 0;
			}	

			/* Move to the next node */
			if(fNode->next)
				fNode = fNode->next;
			else{
				/* Insert if no match found */
				if( (stat = newNode(h, (Node**)&fNode->next, ptr)) )
					return stat; 
				return 0;
			}
		}
	}

return 0;
}


static int upsertHashAsgn(Hash* h, void* ptr){
Node* fNode;
int stat;	

	if( !h || !ptr || !h->hFn || !h->eqFn || !h->cpFn )
		return E_BADINPUT;

	h->curHash = h->hFn(ptr);
	fNode = h->arr[h->curHash];

	if(!fNode){
		if( (stat = newNodeAsgn(h, &h->arr[h->curHash], ptr)) )
			return stat;
		return 0;
	}	
	else{
		while(1){

			/* Update the node being found */
			if( !h->eqFn(fNode->ptr, ptr) ){
				h->cpFn(fNode->ptr, ptr);
				return 0;
			}	

			/* Move to the next node */
			if(fNode->next)
				fNode = fNode->next;
			else{
				/* Insert if no match found */
				if( (stat = newNode(h, (Node**)&fNode->next, ptr)) )
					return stat; 
				return 0;
			}
		}
	}

return 0;
}

int findFirstHash(Hash* h, const void* ptr, ThFn eqFn, Iterator* it){
Node* fNode;

	if( !h || !ptr || !it || !h->hFn || !eqFn )
		return E_BADINPUT;

	it->curHash = h->hFn(ptr);
	fNode = h->arr[it->curHash];

	if(!fNode)
		return E_NOTFOUND;	
	else{
		while(1){

			/* Return the node being found */
			if( !eqFn(fNode->ptr, ptr) ){
				it->curNode = fNode;
				it->ptr = fNode->ptr;
				return 0;
			}	

			/* Move to the next node */
			if(fNode->next)
				fNode = fNode->next;
			else{
				/* Nothing found */
				it->curNode = it->ptr = NULL;
				return E_NOTFOUND;
			}
		}
	}

return 0;
}

int findNextHash(Hash* h, const void *ptr, ThFn eqFn, Iterator* it){
Node* fNode;

	if( !h || !ptr || !it || !h->hFn || !eqFn )
		return E_BADINPUT;

	if( !it->curNode )
		return E_NOTFOUND;

	fNode = it->curNode->next;

	if(!fNode)
		return E_NOTFOUND;	
	else{
		while(1){

			/* Return the node being found */
			if( !eqFn(fNode->ptr, ptr) ){
				it->curNode = fNode;
				it->ptr = fNode->ptr;
				return 0;
			}	

			/* Move to the next node */
			if(fNode->next)
				fNode = fNode->next;
			else{
				/* Nothing was found */
				it->curNode = it->ptr = NULL;
				return E_NOTFOUND;
			}
		}
	}

return 0;
}


int getFirstHash(Hash* h, Iterator* it){
Node* fNode;

	if( !h || !it )
		return E_BADINPUT;

	it->curHash=0;
	while(1){

		fNode = h->arr[it->curHash];
		if(fNode){
			it->curNode = fNode;
			it->ptr = fNode->ptr;
			return 0;
		}

		if(it->curHash == HASH_SIZE-1)
			break;
		else
			++it->curHash;
	}	

	/* Nothing found */
	it->curNode = it->ptr = NULL;

return E_END;
}

int getNextHash(Hash* h, Iterator* it){
Node* fNode;

	if( !h || !it )
		return E_BADINPUT;

	if( it->curNode ){

		fNode = it->curNode->next;
		if(fNode){
			it->curNode = fNode;
			it->ptr = fNode->ptr;
			return 0;
		}	
	}

	/* Run out of nodes in hash list - move to the next one */	
	while( it->curHash != HASH_SIZE-1 ){

		++it->curHash;
		fNode = h->arr[it->curHash];

		if( fNode ){
			it->curNode = fNode;
			it->ptr = fNode->ptr;
			return 0;
		}
	}

	/* Nothing was found */
	it->curNode = it->ptr = NULL;

return E_END;
}

#if 0
int getFirstInNextHash(Hash* h, Iterator* it){
Node* fNode;

	if( !h || !it )
		return E_BADINPUT;


	/* Run out of nodes in hash list - move to the next one */	
	while( it->curHash != HASH_SIZE-1 ){

		++it->curHash;
		fNode = h->arr[it->curHash];

		if( fNode ){
			it->curNode = fNode;
			it->ptr = fNode->ptr;
			return 0;
		}
	}

	/* Nothing was found */
	it->curNode = it->ptr = NULL;

return E_END;
}
#endif

int getFirstHashFilter(Hash* h, const void* ptr, ThFn flFn, Iterator* it){
int stat;

	if( !ptr || !flFn )
		return E_BADINPUT;

	if( (stat = getFirstHash(h, it)) )
		return stat;

	while(1){

		/* Match found */
		if( !flFn(it->ptr, ptr) )
			return 0;

		if( (stat = getNextHash(h, it)) )
			return stat;
	}
}

int getNextHashFilter(Hash* h, const void* ptr, ThFn flFn, Iterator* it){
int stat;

        if( !ptr || !flFn )
                return E_BADINPUT;

	while(1){

	        if( (stat = getNextHash(h, it)) )
        	        return stat;

                /* Match found */
                if( !flFn(it->ptr, ptr) )
                        return 0;

        }
}


int copyHash(Hash* src, Hash* trg){
  int stat;
  Iterator it;

  if( !src || !trg )
    return -1;

  if( stat = getFirstHash(src, &it) ){
    if( stat == E_END )
      return 0;
    else
      return stat;
  }

  while(1){
    
    if( stat = upsertHashAsgn( trg, it.ptr ) )
      return stat;

    if( stat = getNextHash(src, &it) ){
      if( stat == E_END )
	return 0;
      else
	return stat;
    }

  }


}
