"use strict"
var __createBinding =
  (this && this.__createBinding) ||
  (Object.create
    ? function (o, m, k, k2) {
        if (k2 === undefined) k2 = k
        Object.defineProperty(o, k2, {
          enumerable: true,
          get: function () {
            return m[k]
          }
        })
      }
    : function (o, m, k, k2) {
        if (k2 === undefined) k2 = k
        o[k2] = m[k]
      })
var __setModuleDefault =
  (this && this.__setModuleDefault) ||
  (Object.create
    ? function (o, v) {
        Object.defineProperty(o, "default", { enumerable: true, value: v })
      }
    : function (o, v) {
        o["default"] = v
      })
var __importStar =
  (this && this.__importStar) ||
  function (mod) {
    if (mod && mod.__esModule) return mod
    var result = {}
    if (mod != null)
      for (var k in mod)
        if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
          __createBinding(result, mod, k)
    __setModuleDefault(result, mod)
    return result
  }
Object.defineProperty(exports, "__esModule", { value: true })
exports.mergeWith_ = exports.mergeWith = void 0
// tracing: off
const C = __importStar(require("../../Cause"))
const A = __importStar(require("../../Collections/Immutable/Chunk"))
const Tp = __importStar(require("../../Collections/Immutable/Tuple"))
const E = __importStar(require("../../Either"))
const Ex = __importStar(require("../../Exit"))
const F = __importStar(require("../../Fiber"))
const Function_1 = require("../../Function")
const O = __importStar(require("../../Option"))
const RefM = __importStar(require("../../RefM"))
const T = __importStar(require("../_internal/effect"))
const M = __importStar(require("../_internal/managed"))
const H = __importStar(require("../Handoff"))
const TK = __importStar(require("../Take"))
const definitions_1 = require("./definitions")
/**
 * Merges this stream and the specified stream together to a common element
 * type with the specified mapping functions.
 *
 * New produced stream will terminate when both specified stream terminate if
 * no termination strategy is specified.
 *
 * @dataFirst mergeWith_
 */
function mergeWith(that, l, r, strategy = "Both") {
  return (self) => mergeWith_(self, that, l, r, strategy)
}
exports.mergeWith = mergeWith
/**
 * Merges this stream and the specified stream together to a common element
 * type with the specified mapping functions.
 *
 * New produced stream will terminate when both specified stream terminate if
 * no termination strategy is specified.
 */
function mergeWith_(self, that, l, r, strategy = "Both") {
  return new definitions_1.Stream(
    M.map_(
      M.tap_(
        M.tap_(
          M.let_(
            M.bind_(
              M.bind_(
                M.bind_(
                  M.bind_(M.do, "handoff", () => M.fromEffect(H.make())),
                  "done",
                  () => M.fromEffect(RefM.makeRefM(O.none))
                ),
                "chunksL",
                () => self.proc
              ),
              "chunksR",
              () => that.proc
            ),
            "handler",
            ({ done, handoff }) =>
              (pull, terminate) =>
                T.toManagedRelease_(
                  T.interruptible(
                    T.fork(
                      T.repeatWhile_(
                        T.chain_(done.get, (o) => {
                          if (o._tag === "Some" && o.value) {
                            return T.succeed(false)
                          } else {
                            return T.chain_(T.result(pull), (exit) =>
                              RefM.modify((o) => {
                                const causeOrChunk = Ex.fold_(
                                  exit,
                                  (c) => E.left(C.sequenceCauseOption(c)),
                                  E.right
                                )
                                if (o._tag === "Some" && o.value) {
                                  return T.succeed(Tp.tuple(false, o))
                                } else if (causeOrChunk._tag === "Right") {
                                  return T.as_(
                                    H.offer_(handoff, TK.chunk(causeOrChunk.right)),
                                    Tp.tuple(true, o)
                                  )
                                } else if (
                                  causeOrChunk._tag === "Left" &&
                                  causeOrChunk.left._tag === "Some"
                                ) {
                                  return T.as_(
                                    H.offer_(handoff, TK.halt(causeOrChunk.left.value)),
                                    Tp.tuple(false, O.some(true))
                                  )
                                } else if (
                                  causeOrChunk._tag === "Left" &&
                                  causeOrChunk.left._tag === "None" &&
                                  (terminate || o._tag === "Some")
                                ) {
                                  return T.as_(
                                    H.offer_(handoff, TK.end),
                                    Tp.tuple(false, O.some(true))
                                  )
                                } else {
                                  return T.succeed(Tp.tuple(false, O.some(false)))
                                }
                              })(done)
                            )
                          }
                        }),
                        Function_1.identity
                      )
                    )
                  ),
                  F.interrupt
                )
          ),
          ({ chunksL, handler }) =>
            handler(
              T.map_(chunksL, A.map(l)),
              strategy === "Left" || strategy === "Either"
            )
        ),
        ({ chunksR, handler }) =>
          handler(
            T.map_(chunksR, A.map(r)),
            strategy === "Right" || strategy === "Either"
          )
      ),
      ({ done, handoff }) =>
        T.map_(
          T.bind_(
            T.bind_(
              T.bind_(T.do, "done", () => done.get),
              "take",
              (s) =>
                s.done._tag === "Some" && s.done.value
                  ? T.some(H.poll(handoff))
                  : H.take(handoff)
            ),
            "result",
            ({ take }) => TK.done(take)
          ),
          ({ result }) => result
        )
    )
  )
}
exports.mergeWith_ = mergeWith_
//# sourceMappingURL=mergeWith.js.map
