package com.atlassian.bitbucket.util;

import com.google.common.collect.AbstractIterator;

import java.util.Iterator;

/**
 * Sequentially iterates over all {@link Page pages} in the {@link PageProvider}, only loading a single page at a
 * time into memory.
 */
public class PagedIterable<T> implements Iterable<T> {
    
    private final PageProvider<T> pageProvider;
    private final PageRequest pageRequest;

    public PagedIterable(PageProvider<T> pageProvider, int pageSize) {
        this(pageProvider, PageUtils.newRequest(0, pageSize));
    }

    public PagedIterable(PageProvider<T> pageProvider, PageRequest pageRequest) {
        this.pageProvider = pageProvider;
        this.pageRequest = pageRequest;
    }

    @Override
    public Iterator<T> iterator() {
        return new PagedIterator(pageProvider.get(pageRequest));
    }
    
    private class PagedIterator extends AbstractIterator<T> {

        private Page<T> currentPage;
        private Iterator<T> iterator;

        PagedIterator(Page<T> page) {
            this.currentPage = page;
            this.iterator = page.getValues().iterator();
        }

        @Override
        protected T computeNext() {
            while (true) {
                //If the page's iterator is not yet empty, return its next element
                if (iterator.hasNext()) {
                    return iterator.next();
                }

                //Otherwise, check for another page
                PageRequest next = currentPage.getNextPageRequest();
                if (next == null) {
                    return endOfData();
                }

                currentPage = pageProvider.get(next);
                iterator = currentPage.getValues().iterator();
            }
        }
    }
}
