import java.util.Map;

public class HashTable  {  // should implement Map... and there is already a Hashtable.class!
 
   private HashEntry data[];
   private int htSize;
   private int size;
   private static final int defaultCapacity = 997;

   public HashTable(int initialCapacity) {
      data = new HashEntry[initialCapacity];
      size = 0;
      htSize = initialCapacity;
	}

   public HashTable() { this(997); }
   
   public int size() { return size; }
   public int htSize() { return htSize; }
   public boolean isEmpty() { return size == 0; }
     
   public void insert(String key, Object value) {
   		if (key == null) return;
   		int index = hashFunction(key);
   		HashEntry x = data[index];
   		if (x == null || x.deleted)  { // entry is empty
      		data[index] = new HashEntry(key, value, false);
      		size++;
      		return;
   		} else { // collision
      		int k = (index + 1) % htSize;
      		while (true) { // linear probing
         		x = data[k];
         		if (x == null || x.deleted)  { // entry is empty
            		data[k] = new HashEntry(key, value, false);
            		size++;
            		return;
         		} else {
            		k = (k + 1 ) % htSize;
         		}
      		}
   		}
	}

	public Object member(String key) {
   		if (key == null) return null;
   		int index = hashFunction(key);
   		HashEntry x = data[index];
   		if (x == null) { // entry is empty, not found
      		return null;
   		} else if (!x.deleted && x.key.equals(key)) {
      		// found key
      		return x.val;
   		} else { // collision, need to keep looking....
      		int k = (index + 1) % htSize;
      		while (true) {
         		// linear probing
         		x = data[k];
         		if (x == null) { // entry is empty
            		return null;
         		} else if (!x.deleted && x.key.equals(key)) { // found key
            		return x.val;
         		} else {
           		 k = (k + 1) % htSize;
         		}
      		}
   		}
	}
		
	public Object delete(String key) {
   		if (key == null) return null;
   		int index = hashFunction(key);
   		HashEntry x = data[index];
   		if (x == null) { // entry is empty, not found
      		return null;
   		} else if (!x.deleted && x.key.equals(key)) { // found key
      		x.deleted = true; size--;
      		return x.val;
   		} else { // collision
      		int k = (index + 1) % htSize;
      		while (true) { // linear probing
         		x = data[k];
         		if (x == null) { // entry is empty
            		return null;
        		 } else if (!x.deleted && x.key.equals(key)) { // found key
        		 	// what if it was already deleted though?
            		x.deleted = true; size--;
            		return x.val;
         		} else {
            		k = (k + 1) % htSize;
         		}
      		}
   		}
	}
  
   public int hashFunction(String key) {
   	
   		int x = 0;
   		// calculate the hash code -- see p.345 in textbook
   		for (int k=0; k<key.length(); k++) {
      		x = 37 * x + key.charAt(k);
      		// 37 shifts the bit pattern to make
      		// room for the new bit pattern
   		}
   		// do the compression step....
   		x = x % htSize;

   		// for loop could generate overflow
   		// and hence produce a negative value
   		if (x < 0) x = x + htSize;
   			return x;
   }
   
   
   // exercise for in class -- implement a main method!
   public static void main(String args[]) {
   	
   	HashTable myht = new HashTable(20);
   	    
   		for (int i=0; i<18; i++) {
   			String key = "key" + i;
   			String val = "value" + i;
   			System.out.println("inserting key" + i);
   			myht.insert( key, val);
   		}
   		for (int i=0; i<18; i++) {
   			String key = "key" + i;
   			System.out.println("removing key" + i);
   			String val = (String)myht.delete( key);
   			System.out.println("found " + val);
   		}
   					
      		
     
   }
   

}
