package ru.yandex.qe.dispenser.client.v1.impl;

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 javax.annotation.ParametersAreNonnullByDefault;

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

@ParametersAreNonnullByDefault
public abstract class AbstractPageItemIterator<T> implements Iterator<T> {
    @Nullable
    private Iterator<T> currentIterator;

    private boolean isLastPage = false;
    private int nextPage = 1;

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

        return Objects.requireNonNull(currentIterator);
    }

    protected abstract DiPage<T> loadPage(final int page);

    private void loadNextPage() {
        final DiPage<T> resultPage = loadPage(nextPage);

        nextPage += 1;
        isLastPage = !resultPage.hasNext();
        currentIterator = resultPage.iterator();
    }

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

        if (!iterator.hasNext()) {

            if (isLastPage) {
                return false;
            }

            loadNextPage();

            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);
    }
}
