package net.minecraft.util;

public class LongHashMap
{
    /** the array of all elements in the hash */
    private transient LongHashMapEntry[] hashArray = new LongHashMapEntry[16];

    /** the number of elements in the hash array */
    private transient int numHashElements;

    /**
     * the maximum amount of elements in the hash (probably 3/4 the size due to meh hashing function)
     */
    private int capacity = 12;

    /**
     * percent of the hasharray that can be used without hash colliding probably
     */
    private final float percentUseable = 0.75F;

    /** count of times elements have been added/removed */
    private transient volatile int modCount;

    /**
     * returns the hashed key given the original key
     */
    private static int getHashedKey(long par0)
    {
        return hash((int)(par0 ^ par0 >>> 32));
    }

    /**
     * the hash function
     */
    private static int hash(int par0)
    {
        par0 ^= par0 >>> 20 ^ par0 >>> 12;
        return par0 ^ par0 >>> 7 ^ par0 >>> 4;
    }

    /**
     * gets the index in the hash given the array length and the hashed key
     */
    private static int getHashIndex(int par0, int par1)
    {
        return par0 & par1 - 1;
    }

    public int getNumHashElements()
    {
        return this.numHashElements;
    }

    /**
     * get the value from the map given the key
     */
    public Object getValueByKey(long par1)
    {
        int var3 = getHashedKey(par1);

        for (LongHashMapEntry var4 = this.hashArray[getHashIndex(var3, this.hashArray.length)]; var4 != null; var4 = var4.nextEntry)
        {
            if (var4.key == par1)
            {
                return var4.value;
            }
        }

        return null;
    }

    public boolean containsItem(long par1)
    {
        return this.getEntry(par1) != null;
    }

    final LongHashMapEntry getEntry(long par1)
    {
        int var3 = getHashedKey(par1);

        for (LongHashMapEntry var4 = this.hashArray[getHashIndex(var3, this.hashArray.length)]; var4 != null; var4 = var4.nextEntry)
        {
            if (var4.key == par1)
            {
                return var4;
            }
        }

        return null;
    }

    /**
     * Add a key-value pair.
     */
    public void add(long par1, Object par3Obj)
    {
        int var4 = getHashedKey(par1);
        int var5 = getHashIndex(var4, this.hashArray.length);

        for (LongHashMapEntry var6 = this.hashArray[var5]; var6 != null; var6 = var6.nextEntry)
        {
            if (var6.key == par1)
            {
                var6.value = par3Obj;
                return;
            }
        }

        ++this.modCount;
        this.createKey(var4, par1, par3Obj, var5);
    }

    /**
     * resizes the table
     */
    private void resizeTable(int par1)
    {
        LongHashMapEntry[] var2 = this.hashArray;
        int var3 = var2.length;

        if (var3 == 1073741824)
        {
            this.capacity = Integer.MAX_VALUE;
        }
        else
        {
            LongHashMapEntry[] var4 = new LongHashMapEntry[par1];
            this.copyHashTableTo(var4);
            this.hashArray = var4;
            this.capacity = (int)((float)par1 * this.percentUseable);
        }
    }

    /**
     * copies the hash table to the specified array
     */
    private void copyHashTableTo(LongHashMapEntry[] par1ArrayOfLongHashMapEntry)
    {
        LongHashMapEntry[] var2 = this.hashArray;
        int var3 = par1ArrayOfLongHashMapEntry.length;

        for (int var4 = 0; var4 < var2.length; ++var4)
        {
            LongHashMapEntry var5 = var2[var4];

            if (var5 != null)
            {
                var2[var4] = null;
                LongHashMapEntry var6;

                do
                {
                    var6 = var5.nextEntry;
                    int var7 = getHashIndex(var5.hash, var3);
                    var5.nextEntry = par1ArrayOfLongHashMapEntry[var7];
                    par1ArrayOfLongHashMapEntry[var7] = var5;
                    var5 = var6;
                }
                while (var6 != null);
            }
        }
    }

    /**
     * calls the removeKey method and returns removed object
     */
    public Object remove(long par1)
    {
        LongHashMapEntry var3 = this.removeKey(par1);
        return var3 == null ? null : var3.value;
    }

    /**
     * removes the key from the hash linked list
     */
    final LongHashMapEntry removeKey(long par1)
    {
        int var3 = getHashedKey(par1);
        int var4 = getHashIndex(var3, this.hashArray.length);
        LongHashMapEntry var5 = this.hashArray[var4];
        LongHashMapEntry var6;
        LongHashMapEntry var7;

        for (var6 = var5; var6 != null; var6 = var7)
        {
            var7 = var6.nextEntry;

            if (var6.key == par1)
            {
                ++this.modCount;
                --this.numHashElements;

                if (var5 == var6)
                {
                    this.hashArray[var4] = var7;
                }
                else
                {
                    var5.nextEntry = var7;
                }

                return var6;
            }

            var5 = var6;
        }

        return var6;
    }

    /**
     * creates the key in the hash table
     */
    private void createKey(int par1, long par2, Object par4Obj, int par5)
    {
        LongHashMapEntry var6 = this.hashArray[par5];
        this.hashArray[par5] = new LongHashMapEntry(par1, par2, par4Obj, var6);

        if (this.numHashElements++ >= this.capacity)
        {
            this.resizeTable(2 * this.hashArray.length);
        }
    }

    /**
     * public method to get the hashed key(hashCode)
     */
    static int getHashCode(long par0)
    {
        return getHashedKey(par0);
    }
}
