/*
 * Copyright (c) 2009 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 *
 */

package com.jscape.util.collections;

import com.jscape.util.functor.UnaryPredicate;

import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.NoSuchElementException;

import static com.jscape.util.Parameters.assertStateValid;

/**
 * @author Alex Usun
 */
public class FilterIterator<T>
        implements Iterator<T> {

    /**
     * Base.
     */
    private final Iterator<? extends T> base;
    /**
     * Predicate.
     */
    private final UnaryPredicate<? super T> predicate;
    /**
     * Next.
     */
    private T next;
    /**
     * Next set.
     */
    private boolean nextSet;
    /**
     * Can remove.
     */
    private boolean canRemove;

    /**
     * @param base      base
     * @param predicate predicate
     * @param <T>       Iterator<T>
     *
     * @return Iterator<T>
     */
    public static <T> Iterator<T> adapt(Iterator<? extends T> base, UnaryPredicate<? super T> predicate) {
        return new FilterIterator<T>(base, predicate);
    }

    /**
     * @param base      base
     * @param predicate predicate
     * @param <T>       Iterator<T>
     *
     * @return Iterator<T>
     */
    public static <T> Iterator<T> iterator(Collection<? extends T> base, UnaryPredicate<? super T> predicate) {
        return adapt(base.iterator(), predicate);
    }

    /**
     * @param base      base
     * @param predicate predicate
     * @param <T>       Iterator<T>
     *
     * @return Iterator<T>
     */
    public static <T> Iterator<T> iterator(Enumeration<T> base, UnaryPredicate<? super T> predicate) {
        return adapt(EnumerationIterator.iterator(base), predicate);
    }

    /**
     * @param base      base
     * @param predicate predicate
     * @param <T>       Iterator<T>
     *
     * @return Iterator<T>
     */
    public static <T> Iterator<T> iterator(T[] base, UnaryPredicate<? super T> predicate) {
        return adapt(ArrayIterator.iterator(base), predicate);
    }

    /**
     * @param base      base
     * @param predicate predicate
     */
    public FilterIterator(Iterator<? extends T> base, UnaryPredicate<? super T> predicate) {
        this.base = base;
        this.predicate = predicate;
    }

    /**
     * @return has next
     */
    public boolean hasNext() {
        return this.nextSet || setNext();
    }

    /**
     * @return T
     */
    public T next() {
        if (hasNext()) {
            this.nextSet = false;
            this.canRemove = true;

            return getNext();
        }

        throw new NoSuchElementException();
    }

    /** */
    public void remove() {
        assertStateValid(this.canRemove, "Error removing element.");
        this.base.remove();
        this.canRemove = false;
    }

    /**
     * @return T
     */
    private T getNext() {
        T tmp = this.next;
        this.next = null;

        return tmp;
    }

    /**
     * @return set next
     */
    private boolean setNext() {
        while (this.base.hasNext() && !this.nextSet) {
            this.next = this.base.next();
            this.nextSet = this.predicate.evaluate(this.next);
        }

        this.canRemove = false;

        if (!this.nextSet) {
            this.next = null;
        }

        return this.nextSet;
    }

}
