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

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

/**
 * Klasa abstrakcyjna wygodna do implementowania iteratora przeglądającego
 * obiekt o strukturze listo-podobnej. Szczególnie wygodna przy pisaniu delegatorów,
 * które nie muszą koncentrować się na implementacji logiki iteratora.
 * @param <T> 
 * @author ranides
 * jako RandomListIterator, dać defaultowe metody dla read-write wzorując się na ImmutableListIterator
 */
public abstract class AbstractListIterator<T> implements ListIterator<T> {
    
    /**
     * Zobacz: {@link java.util.List#remove}
     * @param index 
     */
    protected abstract void remove(int index);
    
    /**
     * Zobacz: {@link java.util.List#add(int, java.lang.Object) List.add}
     * @param index
     * @param value 
     */
    protected abstract void add(int index, T value);
    
    /**
     * Zobacz: {@link java.util.List#set}
     * @param index
     * @param value
     */
    protected abstract void set(int index, T value);
    
    /**
     * Zobacz: {@link java.util.List#get}
     * @param index
     * @return
     */
    protected abstract T get(int index);
    
    /**
     * Zobacz: {@link java.util.List#size}
     * @return
     */
    protected abstract int size();

/* ************************************************************************** */
    
    private int index;
    
    private int cursor;

    /**
     * Tworzy iterator wskazujący na pierwszą pozycję w liście
     */
    public AbstractListIterator() {
        this(0);
    }

    /**
     * Tworzy iterator wskazujący na podaną pozycję w liście
     * @param index
     */
    public AbstractListIterator(int index) {
        this.index = index;
        this.cursor = index;
    }

    @Override
    public final void set(T value) {
        if (index == -1) {
            throw new IllegalStateException("set must be called after next or previous");
        }
        set(index, value);
    }

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

    @Override
    public final void remove() {
        remove(index);
        index = - 1;
    }

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

    @Override
    public final T next() {
        if( cursor >= size() ) { 
            throw new NoSuchElementException(); 
        }
        index = cursor++;
        return get(index);
    }

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

    @Override
    public final T previous() {
        if( cursor < 1 ) { 
            throw new NoSuchElementException(); 
        }
        index = --cursor;
        return get(index);
    }

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

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