// tracing: off
import * as Chunk from "../Collections/Immutable/Chunk"
import * as Tp from "../Collections/Immutable/Tuple"
import * as Exit from "../Exit"
import * as Fiber from "../Fiber"
import { pipe } from "../Function"
import * as P from "../Promise"
import * as Ref from "../Ref"
import * as as from "./as"
import * as asUnit from "./asUnit"
import * as core from "./core"
import * as Do from "./do"
import { forEach_ } from "./excl-forEach"
import { flatten } from "./flatten"
import * as interruption from "./interruption"
import * as map from "./map"
import * as tap from "./tap"

function arbiter(fibers, winner, promise, fails) {
  return (res) =>
    Exit.foldM(
      (e) =>
        flatten(
          Ref.modify_(fails, (c) =>
            Tp.tuple(c === 0 ? asUnit.asUnit(P.halt(e)(promise)) : core.unit, c - 1)
          )
        ),
      (a) =>
        core.chain_(P.succeed(Tp.tuple(a, winner))(promise), (set) =>
          set
            ? Chunk.reduce_(fibers, core.unit, (io, f) =>
                f === winner ? io : tap.tap_(io, () => Fiber.interrupt(f))
              )
            : core.unit
        )
    )(res)
}
/**
 * Returns an effect that races this effect with all the specified effects,
 * yielding the value of the first effect to succeed with a value.
 * Losers of the race will be interrupted immediately.
 *
 * Note: in case of success eventual interruption errors are ignored
 */

export function raceAllWithStrategy(ios, interruptStrategy, __trace) {
  return map.map_(
    tap.tap_(
      Do.bind_(
        Do.bind_(
          Do.bind_(Do.do, "done", () => P.make()),
          "fails",
          () => Ref.makeRef(ios.length)
        ),
        "c",
        ({ done, fails }) =>
          interruption.uninterruptibleMask(
            ({ restore }) =>
              map.map_(
                Do.bind_(
                  Do.let_(
                    tap.tap_(
                      Do.bind_(Do.do, "fs", () =>
                        forEach_(ios, (x) => core.fork(interruption.interruptible(x)))
                      ),
                      ({ fs }) =>
                        Chunk.reduce_(fs, core.unit, (io, f) =>
                          core.chain_(io, () =>
                            core.fork(core.chain_(f.await, arbiter(fs, f, done, fails)))
                          )
                        )
                    ),
                    "inheritRefs",
                    () => (res) => as.as_(res.get(1).inheritRefs, res.get(0))
                  ),
                  "c",
                  ({ fs, inheritRefs }) =>
                    interruption.onInterrupt_(
                      restore(core.chain_(P.await(done), inheritRefs)),
                      () =>
                        Chunk.reduce_(fs, core.unit, (io, f) =>
                          tap.tap_(io, () => Fiber.interrupt(f))
                        )
                    )
                ),
                ({ c, fs }) => ({
                  c,
                  fs
                })
              ),
            __trace
          )
      ),
      ({ c: { fs } }) =>
        interruptStrategy === "wait" ? forEach_(fs, (f) => f.await) : core.unit
    ),
    ({ c: { c } }) => c
  )
}
/**
 * Returns an effect that races this effect with all the specified effects,
 * yielding the value of the first effect to succeed with a value.
 * Losers of the race will be interrupted immediately.
 *
 * Note: in case of success eventual interruption errors are ignored
 */

export function raceAll(ios, __trace) {
  return raceAllWithStrategy(ios, "background", __trace)
}
/**
 * Returns an effect that races this effect with all the specified effects,
 * yielding the value of the first effect to succeed with a value.
 * Losers of the race will be interrupted immediately.
 *
 * Note: in case of success eventual interruption errors are ignored
 */

export function raceAllWait(ios, __trace) {
  return raceAllWithStrategy(ios, "wait", __trace)
}
//# sourceMappingURL=raceAll.js.map
