package java.util;

/**
 * Maps keys to objects. It has a fixed-size table, so don't
 * expect it to scale well.
 */
public class Hashtable
{
  private static final int TABLE_SIZE = 32;
  private Object[] iTable = new Object[TABLE_SIZE];
  
  public Hashtable()
  {  
  }

  public synchronized Object get (Object aKey)
  {
    Object pElement = iTable[getTableIndex(aKey)];
    if (pElement == null)
      return null;
    KeyValuePair pKeyValuePair = getKeyValuePair (pElement, aKey);
    if (pKeyValuePair == null)
      return null;
    return pKeyValuePair.iValue;
  }
  
  public synchronized void put (Object aKey, Object aValue)
  {
	int pIndex = getTableIndex (aKey);    
    Object pElement = iTable[pIndex];
    KeyValuePair pKeyValuePair = null;
    if (pElement != null)
      pKeyValuePair = getKeyValuePair (pElement, aKey);
    if (pKeyValuePair == null)
    {
    	pKeyValuePair = new KeyValuePair();
    	pKeyValuePair.iKey = aKey;
    	pKeyValuePair.iValue = aValue;
    }
    if (pElement == null)      
    {
    	iTable[pIndex] = pKeyValuePair;
    } 
    else if (pElement == pKeyValuePair)
    {
    	pKeyValuePair.iValue = aValue;	    
    }
    else if (pElement instanceof KeyValuePair)
    {
    	Vector pVector = new Vector();
    	pVector.addElement (pElement);
    	pVector.addElement (pKeyValuePair);
      iTable[pIndex] = pVector;
    }
    else
    {
      // pElement must be a Vector
      ((Vector) pElement).addElement (pKeyValuePair);	    
    }
  }

  /**
   * 
   * @return An Enumeration object containing all keys for this Hashtable
   */
  public Enumeration keys() {
	  return new Enumeration() {
		  int cur = 0;
		  /* Our Hashtable stores more than one object in iTable if
		   * getTableIndex() hashes it to the same slot. When this happens
		   * it stores multiple items in a Vector. In this case, nextElement()
		   * needs to keep track of which vector it is at via curVector.
		   */
		  int curVector = -1; 
		  public boolean hasMoreElements() {
			  /* Difficult to work with our current Hashtable code due to iTable gaps.*/
			   
			  int i=cur;
			  // This little detour is to check if there are any more objects in a Vector (if used)
			  if(i < TABLE_SIZE && iTable[i] instanceof Vector) {
				  Vector v = (Vector)iTable[i];
				  if(curVector + 1 >= v.size())
					  i++;
			  }
			  for(;i<TABLE_SIZE;i++)
				  if(iTable[i] != null)
					  return true;
			  return false;
		  }
		  
		  public Object nextElement() {
			  /* Difficult to work with our current Hashtable code 
			     due to iTable gaps. */
			  // Go thru iTable until object found
			  while(cur < TABLE_SIZE) {
				  if(iTable[cur] instanceof KeyValuePair) {
					  KeyValuePair kvp = (KeyValuePair)iTable[cur];
					  cur++;
					  if(kvp != null) return kvp.iKey;
				  } else if (iTable[cur] instanceof Vector) {
					  curVector++;
					  while(curVector >= 0) {
						  Vector v = (Vector)iTable[cur];
						  if (curVector >= v.size()) {
							  curVector = -1;
							  break;
						  }
						  KeyValuePair kvp = (KeyValuePair)v.elementAt(curVector);
						  if(kvp == null) {
							  curVector = -1;
						  } else {
							  return kvp.iKey;
						  }
					  }
				  }
				  cur++;
			  }
			  return null;
		  }
	  };
  }
  
  private KeyValuePair getKeyValuePair (Object aPivot, Object aKey)
  {
    if (aPivot instanceof Vector)
    {
      Vector pVec = (Vector) aPivot;
      int pSize = pVec.size();
      for (int i = 0; i < pSize; i++)
      {
	KeyValuePair pPair = (KeyValuePair) pVec.elementAt (i);
	if (aKey.equals (pPair.iKey))
          return pPair;
      }
      return null;
    }
    // Not a Vector, must be a lone KeyValuePair
    KeyValuePair pPair = (KeyValuePair) aPivot;
    if (aKey.equals (pPair.iKey))
      return pPair;
    return null;
  }
  
  private int getTableIndex (Object aKey)
  {
    int pHash = aKey.hashCode();
    if (pHash < 0)
      pHash = -pHash;
    return pHash % TABLE_SIZE;
  }

  private static class KeyValuePair
  { 
    Object iKey;
    Object iValue;
  }
}
