"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.aggregateAsyncWithinEither = exports.aggregateAsyncWithinEither_ = void 0;
// ets_tracing: off
const CS = __importStar(require("../../../../Cause"));
const CK = __importStar(require("../../../../Collections/Immutable/Chunk"));
const Tp = __importStar(require("../../../../Collections/Immutable/Tuple"));
const T = __importStar(require("../../../../Effect"));
const E = __importStar(require("../../../../Either"));
const F = __importStar(require("../../../../Fiber"));
const Function_1 = require("../../../../Function");
const M = __importStar(require("../../../../Managed"));
const O = __importStar(require("../../../../Option"));
const Ref = __importStar(require("../../../../Ref"));
const SC = __importStar(require("../../../../Schedule"));
const CH = __importStar(require("../../Channel"));
const C = __importStar(require("../core"));
const HO = __importStar(require("../Handoff"));
const SER = __importStar(require("../SinkEndReason"));
const Chain = __importStar(require("./chain"));
const CrossRight = __importStar(require("./crossRight"));
const FromEffect = __importStar(require("./fromEffect"));
const Managed = __importStar(require("./managed"));
/**
 * Aggregates elements using the provided sink until it completes, or until the
 * delay signalled by the schedule has passed.
 *
 * This operator divides the stream into two asynchronous islands. Operators upstream
 * of this operator run on one fiber, while downstream operators run on another. Elements
 * will be aggregated by the sink until the downstream fiber pulls the aggregated value,
 * or until the schedule's delay has passed.
 *
 * Aggregated elements will be fed into the schedule to determine the delays between
 * pulls.
 */
function aggregateAsyncWithinEither_(self, sink, schedule) {
    const deps = T.tuple(HO.make(), Ref.makeRef(new SER.SinkEnd()), Ref.makeRef(CK.empty()), SC.driver(schedule));
    return Chain.chain_(FromEffect.fromEffect(deps), ({ tuple: [handoff, sinkEndReason, sinkLeftovers, scheduleDriver] }) => {
        const handoffProducer = CH.readWithCause((_in) => CH.zipRight_(CH.fromEffect(HO.offer(handoff, new HO.Emit(_in))), handoffProducer), (cause) => CH.fromEffect(HO.offer(handoff, new HO.Halt(cause))), (_) => CH.fromEffect(HO.offer(handoff, new HO.End(new SER.UpstreamEnd()))));
        const handoffConsumer = CH.unwrap(T.chain_(Ref.getAndSet_(sinkLeftovers, CK.empty()), (leftovers) => {
            if (CK.isEmpty(leftovers)) {
                return T.succeed(CH.zipRight_(CH.write(leftovers), handoffConsumer));
            }
            else {
                return T.map_(HO.take(handoff), (_) => {
                    switch (_._typeId) {
                        case HO.EmitTypeId:
                            return CH.zipRight_(CH.write(_.els), handoffConsumer);
                        case HO.HaltTypeId:
                            return CH.failCause(_.error);
                        case HO.EndTypeId:
                            return CH.fromEffect(Ref.set_(sinkEndReason, _.reason));
                    }
                });
            }
        }));
        const scheduledAggregator = (lastB) => {
            const timeout = T.foldCauseM_(scheduleDriver.next(lastB), (_) => E.fold_(CS.failureOrCause(_), (_) => HO.offer(handoff, new HO.End(new SER.ScheduleTimeout())), (cause) => HO.offer(handoff, new HO.Halt(cause))), (c) => HO.offer(handoff, new HO.End(new SER.ScheduleEnd(c))));
            return (CH.chain_(CH.managed_(T.forkManaged(timeout), (fiber) => {
                return CH.chain_(CH.doneCollect(handoffConsumer[">>>"](sink.channel)), ({ tuple: [leftovers, b] }) => {
                    return CH.zipRight_(CH.fromEffect(T.zipRight_(F.interrupt(fiber), Ref.set_(sinkLeftovers, CK.flatten(leftovers)))), CH.unwrap(Ref.modify_(sinkEndReason, (reason) => {
                        switch (reason._typeId) {
                            case SER.ScheduleEndTypeId:
                                return Tp.tuple(CH.as_(CH.write(CK.from([E.right(b), E.left(reason.c)])), O.some(b)), new SER.SinkEnd());
                            case SER.ScheduleTimeoutTypeId:
                                return Tp.tuple(CH.as_(CH.write(CK.single(E.right(b))), O.some(b)), new SER.SinkEnd());
                            case SER.SinkEndTypeId:
                                return Tp.tuple(CH.as_(CH.write(CK.single(E.right(b))), O.some(b)), new SER.SinkEnd());
                            case SER.UpstreamEndTypeId:
                                return Tp.tuple(CH.as_(CH.write(CK.single(E.right(b))), O.none), new SER.UpstreamEnd());
                        }
                    })));
                });
            }), (_) => {
                if (O.isNone(_)) {
                    return CH.unit;
                }
                else {
                    return scheduledAggregator(_);
                }
            }));
        };
        return CrossRight.crossRight_(Managed.managed((M.fork(CH.runManaged(self.channel[">>>"](handoffProducer))))), new C.Stream(scheduledAggregator(O.none)));
    });
}
exports.aggregateAsyncWithinEither_ = aggregateAsyncWithinEither_;
/**
 * Aggregates elements using the provided sink until it completes, or until the
 * delay signalled by the schedule has passed.
 *
 * This operator divides the stream into two asynchronous islands. Operators upstream
 * of this operator run on one fiber, while downstream operators run on another. Elements
 * will be aggregated by the sink until the downstream fiber pulls the aggregated value,
 * or until the schedule's delay has passed.
 *
 * Aggregated elements will be fed into the schedule to determine the delays between
 * pulls.
 *
 * @ets_data_first aggregateAsyncWithinEither_
 */
function aggregateAsyncWithinEither(sink, schedule) {
    return (self) => aggregateAsyncWithinEither_(self, sink, schedule);
}
exports.aggregateAsyncWithinEither = aggregateAsyncWithinEither;
//# sourceMappingURL=aggregateAsyncWithinEither.js.map