/*******************************************************************************
 * Copyright (c) 2004, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.cdt.core.parser.util;

import java.util.Comparator;

/**
 * @author ddaoust
 */
public class HashTable implements Cloneable {
	protected static final int minHashSize = 2;
	protected int currEntry = -1;
	
    public boolean isEmpty() {
        return currEntry == -1;
    }
    
	final public int size() {
	    return currEntry + 1;
	}
    
	protected int[] hashTable;
	protected int[] nextTable;

	public HashTable(int initialSize) {
		int size = 1;
		while (size < initialSize)
			size <<= 1;
		
		if (size > minHashSize) {
			hashTable = new int[size * 2];
			nextTable = new int[size];
		} else {
			hashTable = null;
			nextTable = null;
		}
	}
	
	@Override
	public Object clone() {
	    HashTable newTable = null;
        try {
            newTable = (HashTable) super.clone();
        } catch (CloneNotSupportedException e) {
            //shouldn't happen because object supports clone.
            return null;
        }
        
        int size = capacity();
        
        if (hashTable != null) {
	        newTable.hashTable = new int[size * 2];
	        newTable.nextTable = new int[size];
		    System.arraycopy(hashTable, 0, newTable.hashTable, 0, hashTable.length);
		    System.arraycopy(nextTable, 0, newTable.nextTable, 0, nextTable.length);
        }
        newTable.currEntry = currEntry;
	    return newTable;
	}
	
	protected void resize() {
		resize(capacity() << 1);
	}

	public void clear() {
		currEntry = -1; 
		
		// clear the table
		if (hashTable == null)
			return;
		
		for (int i = 0; i < capacity(); i++) {
	        hashTable[2 * i] = 0;
	        hashTable[2 * i + 1] = 0;
	        nextTable[i] = 0;
        }
	}
	protected void rehash() {
		if (nextTable == null)
			return;
		
		// clear the table (don't call clear() or else the subclasses stuff will be cleared too)
		for (int i = 0; i < capacity(); i++) {
            hashTable[2 * i] = 0;
            hashTable[2 * i + 1] = 0;
            nextTable[i] = 0;
	    }
		// Need to rehash everything
		for (int i = 0; i <= currEntry; ++i) {
			linkIntoHashTable(i, hash(i));
		}
	}
	protected void resize(int size) {
		if (size > minHashSize) {
			hashTable = new int[size * 2];
			nextTable = new int[size];
			
			// Need to rehash everything
			for (int i = 0; i <= currEntry; ++i) {
				linkIntoHashTable(i, hash(i));
			}
		}
	}
	
	protected int hash(int pos) {
		// return the hash value of the element in the key table 
		throw new UnsupportedOperationException();
	}
	
	protected void linkIntoHashTable(int i, int hash) {
		if (nextTable == null)
			return;
		
		if (hashTable[hash] == 0) {
			hashTable[hash] = i + 1;
		} else {
			// need to link
			int j = hashTable[hash] - 1;
			while (nextTable[j] != 0) {
//				if (nextTable[j] - 1 == j) {
//					break;
//				}
				j = nextTable[j] - 1;
			}
			nextTable[j] = i + 1;
		}
	}
	
	final public int capacity() {
		if (nextTable == null)
			return minHashSize;
		return nextTable.length;
	}
	
	protected void removeEntry(int i, int hash) {
		if (nextTable == null) {
		    --currEntry;
			return;
		}
		
		// Remove the hash entry
		if (hashTable[hash] == i + 1) {
			hashTable[hash] = nextTable[i];
		} else { 
			// find entry pointing to me
			int j = hashTable[hash] - 1;
			while (nextTable[j] != 0 && nextTable[j] != i + 1)
				j = nextTable[j] - 1;
			nextTable[j] = nextTable[i];
		}
		
		if (i < currEntry) {
			// shift everything over
			System.arraycopy(nextTable, i + 1, nextTable, i, currEntry - i);
			
			// adjust hash and next entries for things that moved
			for (int j = 0; j < hashTable.length; ++j) {
				if (hashTable[j] > i + 1)
					--hashTable[j];
			}

			for (int j = 0; j < nextTable.length; ++j) {
				if (nextTable[j] > i + 1)
					--nextTable[j];
			}
		}

		// last entry is now free
		nextTable[currEntry] = 0;
		--currEntry;
	}

    final public void sort(Comparator<Object> c) {
        if (size() > 1) {
	        quickSort(c, 0, size() - 1);       
	        rehash();
        }
    }	
    final private void quickSort(Comparator<Object> c, int p, int r) {
        if (p < r) {
            int q = partition(c, p, r);
            if (p < q)   quickSort(c, p, q);
            if (++q < r) quickSort(c, q, r);
        }
    }
    
    protected int partition(Comparator<Object> c, int p, int r) {
    	throw new UnsupportedOperationException();
    }
    
	public void dumpNexts() {
		if (nextTable == null)
			return;
		
		for (int i = 0; i < nextTable.length; ++i) {
			if (nextTable[i] == 0)
				continue;
			
			System.out.print(i);
			
			for (int j = nextTable[i] - 1; j >= 0; j = nextTable[j] - 1)
				System.out.print(" -> " + j); //$NON-NLS-1$
			
			System.out.println(""); //$NON-NLS-1$
		}
	}
}
