/*
 * @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.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * ForwardIterator is abstract class which can be implemented in a way similar to Spliterator.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @param <T> T
 */
public abstract class ForwardIterator<T> implements Iterator<T> {
    
    private boolean ready = false;
    private T last  = null;

    /**
     * "next" method is stateful and is called on every iteration.
     * It should pass generated value to "action" and return "true".
     * If there is no more results, method should return "false"
     *
     * @param action action
     * @return bool
     */
    protected abstract boolean next(Consumer<? super T> action);

    @Override
    public boolean hasNext() {
        return ready || (ready = next(v -> last = v));
    }
        
    @Override
    public T next() {
        if(ready) {
            ready = false;
            return last;
        }
        if(!next(v -> last = v)) {
            throw new NoSuchElementException();
        }
        return last;
    }

    /**
     * Returns iterator which generates values by recursively applying "next" function into previous results.
     *
     * For example it can be used to iterate through all parents of node.
     *
     * It stops iteration when function returns "null".
     *
     * @param current starting value
     * @param next recursive generator
     * @param <T> T
     * @return iterator
     */
    public static <T> Iterator<T> produce(T current, Function<? super T,? extends T> next) {
        return new MIterator<>(current, next);
    }
    
    private static final class MIterator<T> extends ForwardIterator<T> {
        
        private final Function<? super T,? extends T> next;
                
        private T current;

        public MIterator(T current, Function<? super T,? extends T> next) {
            this.next = next;
            this.current = current;
        }
        
        @Override
        protected boolean next(Consumer<? super T> action) {
            if(null == current) {
                return false;
            }
            action.accept(current);
            current = next.apply(current);
            return true;
        }
    }
    
}
