package com.stripe.model;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;

import com.stripe.Stripe;
import com.stripe.net.APIResource;
import com.stripe.net.RequestOptions;

public class PagingIterator<T extends HasId> extends APIResource implements Iterator<T> {
	private final String url;
	
	@SuppressWarnings("rawtypes")
	private final Class<? extends StripeCollectionInterface> collectionType;
	
	private StripeCollectionInterface<T> currentCollection;
	private Iterator<T> currentDataIterator;
	
	private String lastId;
	
	PagingIterator(final StripeCollectionInterface<T> stripeCollection) {
		this.url = Stripe.getApiBase() + stripeCollection.getUrl();

		this.collectionType = stripeCollection.getClass();
		
		this.currentCollection = stripeCollection;
		this.currentDataIterator = stripeCollection.getData().iterator();
	}

	@Override
	public boolean hasNext() {
		return currentDataIterator.hasNext() ||
			currentCollection.getHasMore();
	}

	@Override
	public T next() {
		if (currentDataIterator.hasNext()) {
			final T next = currentDataIterator.next();
			this.lastId = next.getId();
			return next;
		} else if (currentCollection.getHasMore()) {
			try {
                Map<String, Object> params = new HashMap<String, Object>();

                // copy all the parameters from the initial request
                Map<String, Object> initialParams = currentCollection.getRequestParams();
                if (initialParams != null) {
                    params.putAll(initialParams);
                }

                // then put our new page start in
                params.put("starting_after", lastId);

				this.currentCollection = list(params, currentCollection.getRequestOptions());
				
				this.currentDataIterator =
					currentCollection.getData().iterator();
				
				return next();
			} catch (final Exception e) {
				throw new RuntimeException("Unable to lazy-load stripe objects", e);
			}
			
		} else {
			throw new NoSuchElementException();
		}
	}

	@Override
	public void remove() {
		throw new UnsupportedOperationException();
	}

	@SuppressWarnings("unchecked")
	private StripeCollectionInterface<T> list(
		final Map<String, Object> params, 
		final RequestOptions options
	) throws Exception {
		return APIResource.request(RequestMethod.GET, url, params, collectionType, options);
	}
}

@@ -1051,153 +1051,141 @@
 ) %7B%0A
-%09%09if (currentDataIterator.hasNext()) %7B%0A%09%09%09final T next = currentDataIterator.next();%0A%09%09%09this.lastId = next.getId();%0A%09%09%09return next;%0A%09%09%7D else if (
+        // if we've run out of data on the current page, try to fetch another%0A        // one%0A%09%09if (!currentDataIterator.hasNext() && 
 curr
@@ -1844,32 +1844,8 @@
 ();%0A
-%09%09%09%09%0A%09%09%09%09return next();%0A
 %09%09%09%7D
@@ -1873,16 +1873,16 @@
 on e) %7B%0A
+
 %09%09%09%09thro
@@ -1955,26 +1955,164 @@
 %09%09%7D%0A
-%09%09%09%0A%09%09%7D else %7B%0A%09%09%09
+        %7D%0A%0A%09%09if (currentDataIterator.hasNext()) %7B%0A%09%09%09final T next = currentDataIterator.next();%0A%09%09%09this.lastId = next.getId();%0A%09%09%09return next;%0A%09%09%7D%0A%0A        
 thro
@@ -2143,20 +2143,16 @@
 tion();%0A
-%09%09%7D%0A
 %09%7D%0A%0A%09@Ov

Remove recursion in the iterator