package com.codepoetics.protonpack;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Function;

class ListZippingSpliterator<T, O> implements Spliterator<O> {

    static <T, O> Spliterator<O> zipping(List<Spliterator<T>> spliterators, Function<List<T>, O> combiner) {
        return new ListZippingSpliterator<>(spliterators, combiner);
    }

    private final List<Spliterator<T>> spliterators;
    private final Function<List<T>, O> combiner;
    private boolean hadNext;

    private ListZippingSpliterator(List<Spliterator<T>> spliterators, Function<List<T>, O> combiner) {
        this.spliterators = spliterators;
        this.combiner = combiner;
    }

    @Override
    public boolean tryAdvance(Consumer<? super O> action) {
        hadNext = false;
        Iterator<Spliterator<T>> si = spliterators.iterator();
        if(si.hasNext()) {
            tryAdvance(si, new ArrayList<>(spliterators.size()), action);
        }
        return hadNext;
    }

    private void tryAdvance(Iterator<Spliterator<T>> si, List<T> acc, Consumer<? super O> action) {
        if (si.hasNext()) {
            si.next().tryAdvance(s -> {
                acc.add(s);
                tryAdvance(si, acc, action);
            });
        } else {
            action.accept(combiner.apply(acc));
            hadNext = true;
        }
    }

    @Override
    public Spliterator<O> trySplit() {
        return null;
    }

    @Override
    public long estimateSize() {
        // TODO: benchmark and cache?
        return spliterators.stream()
                .mapToLong(Spliterator::estimateSize)
                .min().orElse(0);
    }

    @Override
    public int characteristics() {
        // TODO: benchmark and cache?
        int characteristics = spliterators.stream()
                .mapToInt(Spliterator::characteristics)
                .reduce(0, (i, j) -> i & j);
        return characteristics & ~(Spliterator.DISTINCT | Spliterator.SORTED);
    }
}

@@ -209,16 +209,68 @@
 unction;
+%0Aimport static java.util.function.Predicate.isEqual;
 %0A%0Aclass 
@@ -617,37 +617,8 @@
 ner;
-%0A    private boolean hadNext;
 %0A%0A  
@@ -887,127 +887,82 @@
     
-hadNext = false;%0A        Iterator%3CSpliterator%3CT%3E%3E si = spliterators.iterator();%0A        if(si.hasNext()) %7B%0A
+if (spliterators.isEmpty()) %7B%0A            return false;%0A    
     
+%7D%0A
         
 tryA
@@ -957,30 +957,29 @@
         
-tryAdvance(si,
+List%3CT%3E acc =
  new Arr
@@ -1007,25 +1007,16 @@
 .size())
-, action)
 ;%0A      
@@ -1021,236 +1021,97 @@
     
-%7D%0A        return hadNext;%0A    %7D%0A%0A    private void tryAdvance(Iterator%3CSpliterator%3CT%3E%3E si, List%3CT%3E acc, Consumer%3C? super O%3E action) %7B%0A        if (si.hasNext()) %7B%0A            si.next().tryAdvance(s -%3E %7B%0A                acc.add(s);
+boolean hadNext = spliterators.stream()%0A                .map(s -%3E s.tryAdvance(acc::add))
 %0A   
@@ -1127,67 +1127,62 @@
     
-tryAdvance(si, acc, action);%0A            %7D);%0A        %7D else
+.allMatch(isEqual(Boolean.TRUE));%0A        if (hadNext)
  %7B%0A 
@@ -1240,37 +1240,33 @@
     
+%7D%0A
     
-hadNext = true;%0A        %7D
+    return hadNext;
 %0A   

replace recursion by iteration