/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.collection.iterators;

import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Spliterator;

/**
 * Abstract class which can be used to implement iterators for any collection which offers random access.
 * Class implements both forward and backward iteration.
 * Class does not support modifications.
 *
 * @param <T> T
 */
public abstract class RandomAccessIterator<T> implements ListIterator<T> {
    
    private int index;
    
    private int last;

    /**
     * Must be implemented: returns size of collection (in constant time)
     * @return int
     */
    protected abstract int size();

    /**
     * Must be implemented: returns element at specified index (in constant time).
     * Iterator validates index before calling this method.
     *
     * @param index index
     * @return T
     */
    protected abstract T get(int index);

    /**
     * Optional: remove element at specified index.
     * Iterator validates index before calling this method.
     *
     * @param index index
     */
    protected void remove(int index) {
        throw new UnsupportedOperationException();
    }

    /**
     * Optional: add element at specified index.
     * Iterator validates index before calling this method.
     *
     * @param index index
     * @param value value
     */
    protected void add(int index, T value) {
        throw new UnsupportedOperationException();
    }

    /**
     * Optional: change element at specified index.
     * Iterator validates index before calling this method.
     *
     * @param index index
     * @param value value
     */
    protected void set(int index, T value) {
        throw new UnsupportedOperationException();
    }

    /**
     * Creates iterator starting from first element.
     */
    protected RandomAccessIterator() {
        this(0);
    }

    /**
     * Creates iterator starting from "index" element.
     *
     * @param index index
     */
    protected RandomAccessIterator(int index) {
        this.index = index;
        this.last = -1;
    }

    @Override
    public final boolean hasNext() {
        return index < size();
    }

    @Override
    public final boolean hasPrevious() {
        return index > 0;
    }

    @Override
    public final T next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        return get(last = index++);
    }

    @Override
    public final T previous() {
        if (!hasPrevious()) {
            throw new NoSuchElementException();
        }
        return get(last = --index);
    }

    @Override
    public final int nextIndex() {
        return index;
    }

    @Override
    public final int previousIndex() {
        return index - 1;
    }

    @Override
    public final void add(T value) {
        add(index++, value);
    }

    @Override
    public final void set(T value) {
        if (last == -1) {
            throw new IllegalStateException();
        }
        set(last, value);
    }

    @Override
    public final void remove() {
        if (last == -1) {
            throw new IllegalStateException();
        }
        remove(last);
        // If the last operation was a next(), 
        // we are removing an element *before* us, 
        // and we must decrease pos correspondingly.
        if (last < index) {
            index--;
        }
        last = -1;
    }

}
