/*
    Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
    и других спецификаций для функционирования компактных приложений на языке
    Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

    Copyright © 2016–2017, 2019–2022 Малик Разработчик

    Это свободная программа: вы можете перераспространять ее и/или изменять
    ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она будет полезной,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <https://www.gnu.org/licenses/>.
*/

package java.util;

import malik.emulator.fileformats.*;
import malik.emulator.util.*;

public class Hashtable extends Object implements DataHolder
{
    private static final class Entry extends Object
    {
        public final int hash;
        public final Object key;
        public Object value;
        public Entry next;

        public Entry(int hash, Object key, Object value, Entry next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }

    private static class Enumerator extends Object implements Enumeration
    {
        private final boolean keys;
        private int index;
        private final Entry[] table;
        private Entry next;
        protected Entry current;

        public Enumerator(boolean keys, Entry[] table) {
            this.keys = keys;
            this.index = table.length;
            this.table = table;
        }

        public boolean hasMoreElements() {
            int i;
            Entry[] t;
            if(next != null) return true;
            for(t = table, i = index; i-- > 0; ) if((next = t[i]) != null)
            {
                index = i;
                return true;
            }
            index = -1;
            return false;
        }

        public Object nextElement() {
            Entry e;
            if((e = next) == null)
            {
                int i = index;
                Entry[] t = table;
                while(i-- > 0 && (e = t[i]) == null);
                next = e;
                index = i < 0 ? -1 : i;
            }
            if(e == null)
            {
                current = null;
                throw new NoSuchElementException("Enumeration.nextElement: элементов больше не осталось.");
            }
            next = (current = e).next;
            return keys ? e.key : e.value;
        }
    }

    private static class KeyEnumerator extends Enumerator implements MapEnumeration
    {
        public KeyEnumerator(Entry[] table) {
            super(true, table);
        }

        public Object value() {
            Entry e;
            if((e = current) == null)
            {
                throw new NoSuchElementException("Hashtable.keys: элементов больше не осталось.");
            }
            return e.value;
        }
    }

    private int factor;
    private int length;
    private Entry[] table;
    private final Object monitor;

    public Hashtable() {
        this(15);
    }

    public Hashtable(int initialCapacity) {
        if(initialCapacity < 0)
        {
            throw new IllegalArgumentException("Hashtable: аргумент initialCapacity не может быть отрицательным.");
        }
        if(initialCapacity == 0) initialCapacity = 1;
        this.factor = initialCapacity - (initialCapacity >> 2);
        this.table = new Entry[initialCapacity];
        this.monitor = new Object();
    }

    public String toString() {
        StringBuilder result = (new StringBuilder()).append('{');
        synchronized(monitor)
        {
            KeyEnumerator e = new KeyEnumerator(table);
            for(int i = length; i-- > 0; )
            {
                result.append(e.nextElement().toString()).append('=').append(e.value().toString());
                if(i > 0) result.append(",\u0020");
            }
        }
        return result.append('}').toString();
    }

    public void clear() {
        synchronized(monitor)
        {
            Object[] t;
            Array.fill(t = table, 0, t.length, null);
            length = 0;
        }
    }

    public boolean isEmpty() {
        return length <= 0;
    }

    public boolean contains(Object value) {
        boolean result;
        if(value == null)
        {
            throw new NullPointerException("Hashtable.contains: аргумент value равен нулевой ссылке.");
        }
        synchronized(monitor)
        {
            Entry[] t;
            result = false;
            label0: for(int i = (t = table).length; i-- > 0; ) for(Entry e = t[i]; e != null; e = e.next) if(value.equals(e.value))
            {
                result = true;
                break label0;
            }
        }
        return result;
    }

    public boolean containsKey(Object key) {
        boolean result;
        if(key == null) return false;
        synchronized(monitor)
        {
            int h;
            Entry[] t;
            result = false;
            for(Entry e = (t = table)[((h = key.hashCode()) & Integer.MAX_VALUE) % t.length]; e != null; e = e.next) if(h == e.hash && key.equals(e.key))
            {
                result = true;
                break;
            }
        }
        return result;
    }

    public int size() {
        return length;
    }

    public Object put(Object key, Object value) {
        Object result;
        if(key == null)
        {
            throw new NullPointerException("Hashtable.put: аргумент key равен нулевой ссылке.");
        }
        if(value == null)
        {
            throw new NullPointerException("Hashtable.put: аргумент value равен нулевой ссылке.");
        }
        synchronized(monitor)
        {
            label0:
            {
                int i;
                int h;
                int len;
                Entry[] t;
                for(Entry e = (t = table)[i = ((h = key.hashCode()) & Integer.MAX_VALUE) % t.length]; e != null; e = e.next) if(h == e.hash && key.equals(e.key))
                {
                    result = e.value;
                    e.value = value;
                    break label0;
                }
                if((len = length) >= factor)
                {
                    rehash();
                    i = (h & Integer.MAX_VALUE) % (t = table).length;
                }
                t[i] = new Entry(h, key, value, t[i]);
                length = len + 1;
                result = null;
            }
        }
        return result;
    }

    public Object remove(Object key) {
        Object result;
        if(key == null) return null;
        synchronized(monitor)
        {
            int i;
            int h;
            Entry[] t;
            result = null;
            for(Entry f = null, e = (t = table)[i = ((h = key.hashCode()) & Integer.MAX_VALUE) % t.length]; e != null; f = e, e = e.next)
            {
                if(h != e.hash || !key.equals(e.key)) continue;
                if(f != null)
                {
                    f.next = e.next;
                } else
                {
                    t[i] = e.next;
                }
                length--;
                result = e.value;
                break;
            }
        }
        return result;
    }

    public Object get(Object key) {
        Object result;
        if(key == null) return null;
        synchronized(monitor)
        {
            int h;
            Entry[] t;
            result = null;
            for(Entry e = (t = table)[((h = key.hashCode()) & Integer.MAX_VALUE) % t.length]; e != null; e = e.next) if(h == e.hash && key.equals(e.key))
            {
                result = e.value;
                break;
            }
        }
        return result;
    }

    public Enumeration elements() {
        return new Enumerator(false, table);
    }

    public Enumeration keys() {
        return new KeyEnumerator(table);
    }

    protected void rehash() {
        int nc;
        int oc;
        Entry[] nt;
        Entry[] ot;
        table = nt = new Entry[nc = ((oc = (ot = table).length) << 1) + 1];
        factor = nc - (nc >> 2);
        for(int i = oc; i-- > 0; ) for(Entry f = ot[i]; f != null; )
        {
            int j;
            Entry e;
            j = ((e = f).hash & Integer.MAX_VALUE) % nc;
            f = f.next;
            e.next = nt[j];
            nt[j] = e;
        }
    }
}
