/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.datastax.driver.core;

import com.google.common.util.concurrent.ListenableFuture;
import java.util.Iterator;
import java.util.List;

/**
 * Defines an iterable whose elements can be remotely fetched and paged, possibly asynchronously.
 */
public interface PagingIterable<S extends PagingIterable<S, T>, T> extends Iterable<T> {

  /**
   * Returns whether this result set has more results.
   *
   * @return whether this result set has more results.
   */
  boolean isExhausted();

  /**
   * Whether all results from this result set have been fetched from the database.
   *
   * <p>Note that if {@code isFullyFetched()}, then {@link #getAvailableWithoutFetching} will return
   * how many rows remain in the result set before exhaustion. But please note that {@code
   * isFullyFetched()} never guarantees that the result set is exhausted (you should call {@link
   * #isExhausted()} to verify it).
   *
   * @return whether all results have been fetched.
   */
  boolean isFullyFetched();

  /**
   * The number of rows that can be retrieved from this result set without blocking to fetch.
   *
   * @return the number of rows readily available in this result set. If {@link #isFullyFetched()},
   *     this is the total number of rows remaining in this result set (after which the result set
   *     will be exhausted).
   */
  int getAvailableWithoutFetching();

  /**
   * Force fetching the next page of results for this result set, if any.
   *
   * <p>This method is entirely optional. It will be called automatically while the result set is
   * consumed (through {@link #one}, {@link #all} or iteration) when needed (i.e. when {@code
   * getAvailableWithoutFetching() == 0} and {@code isFullyFetched() == false}).
   *
   * <p>You can however call this method manually to force the fetching of the next page of results.
   * This can allow to prefetch results before they are strictly needed. For instance, if you want
   * to prefetch the next page of results as soon as there is less than 100 rows readily available
   * in this result set, you can do:
   *
   * <pre>
   *   ResultSet rs = session.execute(...);
   *   Iterator&lt;Row&gt; iter = rs.iterator();
   *   while (iter.hasNext()) {
   *       if (rs.getAvailableWithoutFetching() == 100 &amp;&amp; !rs.isFullyFetched())
   *           rs.fetchMoreResults();
   *       Row row = iter.next()
   *       ... process the row ...
   *   }
   * </pre>
   *
   * This method is not blocking, so in the example above, the call to {@code fetchMoreResults} will
   * not block the processing of the 100 currently available rows (but {@code iter.hasNext()} will
   * block once those rows have been processed until the fetch query returns, if it hasn't yet).
   *
   * <p>Only one page of results (for a given result set) can be fetched at any given time. If this
   * method is called twice and the query triggered by the first call has not returned yet when the
   * second one is performed, then the 2nd call will simply return a future on the currently in
   * progress query.
   *
   * @return a future on the completion of fetching the next page of results. If the result set is
   *     already fully retrieved ({@code isFullyFetched() == true}), then the returned future will
   *     return immediately but not particular error will be thrown (you should thus call {@link
   *     #isFullyFetched()} to know if calling this method can be of any use}).
   */
  ListenableFuture<S> fetchMoreResults();

  /**
   * Returns the next result from this result set.
   *
   * @return the next row in this result set or null if this result set is exhausted.
   */
  T one();

  /**
   * Returns all the remaining rows in this result set as a list.
   *
   * <p>Note that, contrary to {@link #iterator()} or successive calls to {@link #one()}, this
   * method forces fetching the full content of the result set at once, holding it all in memory in
   * particular. It is thus recommended to prefer iterations through {@link #iterator()} when
   * possible, especially if the result set can be big.
   *
   * @return a list containing the remaining results of this result set. The returned list is empty
   *     if and only the result set is exhausted. The result set will be exhausted after a call to
   *     this method.
   */
  List<T> all();

  /**
   * Returns an iterator over the rows contained in this result set.
   *
   * <p>The {@link Iterator#next} method is equivalent to calling {@link #one}. So this iterator
   * will consume results from this result set and after a full iteration, the result set will be
   * empty.
   *
   * <p>The returned iterator does not support the {@link Iterator#remove} method.
   *
   * @return an iterator that will consume and return the remaining rows of this result set.
   */
  Iterator<T> iterator();

  /**
   * Returns information on the execution of the last query made for this result set.
   *
   * <p>Note that in most cases, a result set is fetched with only one query, but large result sets
   * can be paged and thus be retrieved by multiple queries. In that case this method return the
   * {@link ExecutionInfo} for the last query performed. To retrieve the information for all
   * queries, use {@link #getAllExecutionInfo}.
   *
   * <p>The returned object includes basic information such as the queried hosts, but also the
   * Cassandra query trace if tracing was enabled for the query.
   *
   * @return the execution info for the last query made for this result set.
   */
  ExecutionInfo getExecutionInfo();

  /**
   * Return the execution information for all queries made to retrieve this result set.
   *
   * <p>Unless the result set is large enough to get paged underneath, the returned list will be
   * singleton. If paging has been used however, the returned list contains the {@link
   * ExecutionInfo} objects for all the queries done to obtain this result set (at the time of the
   * call) in the order those queries were made.
   *
   * @return a list of the execution info for all the queries made for this result set.
   */
  List<ExecutionInfo> getAllExecutionInfo();
}
