package com.codepoetics.protonpack.stateful;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.IntStream;
import java.util.stream.Stream;

final class Window<T> {

  static <T> Window<T> initialise(int size) {
    List<T> initialContents = new ArrayList<>(size);
    IntStream.range(0, size).forEach(i -> initialContents.add(null));
    return new Window<>(size, 0, 0, initialContents);
  }

  private final int size;
  private final int count;
  private final int index;
  private final List<T> contents;

  private Window(int size, int count, int index, List<T> contents) {
    this.size = size;
    this.count = count;
    this.index = index;
    this.contents = contents;
  }

  Window<T> add(T item) {
    List<T> newContents = new ArrayList<>(contents);
    newContents.set(index, item);

    int newIndex = index + 1;
    if (newIndex == size) {
      newIndex = 0;
    }

    int newCount = count + 1;
    if (newCount > size) {
      newCount = size;
    }

    return new Window<>(size, newCount, newIndex, newContents);
  }

  <R> Stream<R> reduce(Function<Stream<T>, R> reducer) {
    if (count < size) {
      return Stream.empty();
    }
    try (Stream<T> windowStream = Stream.concat(
        IntStream.range(index, size).mapToObj(contents::get),
        IntStream.range(0, index).mapToObj(contents::get))) {
      return Stream.of(reducer.apply(windowStream));
    }
  }
}