package interfaces;

import static org.junit.Assert.assertThrows;

import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.BinaryOperator;

public class BinaryComputingIterator implements Iterator<Double> {

  private Iterator<Double> iterator1;
  private Iterator<Double> iterator2;

  private Optional<Double> default1 = Optional.empty();
  private Optional<Double> default2 = Optional.empty();

  private BinaryOperator<Double> operator;

  public BinaryComputingIterator(Iterator<Double> iterator1,
                                 Iterator<Double> iterator2,
                                 BinaryOperator<Double> operator)
  {
    this.iterator1 = iterator1;
    this.iterator2 = iterator2;
    this.operator  = operator;
  }

  public BinaryComputingIterator(Iterator<Double> iterator1,
                                 Iterator<Double> iterator2,
                                 Double default1,
                                 Double default2,
                                 BinaryOperator<Double> operator)
  {
    this.iterator1 = iterator1;
    this.iterator2 = iterator2;
    this.operator  = operator;
    this.default1  = Optional.ofNullable(default1);
    this.default2  = Optional.ofNullable(default2);
  }

  @Override
  public Double next() {
    return this.operator.apply(
      this.iterator1.hasNext() ? this.iterator1.next() : this.default1.orElseThrow(),
      this.iterator2.hasNext() ? this.iterator2.next() : this.default2.orElseThrow()
    );
  }

  @Override
  public boolean hasNext() {
    return this.default1.isPresent() || this.default2.isPresent()
    ? this.iterator1.hasNext() || this.iterator2.hasNext()
    : this.iterator1.hasNext() && this.iterator2.hasNext();
  }
  
}