import Dictionary;
import Entry;

/**
 * Hash tables that use the "shift into an empty space" strategy.
 *
 * @author Herman Hacker
 * @author Hermione Hacker
 * @author Thelma Thinker
 * @author Thomas Thinker
 * @author Samuel A. Rebelsky
 * @version 1.2 of April 2003
 */
public class ShiftyHashTable
    implements Dictionary
{

    // +--------+----------------------------------------
    // | Fields |
    // +--------+

    /** 
     * An array that contains all the elements in the
     * hash table.  An entry might be stored at location
     *    entry.getKey().hashCode() % entries.length
     * (or after, if that cell is full).  If null is stored 
     * in a particular cell, that cell is empty.
     */
    protected Entry[] entries;

    /**
     * The number of entries in the hash table.  Not
     * the same as entries.length, since some (hopefully
     * many) of the cells in entries will be null.
     */
    protected int numEntries;

    /**
     * The last place the findKey operation looked.  Useful
     * for the add routine.
     */
    protected int curLoc;

    // +--------------+----------------------------------
    // | Constructors |
    // +--------------+

    /**
     * Build a new hash table that should work relatively
     * well for up to maxEntries entries.
     */
    public ShiftyHashTable(int maxEntries) {
        this.numEntries = 0;
        this.entries = new Entry[maxEntries*2];
    } // ShiftyHashTable(int)

    // +------------+------------------------------------
    // | Extractors |
    // +------------+

    /**
     * Determine if a particular key is in the dictionary.
     */
    public boolean contains(Object key) {
        // Find the key.
        try {
            findKey(key);
            return true;
        }
        // If the key wasn't there, return false.
        catch (Exception e) {
            return false;
        }
    } // contains(Object)

    /**
     * Look up an entry in the dictionary.
     */
    public Object lookup(Object key)
          throws Exception
    {
        // Find the index for the key and return the
        // corresponding entry.  Since findKey throws
        // an exception if the key is not found, we don't
        // need to.
        return entries[findKey(key)].getValue();
    } // lookup(Object)

    // +-----------+-------------------------------------
    // | Modifiers |
    // +-----------+

    /**
     * Add an entry to the dictionary.
     */
    public void add(Object key, Object value)
          throws Exception
    {
        try {
            findKey(key);
        }
        catch (Exception e) {
            // It's not there.  Use curLoc, which findKey has
            // advanced to the correct position.
            if (entries[curLoc] == null) {
                ++numEntries;
                entries[curLoc] = new Entry(key,value);
            }
            else {
                throw new Exception("No room");
            }
            return;
        } // catch (Exception)
        // findKey succeeded.  We can't add duplicate entries.
        throw new Exception("Already there");
    } // add(Object, Object)

    /**
     * Delete an entry in the dictionary.
     */
    public void delete(Object key)
        throws Exception
    {
        throw new Exception("STUB");
    } // delete(Object)

    // +---------+---------------------------------------
    // | Helpers |
    // +---------+

    /**
     * Determine the index of a particular key.  
     * @exception Exception
     *    If the key is not in the table.
     */
    protected int findKey(Object key) 
        throws Exception
    {
        // Determine the first place to look.
        int startLoc = key.hashCode() % entries.length;
        curLoc = startLoc;
        // Step through until we (1) find the desired
        // key; (2) find an empty space; or (3) return 
        // to where we started.
        do {
            // Is there no entry at the appropriate index?
            if (entries[curLoc] == null) {
                throw new Exception("Not found");
            }
            // Is an equal entry at the appropriate index?
            if (key.equals(entries[curLoc].getKey())) {
                return curLoc;
            }
            // Advance to the next index.
            curLoc = (curLoc + 1) % entries.length;
        } while (curLoc != startLoc);
        // We've wrapped around or hit null.  Give up.
        throw new Exception("Not found");
    } // findKey(Object)

} // class ShiftyHashTable

