package com.srbenoit.modeling.grid;

import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * An iterator that will iterate over a grid's elements.
 */
public final class GridIterator2D implements Iterator<GridMember2Int> {

    /** the source grid being iterated */
    private final Grid2D source;

    /** the index of the current element, -1 if finished or not yet started */
    private int currentElement;

    /** the index of the next element to be returned, -1 if finished */
    private int nextElement;

    /**
     * Constructs a new <code>GridIterator</code>.
     *
     * @param  sourceGrid  the source array being iterated
     */
    public GridIterator2D(final Grid2D sourceGrid) {

        this.source = sourceGrid;
        this.nextElement = sourceGrid.getNextFilled(0);
        this.currentElement = -1;
    }

    /**
     * Resets the iterator to iterate over the grid again. This puts the iterator into the same
     * state it would be in if it had just been created. This saves object creations when iterating
     * a grid multiple times.
     */
    public void reset() {

        this.nextElement = this.source.getNextFilled(0);
        this.currentElement = -1;
    }

    /**
     * Returns <code>true</code> if the iteration has more elements. In other words, returns <code>
     * true</code> if <code>next</code> would return an element rather than throwing an exception.
     *
     * @return  <code>true</code> if the iteration has more elements; <code>false</code> otherwise
     */
    @Override public boolean hasNext() {

        return this.nextElement != -1;
    }

    /**
     * Returns the next element in the iteration.
     *
     * @return  the next element in the iteration
     * @throws  NoSuchElementException  if the iteration has no more elements
     */
    @Override public GridMember2Int next() throws NoSuchElementException {

        GridMember2Int result;

        result = this.source.get(this.nextElement);
        this.currentElement = this.nextElement;
        this.nextElement = this.source.getNextFilled(this.currentElement + 1);

        return result;
    }

    /**
     * Removes from the underlying collection the last element returned by the iterator (optional
     * operation). This method can be called only once per call to <code>next</code>.
     *
     * <p>The behavior of an iterator is unspecified if the underlying collection is modified while
     * the iteration is in progress in any way other than by calling this method.
     *
     * @throws  IllegalStateException  if the <code>next</code> method has not yet been called, or
     *                                 the <code>remove</code> method has already been called after
     *                                 the last call to the <code>next</code> method
     */
    @Override public void remove() throws IllegalStateException {

        if (this.currentElement == -1) {
            throw new IllegalStateException();
        }

        this.source.remove(this.currentElement);
        this.currentElement = -1;
    }
}
