// tracing: off
import * as A from "../../Collections/Immutable/Chunk"
import * as Tp from "../../Collections/Immutable/Tuple"
import { pipe } from "../../Function"
import * as T from "../_internal/effect"
import * as F from "../_internal/fiber"
import * as M from "../_internal/managed"
import * as Ref from "../_internal/ref"
import * as chain from "./chain"
import * as concat from "./concat"
import { Stream } from "./definitions"
import { fromChunk } from "./fromChunk"
import { fromEffect } from "./fromEffect"
import { fromEffectOption } from "./fromEffectOption"
import * as mergeWith from "./mergeWith"
import { repeatEffectOption } from "./repeatEffectOption"

function pullNonEmpty(pull) {
  return T.chain_(pull, (chunk) =>
    A.isEmpty(chunk) ? pullNonEmpty(pull) : T.succeed(chunk)
  )
}
/**
 * Zips the two streams so that when a value is emitted by either of the two streams,
 * it is combined with the latest value from the other stream to produce a result.
 *
 * Note: tracking the latest value is done on a per-chunk basis. That means that
 * emitted elements that are not the last value in chunks will never be used for zipping.
 */

export function zipWithLatest(self, that) {
  return (f) =>
    new Stream(
      M.map_(
        M.bind_(
          M.bind_(
            M.bind_(M.do, "left", () => M.map_(self.proc, pullNonEmpty)),
            "right",
            () => M.map_(that.proc, pullNonEmpty)
          ),
          "pull",
          ({ left, right }) =>
            chain.chain(({ tuple: [l, r, leftFirst] }) =>
              chain.chain(({ tuple: [latestLeft, latestRight] }) =>
                concat.concat(
                  chain.chain(fromChunk)(
                    mergeWith.mergeWith_(
                      repeatEffectOption(
                        T.zip_(
                          T.tap_(left, (chunk) =>
                            latestLeft.set(A.unsafeGet_(chunk, A.size(chunk) - 1))
                          ),
                          latestRight.get
                        )
                      ),
                      repeatEffectOption(
                        T.zip_(
                          T.tap_(right, (chunk) =>
                            latestRight.set(A.unsafeGet_(chunk, A.size(chunk) - 1))
                          ),
                          latestLeft.get
                        )
                      ),
                      ({ tuple: [leftChunk, rightLatest] }) =>
                        A.map_(leftChunk, (_) => f(_, rightLatest)),
                      ({ tuple: [rightChunk, leftLatest] }) =>
                        A.map_(rightChunk, (_) => f(leftLatest, _))
                    )
                  )
                )(
                  fromChunk(
                    leftFirst
                      ? A.map_(r, (_) => f(A.unsafeGet_(l, A.size(l) - 1), _))
                      : A.map_(l, (_) => f(_, A.unsafeGet_(r, A.size(r) - 1)))
                  )
                )
              )(
                fromEffect(
                  T.zip_(
                    Ref.makeRef(A.unsafeGet_(l, A.size(l) - 1)),
                    Ref.makeRef(A.unsafeGet_(r, A.size(r) - 1))
                  )
                )
              )
            )(
              fromEffectOption(
                T.raceWith_(
                  left,
                  right,
                  (leftDone, rightFiber) =>
                    T.zipWith_(T.done(leftDone), F.join(rightFiber), (a, b) =>
                      Tp.tuple(a, b, true)
                    ),
                  (rightDone, leftFiber) =>
                    T.zipWith_(T.done(rightDone), F.join(leftFiber), (r, l) =>
                      Tp.tuple(l, r, false)
                    )
                )
              )
            ).proc
        ),
        ({ pull }) => pull
      )
    )
}
//# sourceMappingURL=zipWithLatest.js.map
