package org.xbib.datastructures.immutable;

import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Spliterator;
import java.util.function.Consumer;

/**
 * {@code values()} implementation for {@link ImmutableMap}.
 */
final class ImmutableMapValues<K, V> extends ImmutableCollection<V> {
    private final ImmutableMap<K, V> map;

    ImmutableMapValues(ImmutableMap<K, V> map) {
        this.map = map;
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public UnmodifiableIterator<V> iterator() {
        return new UnmodifiableIterator<V>() {
            final UnmodifiableIterator<Entry<K, V>> entryItr = map.entrySet().iterator();

            @Override
            public boolean hasNext() {
                return entryItr.hasNext();
            }

            @Override
            public V next() {
                return entryItr.next().getValue();
            }
        };
    }

    @Override
    public Spliterator<V> spliterator() {
        return Spliterators.map(map.entrySet().spliterator(), Entry::getValue);
    }

    @Override
    public boolean contains(Object object) {
        return object != null && contains(iterator(), object);
    }

    @Override
    boolean isPartialView() {
        return true;
    }

    @Override
    public ImmutableList<V> asList() {
        final ImmutableList<Entry<K, V>> entryList = map.entrySet().asList();
        return new ImmutableAsList<V>() {
            @Override
            public V get(int index) {
                return entryList.get(index).getValue();
            }

            @Override
            ImmutableCollection<V> delegateCollection() {
                return ImmutableMapValues.this;
            }
        };
    }

    @Override
    public void forEach(Consumer<? super V> action) {
        Objects.requireNonNull(action);
        map.forEach((k, v) -> action.accept(v));
    }

    /**
     * Returns {@code true} if {@code iterator} contains {@code element}.
     */
    static boolean contains(Iterator<?> iterator, Object element) {
        if (element == null) {
            while (iterator.hasNext()) {
                if (iterator.next() == null) {
                    return true;
                }
            }
        } else {
            while (iterator.hasNext()) {
                if (element.equals(iterator.next())) {
                    return true;
                }
            }
        }
        return false;
    }
}
