package org.basex.query.iter;

import org.basex.query.QueryException;
import org.basex.query.item.Empty;
import org.basex.query.item.Item;
import org.basex.query.item.Seq;
import org.basex.query.item.Value;
import org.basex.util.Array;

/**
 * Iterator interface.
 *
 * @author BaseX Team 2005-11, BSD License
 * @author Christian Gruen
 */
public abstract class Iter {
  /**
   * Returns the next item or {@code null} if no other items are found.
   * @return resulting item or {@code null}
   * @throws QueryException query exception
   */
  public abstract Item next() throws QueryException;

  /**
   * Returns the specified item, or an arbitrary item if the index is invalid.
   * This method needs to be implemented - and should only be called - if
   * {@link #size} returns the correct number of results. A calling method
   * should call {@link #reset} after the last items has been retrieved.
   * @param i value offset
   * @return specified item
   * @throws QueryException query exception
   */
  @SuppressWarnings("unused")
  public Item get(final long i) throws QueryException {
    return null;
  }

  /**
   * Returns the iterator size. Note: {@code -1} is returned if the
   * result size is unknown. If this method is implemented by an iterator,
   * {@link #get} needs to be implemented as well.
   * @return number of entries
   */
  public long size() {
    return -1;
  }

  /**
   * Resets the iterator and returns {@code true} if operation was successful.
   * {@code false} is returned if the iterator cannot be reset.
   * @return true if operator could be reset
   */
  public boolean reset() {
    return false;
  }

  /**
   * Returns a sequence with all iterator values.
   * Must only be called if {@link #next} has not been called before.
   * @return sequence
   * @throws QueryException query exception
   */
  public Value value() throws QueryException {
    // check if sequence is empty
    Item i = next();
    if(i == null) return Empty.SEQ;

    // if possible, allocate array with final size, and add all single items
    Item[] item = new Item[Math.max(1, (int) size())];
    int s = 0;
    do {
      if(s == item.length) item = extend(item);
      item[s++] = i;
    } while((i = next()) != null);

    // create final value
    return Seq.get(item, s);
  }

  /**
   * Doubles the size of an item array.
   * @param it item array
   * @return resulting array
   */
  protected static Item[] extend(final Item[] it) {
    final int s = it.length;
    final Item[] tmp = new Item[Array.newSize(s)];
    System.arraycopy(it, 0, tmp, 0, s);
    return tmp;
  }
}
