"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.groupBy = exports.groupBy_ = void 0
// tracing: off
const MP = __importStar(require("../../Collections/Immutable/Map"))
const Tp = __importStar(require("../../Collections/Immutable/Tuple"))
const Ex = __importStar(require("../../Exit"))
const Function_1 = require("../../Function")
const O = __importStar(require("../../Option"))
const P = __importStar(require("../../Promise"))
const Q = __importStar(require("../../Queue"))
const T = __importStar(require("../_internal/effect"))
const M = __importStar(require("../_internal/managed"))
const Ref = __importStar(require("../_internal/ref"))
const GB = __importStar(require("../GroupBy"))
const distributedWithDynamic_1 = require("./distributedWithDynamic")
const flattenExitOption_1 = require("./flattenExitOption")
const fromQueueWithShutdown_1 = require("./fromQueueWithShutdown")
const mapM_1 = require("./mapM")
const unwrapManaged_1 = require("./unwrapManaged")
/**
 * More powerful version of `Stream.groupByKey`
 */
function groupBy_(self, f, buffer = 16) {
  const qstream = (0, unwrapManaged_1.unwrapManaged)(
    M.map_(
      M.tap_(
        M.bind_(
          M.bind_(
            M.bind_(
              M.bind_(M.do, "decider", () => T.toManaged(P.make())),
              "out",
              () => T.toManagedRelease_(Q.makeBounded(buffer), Q.shutdown)
            ),
            "ref",
            () => T.toManaged(Ref.makeRef(MP.empty))
          ),
          "add",
          ({ decider, out }) =>
            (0, distributedWithDynamic_1.distributedWithDynamic)(
              buffer,
              (kv) => T.chain_(P.await(decider), (_) => _(...kv.tuple)),
              (x) => Q.offer_(out, x)
            )((0, mapM_1.mapM_)(self, f))
        ),
        ({ add, decider, out, ref }) =>
          T.toManaged(
            P.succeed_(decider, (k, _) =>
              T.chain_(
                T.map_(ref.get, MP.lookup(k)),
                O.fold(
                  () =>
                    T.chain_(add, ({ tuple: [idx, q] }) =>
                      T.as_(
                        T.zipRight_(
                          Ref.update_(ref, MP.insert(k, idx)),
                          Q.offer_(
                            out,
                            Ex.succeed(
                              Tp.tuple(
                                k,
                                Q.map_(q, (ex) => Ex.map_(ex, (_) => _.get(1)))
                              )
                            )
                          )
                        ),
                        (_) => _ === idx
                      )
                    ),
                  (idx) => T.succeed((_) => _ === idx)
                )
              )
            )
          )
      ),
      ({ out }) =>
        (0, flattenExitOption_1.flattenExitOption)(
          (0, fromQueueWithShutdown_1.fromQueueWithShutdown)(out)
        )
    )
  )
  return new GB.GroupBy(qstream, buffer)
}
exports.groupBy_ = groupBy_
/**
 * More powerful version of `Stream.groupByKey`
 */
function groupBy(f, buffer = 16) {
  return (self) => groupBy_(self, f, buffer)
}
exports.groupBy = groupBy
//# sourceMappingURL=groupBy.js.map
