package me.prettyprint.cassandra.service;

import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
import java.util.Iterator;
import me.prettyprint.cassandra.service.template.SliceFilter;
import me.prettyprint.hector.api.beans.Row;
import me.prettyprint.hector.api.query.RangeSlicesQuery;

/**
 * Iterates over the range slices, automatically refreshing the query until all matching rows are returned.
 *
 * @author thrykol
 */
public class RangeSlicesIterator<K, N, V> implements Iterator<Row<K, N, V>> {

	private RangeSlicesQuery<K, N, V> query;
	private K startKey;
	private K endKey;
	private PeekingIterator<Row<K, N, V>> iterator;
	private SliceFilter<Row<K, N, V>> filter = null;
	private int rows = 0;

	public RangeSlicesIterator(RangeSlicesQuery<K, N, V> query, K startKey, K endKey) {
		this.query = query;
		this.startKey = startKey;

		this.query.setKeys(startKey, endKey);
	}

	@Override
	public boolean hasNext() {
		if (iterator == null) {
			// First time through
			iterator = Iterators.peekingIterator(query.execute().get().getList().iterator());
		} else if (!iterator.hasNext() && rows == query.getRowCount()) {  // only need to do another query if maximum rows were retrieved
			query.setKeys(startKey, endKey);
			iterator = Iterators.peekingIterator(query.execute().get().getList().iterator());
			rows = 0;

			if (iterator.hasNext()) {
				// First element is startKey which was the last element on the previous query result - skip it
				next();
			}
		}

		while(filter != null && iterator != null && iterator.hasNext() && !filter.accept(iterator.peek())) {
			next();

			if(!iterator.hasNext() && rows == query.getRowCount()) {
				refresh();
			}
		}

		return iterator.hasNext();
	}

	@Override
	public Row<K, N, V> next() {
		Row<K, N, V> row = iterator.next();
		startKey = row.getKey();
		rows++;

		return row;
	}

	@Override
	public void remove() {
		iterator.remove();
	}

	public RangeSlicesIterator<K, N, V> setFilter(SliceFilter<Row<K, N, V>> filter) {
		this.filter = filter;

		return this;
	}
	
	private void refresh() {
		query.setKeys(startKey, endKey);
		iterator = Iterators.peekingIterator(query.execute().get().getList().iterator());
		rows = 0;

		if (iterator.hasNext()) {
			// First element is startKey which was the last element on the previous query result - skip it
			next();
		}
	}
}

package me.prettyprint.cassandra.service;

import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
import java.util.Iterator;
import me.prettyprint.cassandra.service.template.SliceFilter;
import me.prettyprint.hector.api.beans.Row;
import me.prettyprint.hector.api.query.RangeSlicesQuery;

/**
 * Iterates over the range slices, automatically refreshing the query until all matching rows are returned.
 *
 * @author thrykol
 */
public class RangeSlicesIterator<K, N, V> implements Iterator<Row<K, N, V>> {

	private RangeSlicesQuery<K, N, V> query;
	private K startKey;
	private K endKey;
	private PeekingIterator<Row<K, N, V>> iterator;
	private SliceFilter<Row<K, N, V>> filter = null;
	private int rows = 0;

	public RangeSlicesIterator(RangeSlicesQuery<K, N, V> query, K startKey, K endKey) {
		this.query = query;
		this.startKey = startKey;
		this.endKey = endKey;

		this.query.setKeys(startKey, endKey);
	}

	@Override
	public boolean hasNext() {
		if (iterator == null) {
			// First time through
			iterator = Iterators.peekingIterator(query.execute().get().getList().iterator());
		} else if (!iterator.hasNext() && rows == query.getRowCount()) {  // only need to do another query if maximum rows were retrieved
			query.setKeys(startKey, endKey);
			iterator = Iterators.peekingIterator(query.execute().get().getList().iterator());
			rows = 0;

			if (iterator.hasNext()) {
				// First element is startKey which was the last element on the previous query result - skip it
				next();
			}
		}

		while(filter != null && iterator != null && iterator.hasNext() && !filter.accept(iterator.peek())) {
			next();

			if(!iterator.hasNext() && rows == query.getRowCount()) {
				refresh();
			}
		}

		return iterator.hasNext();
	}

	@Override
	public Row<K, N, V> next() {
		Row<K, N, V> row = iterator.next();
		startKey = row.getKey();
		rows++;

		return row;
	}

	@Override
	public void remove() {
		iterator.remove();
	}

	public RangeSlicesIterator<K, N, V> setFilter(SliceFilter<Row<K, N, V>> filter) {
		this.filter = filter;

		return this;
	}
	
	private void refresh() {
		query.setKeys(startKey, endKey);
		iterator = Iterators.peekingIterator(query.execute().get().getList().iterator());
		rows = 0;

		if (iterator.hasNext()) {
			// First element is startKey which was the last element on the previous query result - skip it
			next();
		}
	}
}

set endKey field in constructor