// tracing: off
import * as C from "../../Cause"
import * as A from "../../Collections/Immutable/Chunk"
import { pipe } from "../../Function"
import * as O from "../../Option"
import * as T from "../_internal/effect"
import * as M from "../_internal/managed"
import * as Ref from "../_internal/ref"
import * as Pull from "../Pull"
import { Stream } from "./definitions"
import { halt } from "./halt"

class State {
  constructor(buffer, done) {
    this.buffer = buffer
    this.done = done
  }
}

function emitOrAccumulate(buffer, done, ref, pull, n) {
  if (A.size(buffer) < n) {
    if (done) {
      if (A.isEmpty(buffer)) {
        return Pull.end
      } else {
        return T.zipRight_(ref.set(new State(A.empty(), true)), Pull.emitChunk(buffer))
      }
    } else {
      return T.foldM_(
        pull,
        O.fold(() => emitOrAccumulate(buffer, true, ref, pull, n), Pull.fail),
        (ch) => emitOrAccumulate(A.concat_(buffer, ch), false, ref, pull, n)
      )
    }
  } else {
    const {
      tuple: [chunk, leftover]
    } = A.splitAt_(buffer, n)
    return T.zipRight_(ref.set(new State(leftover, done)), Pull.emitChunk(chunk))
  }
}
/**
 * Re-chunks the elements of the stream into chunks of
 * `n` elements each.
 * The last chunk might contain less than `n` elements
 */

export function chunkN_(self, n) {
  if (n < 1) {
    return halt(C.die(new C.IllegalArgumentException("chunkN: n must be at least 1")))
  } else {
    return new Stream(
      M.map_(
        M.let_(
          M.bind_(
            M.bind_(M.do, "ref", () =>
              T.toManaged(Ref.makeRef(new State(A.empty(), false)))
            ),
            "p",
            () => self.proc
          ),
          "pull",
          ({ p, ref }) =>
            T.chain_(ref.get, (s) => emitOrAccumulate(s.buffer, s.done, ref, p, n))
        ),
        ({ pull }) => pull
      )
    )
  }
}
/**
 * Re-chunks the elements of the stream into chunks of
 * `n` elements each.
 * The last chunk might contain less than `n` elements
 */

export function chunkN(n) {
  return (self) => chunkN_(self, n)
}
//# sourceMappingURL=chunkN.js.map
