
import sequences.util.*;


//
// filtered
//

inline filtered(predicate, forward ..seqs) = Filtered(predicate, zipped(..seqs));

inline overload filtered(predicate, forward seq) =
    Filtered(predicate, wrapSequence(seq));


private record Filtered[F, A] (
    predicate : F,
    seq : A,
);

[F, A | LightweightSequence?(A)]
overload LightweightSequence?(static Filtered[F,A]) = true;

[F,A | CallDefined?(UnderlyingContainerConstructor, Static[A])]
overload UnderlyingContainerConstructor(static Filtered[F,A]) =
    UnderlyingContainerConstructor(A);



//
// iterator, reverseIterator
//

[F, A]
inline overload iterator(a:Filtered[F, A]) {
    alias T = Type(captureValues(..next(iterator(null(A)^))));
    return FilteredIterator(a.predicate,
                            iterator(a.seq),
                            false,
                            T());
}

[F, A | CallDefined?(reverseIterator, A)]
inline overload reverseIterator(a:Filtered[F, A]) {
    alias T = Type(captureValues(..next(reverseIterator(null(A)^))));
    return FilteredIterator(a.predicate,
                            reverseIterator(a.seq),
                            false,
                            T());
}



//
// FilteredIterator
//

private record FilteredIterator[F, ITER, VALS] (
    predicate : F,
    iter : ITER,
    hasQueued? : Bool,
    queued : VALS,
);

[F, ITER, VALS]
inline overload hasNext?(iter:FilteredIterator[F, ITER, VALS]) {
    while (not iter.hasQueued?) {
        if (not hasNext?(iter.iter))
            return false;
        iter.queued = captureValues(..next(iter.iter));
        iter.hasQueued? = iter.predicate(..capturedRefs(iter.queued));
    }
    return true;
}

[F, ITER, VALS]
inline overload next(iter:FilteredIterator[F, ITER, VALS]) {
    if (not iter.hasQueued?) {
        var result = hasNext?(iter);
        assert(result);
    }
    iter.hasQueued? = false;
    return forward ..forwardValues(iter.queued);
}
