package ru.yandex.qe.dispenser.ws.abc;


import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import ru.yandex.qe.dispenser.api.v1.response.DiCursorPage;

public class CursorItemIterator<T> implements Iterator<T> {
    private final CursorPageLoader<T> loader;

    private Iterator<T> currentIterator;
    private boolean isLastItemPack;
    private String cursor = null;

    public CursorItemIterator(final CursorPageLoader<T> loader) {
        this.loader = loader;
    }

    private void loadNextItems() {
        final DiCursorPage<T> items = loader.loadPage(cursor);

        currentIterator = items.iterator();
        isLastItemPack = !items.hasNext();
        cursor = items.cursor();
    }

    @Nonnull
    private Iterator<T> getCurrentIterator() {
        if (currentIterator == null) {
            loadNextItems();
        }

        return Objects.requireNonNull(currentIterator);
    }

    @Override
    public boolean hasNext() {
        final Iterator<T> iterator = getCurrentIterator();

        if (!iterator.hasNext()) {

            if (isLastItemPack) {
                return false;
            }

            loadNextItems();

            return getCurrentIterator().hasNext();
        } else {
            return true;
        }
    }

    @Override
    public T next() {
        if (hasNext()) {
            return getCurrentIterator().next();
        }
        throw new NoSuchElementException("No elements");
    }

    public Stream<T> stream() {
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(this, 0), false);
    }

    public interface CursorPageLoader<T> {
        DiCursorPage<T> loadPage(@Nullable final String cursor);
    }
}
