/*
 * (C) Copyright The MITRE Corporation 1999  All rights reserved.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * The program provided "as is" without any warranty express or
 * implied, including the warranty of non-infringement and the implied
 * warranties of merchantibility and fitness for a particular purpose.
 * The Copyright owner will not be liable for any damages suffered by
 * you as a result of using the Program. In no event will the Copyright
 * owner be liable for any special, indirect or consequential damages or
 * lost profits even if the Copyright owner has been advised of the
 * possibility of their occurrence.
 *
 * Please see release.txt distributed with this file for more information.
 *
 */

// Tom Kneeland (01/21/2000)
//
//  Implementation of a simple Hash Table.  All objects are stored as 
//  MITREObject pointers and hashed using the provided value.  Collisions
//  are solved using a linked list for each hash bin.
//
// Modification History:
// Who  When        What

#include "HashTable.h"
#include <iostream.h>

HashTable::HashTable()
{
    for (Int32 binIndex=0; binIndex < HASHTABLE_SIZE; binIndex++)
        table[binIndex] = NULL;
}

//
//Destroy the hash table.  Loop through all the bins, deleting all the objects
//and list items.
//
HashTable::~HashTable()
{
    Int32 binIndex = 0;
    HashItem* currentItem = NULL;
    HashItem* nextItem = NULL;

    for (binIndex=0;binIndex<HASHTABLE_SIZE;binIndex++)
    {
        currentItem = table[binIndex];
        while (currentItem != NULL)
        {
            nextItem = currentItem->nextItem;
            delete currentItem->objPtr;
            delete currentItem;
            currentItem = nextItem;
        }
    }
}

//
//Compute the bin from the Hash value, and store the object in the table
//
void HashTable::add(MITREObject* obj, Int32 hashValue)
{
    Int32 bin = hashValue % HASHTABLE_SIZE;
    HashItem* newHashItem = new HashItem;
    HashItem* existingItem = NULL;

    //Create a new Hash Object to hold the MITREObject being inserted
    newHashItem->objPtr = obj;
    newHashItem->hashValue = hashValue;
    newHashItem->prevItem = NULL;
    newHashItem->nextItem = NULL;

    //Addition is simple if there is nothing in the bin
    if (table[bin] == NULL)
        table[bin] = newHashItem;
    else
    {
        //The bin has at least one item in it already, so check the list to
        //make sure the item doesn't already exit.  If it does, replace it,
        //else just prepend a new item.
        if ((existingItem = retrieveHashItem(hashValue)) != NULL)
        {
            delete existingItem->objPtr;
            existingItem->objPtr = obj;
        }
        else
        {
            newHashItem->nextItem = table[bin];
            table[bin]->prevItem = newHashItem;
            table[bin] = newHashItem;
        }
    }
}

//
//Locate the object specified by the hashValue
//
MITREObject* HashTable::retrieve(Int32 hashValue)
{
    HashItem* searchValue = retrieveHashItem(hashValue);

    if (searchValue)
        return searchValue->objPtr;
    else
        return NULL;
}

//
//Locate the object specified by the hashValue, remove it from the table
//and return the orriginal object to the caller
//
MITREObject* HashTable::remove(Int32 hashValue)
{
    Int32 bin = hashValue % HASHTABLE_SIZE;
    MITREObject* returnObj = NULL;
    HashItem* searchItem = table[bin];

    while (1)
    {
        if (searchItem == NULL)
            return NULL;

        if (searchItem->hashValue == hashValue)
        {
            returnObj = searchItem->objPtr;

            if (searchItem->prevItem == NULL)
                table[bin] = searchItem->nextItem;
            else
                searchItem->prevItem->nextItem = searchItem->nextItem;

            if (searchItem->nextItem != NULL)
                searchItem->nextItem->prevItem = searchItem->prevItem;

            delete searchItem;
            return returnObj;
        }
        
        searchItem = searchItem->nextItem;
    }
}

HashTable::HashItem* HashTable::retrieveHashItem(Int32  hashValue)
{
    Int32 bin = hashValue % HASHTABLE_SIZE;
    HashItem* searchItem = NULL;

    //Goto the calculated bin, and begin a linear search for the specified
    //value.
    searchItem = table[bin];
    while (1)
    {      
        if (searchItem == NULL)
            return NULL;

        if (searchItem->hashValue == hashValue)
            return searchItem;
        
        searchItem = searchItem->nextItem;
    }
}
