package net.ranides.test.data;

import net.ranides.assira.collection.lookups.ALookup;
import net.ranides.assira.collection.lookups.Lookup;
import net.ranides.assira.collection.maps.IntMap;
import net.ranides.assira.generic.CompareUtils;
import net.ranides.assira.generic.HashUtils;

import java.util.AbstractMap;
import java.util.Map;

public final class TMapItem<K, V> {

    private final TMap<K, V> that;

    private final int index;

    private final K key;

    private final V value;

    protected TMapItem(TMap<K, V> that, int index, int n) {
        this.that = that;
        this.index = index;
        this.key = that.key(index, n);
        this.value = that.value(index, n);
    }

    public int index() {
        return index;
    }

    public K key() {
        return key;
    }

    public V value() {
        return value;
    }

    public int keyInt() {
        return (Integer) key;
    }

    public int valueInt() {
        return (Integer) value;
    }

    public Map.Entry<K, V> entry() {
        return new AbstractMap.SimpleImmutableEntry<>(key, value);
    }

    public Map.Entry<K, V> entry(int value) {
        return new AbstractMap.SimpleImmutableEntry<>(key, that.value(value));
    }

    public IntMap.IntEntry<V> entryInt() {
        return new BasicIntEntry<>(keyInt(), value());
    }

    public IntMap.IntEntry<V> entryInt(int value) {
        return new BasicIntEntry<>(keyInt(), that.value(value));
    }

    public Lookup.LookupEntry<K> lookup(int value) {
        return new ALookup.BasicEntry<>(key, that.item(value).valueInt());
    }

    public Lookup.LookupEntry<K> lookup() {
        return new ALookup.BasicEntry<>(key, valueInt());
    }

    public <M extends Map<K, V>> M into(M target) {
        target.put(key(), value());
        return target;
    }

    private static class BasicIntEntry<V> implements IntMap.IntEntry<V> {

        public int key;
        public V value;

        public BasicIntEntry(Integer key, V value) {
            this.key = key;
            this.value = value;
        }

        public BasicIntEntry(int key, V value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public Integer getKey() {
            return key;
        }

        @Override
        public int getIntKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        @Override
        public V setValue(V value) {
            throw new UnsupportedOperationException();
        }

        @Override
        public final boolean equals(Object other) {
            if (!(other instanceof Map.Entry)) {
                return false;
            }
            Map.Entry<?, ?> that = (Map.Entry<?, ?>) other;
            return CompareUtils.equals(this.getIntKey(), that.getKey())
                && CompareUtils.equals(this.getValue(), that.getValue());
        }

        @Override
        public final int hashCode() {
            return getIntKey() ^ HashUtils.hash(getValue());
        }

        @Override
        public final String toString() {
            return getIntKey() + "=" + getValue();
        }

    }

}
