content: /*
   * Copyright 2015, 2024 StreamEx contributors
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package one.util.streamex;
  
  import java.util.Spliterator;
  import java.util.Spliterators;
  import java.util.function.Consumer;
  import java.util.function.DoubleConsumer;
  import java.util.function.IntConsumer;
  import java.util.function.LongConsumer;
  import java.util.stream.DoubleStream;
  import java.util.stream.IntStream;
  import java.util.stream.LongStream;
  import java.util.stream.Stream;
  
  /* package */final class EmitterSpliterator<T> extends Spliterators.AbstractSpliterator<T> implements Consumer<T> {
      StreamEx.Emitter<T> e;
      private Spliterator<T> buf;
      private int vals;
      private Consumer<? super T> cons;
  
      EmitterSpliterator(StreamEx.Emitter<T> e) {
          super(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.IMMUTABLE);
          this.e = e;
      }
  
      @SuppressWarnings("unchecked")
      @Override
      public boolean tryAdvance(Consumer<? super T> action) {
          if (buf != null) {
              if (buf.tryAdvance(action))
                  return true;
              buf = null;
          }
          cons = action;
          for (vals = 0; vals == 0; e = e.next(this)) {
              if (e == null)
                  return false;
          }
          if (vals > 1) {
              buf = ((Stream.Builder<T>) cons).build().spliterator();
          }
          cons = null;
          return true;
      }
  
      @Override
      public void forEachRemaining(Consumer<? super T> action) {
          if (buf != null) {
              buf.forEachRemaining(action);
              buf = null;
          }
          StreamEx.Emitter<T> e = this.e;
          this.e = null;
          while (e != null)
              e = e.next(action);
      }
  
      @Override
      public void accept(T t) {
          if ((vals += vals < 3 ? 1 : 0) == 2) {
              cons = Stream.builder();
          }
          cons.accept(t);
      }
  
      static final class OfInt extends Spliterators.AbstractIntSpliterator implements IntConsumer {
          IntStreamEx.IntEmitter e;
          Spliterator.OfInt buf;
          int vals;
          IntConsumer cons;
  
          OfInt(IntStreamEx.IntEmitter e) {
              super(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL);
              this.e = e;
          }
  
          @Override
          public boolean tryAdvance(IntConsumer action) {
              if (buf != null) {
                  if (buf.tryAdvance(action))
                      return true;
                  buf = null;
              }
              cons = action;
              for (vals = 0; vals == 0; e = e.next(this)) {
                  if (e == null)
                      return false;
              }
              if (vals > 1) {
                  buf = ((IntStream.Builder) cons).build().spliterator();
              }
              cons = null;
              return true;
          }
  
          @Override
          public void forEachRemaining(IntConsumer action) {
              if (buf != null) {
                  buf.forEachRemaining(action);
                  buf = null;
              }
              IntStreamEx.IntEmitter e = this.e;
              this.e = null;
              while (e != null)
                  e = e.next(action);
          }
  
          @Override
          public void accept(int t) {
              if ((vals += vals < 3 ? 1 : 0) == 2) {
                  cons = IntStream.builder();
              }
              cons.accept(t);
          }
      }
  
      static final class OfLong extends Spliterators.AbstractLongSpliterator implements LongConsumer {
          LongStreamEx.LongEmitter e;
          Spliterator.OfLong buf;
          int vals;
          LongConsumer cons;
  
          OfLong(LongStreamEx.LongEmitter e) {
              super(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL);
              this.e = e;
          }
  
          @Override
          public boolean tryAdvance(LongConsumer action) {
              if (buf != null) {
                  if (buf.tryAdvance(action))
                      return true;
                  buf = null;
              }
              cons = action;
              for (vals = 0; vals == 0; e = e.next(this)) {
                  if (e == null)
                      return false;
              }
              if (vals > 1) {
                  buf = ((LongStream.Builder) cons).build().spliterator();
              }
              cons = null;
              return true;
          }
  
          @Override
          public void forEachRemaining(LongConsumer action) {
              if (buf != null) {
                  buf.forEachRemaining(action);
                  buf = null;
              }
              LongStreamEx.LongEmitter e = this.e;
              this.e = null;
              while (e != null)
                  e = e.next(action);
          }
  
          @Override
          public void accept(long t) {
              if ((vals += vals < 3 ? 1 : 0) == 2) {
                  cons = LongStream.builder();
              }
              cons.accept(t);
          }
      }
  
      static final class OfDouble extends Spliterators.AbstractDoubleSpliterator implements DoubleConsumer {
          DoubleStreamEx.DoubleEmitter e;
          Spliterator.OfDouble buf;
          int vals;
          DoubleConsumer cons;
  
          OfDouble(DoubleStreamEx.DoubleEmitter e) {
              super(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL);
              this.e = e;
          }
  
          @Override
          public boolean tryAdvance(DoubleConsumer action) {
              if (buf != null) {
                  if (buf.tryAdvance(action))
                      return true;
                  buf = null;
              }
              cons = action;
              for (vals = 0; vals == 0; e = e.next(this)) {
                  if (e == null)
                      return false;
              }
              if (vals > 1) {
                  buf = ((DoubleStream.Builder) cons).build().spliterator();
              }
              cons = null;
              return true;
          }
  
          @Override
          public void forEachRemaining(DoubleConsumer action) {
              if (buf != null) {
                  buf.forEachRemaining(action);
                  buf = null;
              }
              DoubleStreamEx.DoubleEmitter e = this.e;
              this.e = null;
              while (e != null)
                  e = e.next(action);
          }
  
          @Override
          public void accept(double t) {
              if ((vals += vals < 3 ? 1 : 0) == 2) {
                  cons = DoubleStream.builder();
              }
              cons.accept(t);
          }
      }
  }