"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.unsafeMakeUnbounded = exports.makeUnbounded = exports.unsafeMakeSliding = exports.makeSliding = exports.unsafeMakeDropping = exports.makeDropping = exports.unsafeMakeBounded = exports.makeBounded = exports.toQueue = exports.mapM = exports.mapM_ = exports.map = exports.map_ = exports.filterOutputM = exports.filterOutputM_ = exports.filterOutput = exports.filterOutput_ = exports.filterInputM = exports.filterInputM_ = exports.filterInput = exports.filterInput_ = exports.dimapM = exports.dimapM_ = exports.dimap = exports.dimap_ = exports.contramapM = exports.contramapM_ = exports.subscribe = exports.size = exports.shutdown = exports.publishAll = exports.publishAll_ = exports.publish = exports.publish_ = exports.isShutdown = exports.capacity = exports.awaitShutdown = exports.concrete = exports.XHubInternal = exports.HubTypeId = void 0;
// ets_tracing: off
require("../Operator");
const AR = __importStar(require("../Collections/Immutable/Array"));
const Chunk = __importStar(require("../Collections/Immutable/Chunk"));
const HS = __importStar(require("../Collections/Mutable/HashSet"));
const T = __importStar(require("../Effect"));
const ES = __importStar(require("../Effect/ExecutionStrategy"));
const Ex = __importStar(require("../Exit"));
const F = __importStar(require("../Fiber"));
const Function_1 = require("../Function");
const M = __importStar(require("../Managed"));
const RM = __importStar(require("../Managed/ReleaseMap"));
const P = __importStar(require("../Promise"));
const Q = __importStar(require("../Queue"));
const Queue_1 = require("../Queue");
const Ref = __importStar(require("../Ref"));
const AB = __importStar(require("../Support/AtomicBoolean"));
const MQ = __importStar(require("../Support/MutableQueue"));
const HP = __importStar(require("./_internal/HashedPair"));
const HF = __importStar(require("./_internal/hubFactory"));
const U = __importStar(require("./_internal/unsafe"));
const PR = __importStar(require("./primitives"));
const S = __importStar(require("./Strategy"));
exports.HubTypeId = Symbol();
class XHubInternal {
    constructor() {
        this.typeId = exports.HubTypeId;
    }
}
exports.XHubInternal = XHubInternal;
PR._RA, PR._RB, PR._EA, PR._EB, PR._A, PR._B;
/**
 * @ets_optimize remove
 */
function concrete(_) {
    //
}
exports.concrete = concrete;
/**
 * Waits for the hub to be shut down.
 */
function awaitShutdown(self) {
    ;
    return self.awaitShutdown;
}
exports.awaitShutdown = awaitShutdown;
/**
 * The maximum capacity of the hub.
 */
function capacity(self) {
    ;
    return self.capacity;
}
exports.capacity = capacity;
/**
 * Checks whether the hub is shut down.
 */
function isShutdown(self) {
    ;
    return self.isShutdown;
}
exports.isShutdown = isShutdown;
/**
 * Publishes a message to the hub, returning whether the message was
 * published to the hub.
 */
function publish_(self, a) {
    ;
    return self.publish(a);
}
exports.publish_ = publish_;
/**
 * Publishes a message to the hub, returning whether the message was
 * published to the hub.
 *
 * @ets_data_first publish_
 */
function publish(a) {
    return (self) => publish_(self, a);
}
exports.publish = publish;
/**
 * Publishes all of the specified messages to the hub, returning whether
 * they were published to the hub.
 */
function publishAll_(self, as) {
    ;
    return self.publishAll(as);
}
exports.publishAll_ = publishAll_;
/**
 * Publishes all of the specified messages to the hub, returning whether
 * they were published to the hub.
 *
 * @ets_data_first publishAll_
 */
function publishAll(as) {
    return (self) => publishAll_(self, as);
}
exports.publishAll = publishAll;
/**
 * Shuts down the hub.
 */
function shutdown(self) {
    ;
    return self.shutdown;
}
exports.shutdown = shutdown;
/**
 * The current number of messages in the hub.
 */
function size(self) {
    ;
    return self.size;
}
exports.size = size;
/**
 * Subscribes to receive messages from the hub. The resulting subscription
 * can be evaluated multiple times within the scope of the managed to take a
 * message from the hub each time.
 */
function subscribe(self) {
    ;
    return self.subscribe;
}
exports.subscribe = subscribe;
/**
 * Transforms messages published to the hub using the specified effectual
 * function.
 */
function contramapM_(self, f) {
    return dimapM_(self, f, T.succeed);
}
exports.contramapM_ = contramapM_;
/**
 * Transforms messages published to the hub using the specified effectual
 * function.
 *
 * @ets_data_first contramapM_
 */
function contramapM(f) {
    return (self) => contramapM_(self, f);
}
exports.contramapM = contramapM;
/**
 * Transforms messages published to and taken from the hub using the
 * specified functions.
 */
function dimap_(self, f, g) {
    return dimapM_(self, (c) => T.succeed(f(c)), (b) => T.succeed(g(b)));
}
exports.dimap_ = dimap_;
/**
 * Transforms messages published to and taken from the hub using the
 * specified functions.
 *
 * @ets_data_first dimap_
 */
function dimap(f, g) {
    return (self) => dimap_(self, f, g);
}
exports.dimap = dimap;
class DimapMImplementation extends XHubInternal {
    constructor(source, f, g) {
        super();
        this.source = source;
        this.f = f;
        this.awaitShutdown = source.awaitShutdown;
        this.capacity = source.capacity;
        this.isShutdown = source.isShutdown;
        this.shutdown = source.shutdown;
        this.size = source.size;
        this.subscribe = M.map_(source.subscribe, Q.mapM(g));
    }
    publish(c) {
        return T.chain_(this.f(c), (a) => this.source.publish(a));
    }
    publishAll(cs) {
        return T.chain_(T.forEach_(cs, this.f), (as) => this.source.publishAll(as));
    }
}
/**
 * Transforms messages published to and taken from the hub using the
 * specified effectual functions.
 */
function dimapM_(self, f, g) {
    ;
    return new DimapMImplementation(self, f, g);
}
exports.dimapM_ = dimapM_;
/**
 * Transforms messages published to and taken from the hub using the
 * specified effectual functions.
 *
 * @ets_data_first dimapM_
 */
function dimapM(f, g) {
    return (self) => dimapM_(self, f, g);
}
exports.dimapM = dimapM;
class filterInputMImplementation extends XHubInternal {
    constructor(source, f) {
        super();
        this.source = source;
        this.f = f;
        this.awaitShutdown = source.awaitShutdown;
        this.capacity = source.capacity;
        this.isShutdown = source.isShutdown;
        this.shutdown = source.shutdown;
        this.size = source.size;
        this.subscribe = source.subscribe;
    }
    publish(a) {
        return T.chain_(this.f(a), (b) => (b ? this.source.publish(a) : T.succeed(false)));
    }
    publishAll(as) {
        return T.chain_(T.filter_(as, this.f), (as) => AR.isNonEmpty(as) ? this.source.publishAll(as) : T.succeed(false));
    }
}
/**
 * Filters messages published to the hub using the specified function.
 */
function filterInput_(self, f) {
    return filterInputM_(self, (a) => T.succeed(f(a)));
}
exports.filterInput_ = filterInput_;
/**
 * Filters messages published to the hub using the specified function.
 *
 * @ets_data_first filterInput_
 */
function filterInput(f) {
    return (self) => filterInput_(self, f);
}
exports.filterInput = filterInput;
/**
 * Filters messages published to the hub using the specified effectual
 * function.
 */
function filterInputM_(self, f) {
    ;
    return new filterInputMImplementation(self, f);
}
exports.filterInputM_ = filterInputM_;
/**
 * Filters messages published to the hub using the specified effectual
 * function.
 *
 * @ets_data_first filterInputM_
 */
function filterInputM(f) {
    return (self) => filterInputM_(self, f);
}
exports.filterInputM = filterInputM;
/**
 * Filters messages taken from the hub using the specified function.
 */
function filterOutput_(self, f) {
    return filterOutputM_(self, (b) => T.succeed(f(b)));
}
exports.filterOutput_ = filterOutput_;
/**
 * Filters messages taken from the hub using the specified function.
 *
 * @ets_data_first filterOutput_
 */
function filterOutput(f) {
    return (self) => filterOutput_(self, f);
}
exports.filterOutput = filterOutput;
class filterOutputMImplementation extends XHubInternal {
    constructor(source, f) {
        super();
        this.source = source;
        this.f = f;
        this.awaitShutdown = source.awaitShutdown;
        this.capacity = source.capacity;
        this.isShutdown = source.isShutdown;
        this.shutdown = source.shutdown;
        this.size = source.size;
        this.subscribe = M.map_(source.subscribe, Q.filterOutputM(f));
    }
    publish(a) {
        return this.source.publish(a);
    }
    publishAll(as) {
        return this.source.publishAll(as);
    }
}
/**
 * Filters messages taken from the hub using the specified effectual
 * function.
 */
function filterOutputM_(self, f) {
    ;
    return new filterOutputMImplementation(self, f);
}
exports.filterOutputM_ = filterOutputM_;
/**
 * Filters messages taken from the hub using the specified effectual
 * function.
 *
 * @ets_data_first filterOutputM_
 */
function filterOutputM(f) {
    return (self) => filterOutputM_(self, f);
}
exports.filterOutputM = filterOutputM;
/**
 * Transforms messages taken from the hub using the specified function.
 */
function map_(self, f) {
    return mapM_(self, (b) => T.succeed(f(b)));
}
exports.map_ = map_;
/**
 * Transforms messages taken from the hub using the specified function.
 *
 * @ets_data_first map_
 */
function map(f) {
    return (self) => map_(self, f);
}
exports.map = map;
/**
 * Transforms messages taken from the hub using the specified effectual
 * function.
 */
function mapM_(self, f) {
    return dimapM_(self, (a) => T.succeed(a), f);
}
exports.mapM_ = mapM_;
/**
 * Transforms messages taken from the hub using the specified effectual
 * function.
 *
 * @ets_data_first mapM_
 */
function mapM(f) {
    return (self) => mapM_(self, f);
}
exports.mapM = mapM;
class ToQueueImplementation extends Queue_1.XQueueInternal {
    constructor(source) {
        super();
        this.source = source;
        this.awaitShutdown = source.awaitShutdown;
        this.capacity = source.capacity;
        this.isShutdown = source.isShutdown;
        this.shutdown = source.shutdown;
        this.size = source.size;
        this.take = T.never;
        this.takeAll = T.succeed(Chunk.empty());
    }
    offer(a) {
        return this.source.publish(a);
    }
    offerAll(as) {
        return this.source.publishAll(as);
    }
    takeUpTo() {
        return T.succeed(Chunk.empty());
    }
}
/**
 * Views the hub as a queue that can only be written to.
 */
function toQueue(self) {
    ;
    return new ToQueueImplementation(self);
}
exports.toQueue = toQueue;
/**
 * Creates a bounded hub with the back pressure strategy. The hub will retain
 * messages until they have been taken by all subscribers, applying back
 * pressure to publishers if the hub is at capacity.
 *
 * For best performance use capacities that are powers of two.
 */
function makeBounded(requestedCapacity) {
    return T.chain_(T.succeedWith(() => HF.makeBounded(requestedCapacity)), (_) => makeHub(_, new S.BackPressure()));
}
exports.makeBounded = makeBounded;
/**
 * Creates a bounded hub with the back pressure strategy. The hub will retain
 * messages until they have been taken by all subscribers, applying back
 * pressure to publishers if the hub is at capacity.
 *
 * For best performance use capacities that are powers of two.
 */
function unsafeMakeBounded(requestedCapacity) {
    const releaseMap = new RM.ReleaseMap(Ref.unsafeMakeRef(new RM.Running(0, new Map())));
    return unsafeMakeHub(HF.makeBounded(requestedCapacity), makeSubscribersHashSet(), releaseMap, P.unsafeMake(F.None), new AB.AtomicBoolean(false), new S.BackPressure());
}
exports.unsafeMakeBounded = unsafeMakeBounded;
/**
 * Creates a bounded hub with the dropping strategy. The hub will drop new
 * messages if the hub is at capacity.
 *
 * For best performance use capacities that are powers of two.
 */
function makeDropping(requestedCapacity) {
    return T.chain_(T.succeedWith(() => {
        return HF.makeBounded(requestedCapacity);
    }), (_) => makeHub(_, new S.Dropping()));
}
exports.makeDropping = makeDropping;
/**
 * Creates a bounded hub with the dropping strategy. The hub will drop new
 * messages if the hub is at capacity.
 *
 * For best performance use capacities that are powers of two.
 */
function unsafeMakeDropping(requestedCapacity) {
    const releaseMap = new RM.ReleaseMap(Ref.unsafeMakeRef(new RM.Running(0, new Map())));
    return unsafeMakeHub(HF.makeBounded(requestedCapacity), makeSubscribersHashSet(), releaseMap, P.unsafeMake(F.None), new AB.AtomicBoolean(false), new S.Dropping());
}
exports.unsafeMakeDropping = unsafeMakeDropping;
/**
 * Creates a bounded hub with the sliding strategy. The hub will add new
 * messages and drop old messages if the hub is at capacity.
 *
 * For best performance use capacities that are powers of two.
 */
function makeSliding(requestedCapacity) {
    return T.chain_(T.succeedWith(() => {
        return HF.makeBounded(requestedCapacity);
    }), (_) => makeHub(_, new S.Sliding()));
}
exports.makeSliding = makeSliding;
/**
 * Creates a bounded hub with the sliding strategy. The hub will add new
 * messages and drop old messages if the hub is at capacity.
 *
 * For best performance use capacities that are powers of two.
 */
function unsafeMakeSliding(requestedCapacity) {
    const releaseMap = new RM.ReleaseMap(Ref.unsafeMakeRef(new RM.Running(0, new Map())));
    return unsafeMakeHub(HF.makeBounded(requestedCapacity), makeSubscribersHashSet(), releaseMap, P.unsafeMake(F.None), new AB.AtomicBoolean(false), new S.Sliding());
}
exports.unsafeMakeSliding = unsafeMakeSliding;
/**
 * Creates an unbounded hub.
 */
function makeUnbounded() {
    return T.chain_(T.succeedWith(() => {
        return HF.makeUnbounded();
    }), (_) => makeHub(_, new S.Dropping()));
}
exports.makeUnbounded = makeUnbounded;
/**
 * Creates an unbounded hub.
 */
function unsafeMakeUnbounded() {
    const releaseMap = new RM.ReleaseMap(Ref.unsafeMakeRef(new RM.Running(0, new Map())));
    return unsafeMakeHub(HF.makeUnbounded(), makeSubscribersHashSet(), releaseMap, P.unsafeMake(F.None), new AB.AtomicBoolean(false), new S.Dropping());
}
exports.unsafeMakeUnbounded = unsafeMakeUnbounded;
class UnsafeMakeHubImplementation extends XHubInternal {
    constructor(hub, subscribers, releaseMap, shutdownHook, shutdownFlag, strategy) {
        super();
        this.hub = hub;
        this.subscribers = subscribers;
        this.shutdownFlag = shutdownFlag;
        this.strategy = strategy;
        this.awaitShutdown = P.await(shutdownHook);
        this.capacity = hub.capacity;
        this.isShutdown = T.succeedWith(() => shutdownFlag.get);
        this.shutdown = T.uninterruptible(T.suspend((_, fiberId) => {
            shutdownFlag.set(true);
            return T.whenM_(T.zipRight_(RM.releaseAll(Ex.interrupt(fiberId), ES.parallel)(releaseMap), strategy.shutdown), P.succeed_(shutdownHook, undefined));
        }));
        this.size = T.suspend(() => {
            if (shutdownFlag.get) {
                return T.interrupt;
            }
            return T.succeed(hub.size());
        });
        this.subscribe = (M.map_(M.tap_(M.bind_(M.do, "dequeue", () => T.toManaged(makeSubscription(hub, subscribers, strategy))), ({ dequeue }) => M.makeExit_(RM.add((_) => Q.shutdown(dequeue))(releaseMap), (finalizer, exit) => finalizer(exit))), ({ dequeue }) => dequeue));
    }
    publish(a) {
        return T.suspend(() => {
            if (this.shutdownFlag.get) {
                return T.interrupt;
            }
            if (this.hub.publish(a)) {
                this.strategy.unsafeCompleteSubscribers(this.hub, this.subscribers);
                return T.succeed(true);
            }
            return this.strategy.handleSurplus(this.hub, this.subscribers, AR.single(a), this.shutdownFlag);
        });
    }
    publishAll(as) {
        return T.suspend(() => {
            if (this.shutdownFlag.get) {
                return T.interrupt;
            }
            const surplus = U.unsafePublishAll(this.hub, as);
            this.strategy.unsafeCompleteSubscribers(this.hub, this.subscribers);
            if (Chunk.isEmpty(surplus)) {
                return T.succeed(true);
            }
            return this.strategy.handleSurplus(this.hub, this.subscribers, surplus, this.shutdownFlag);
        });
    }
}
function makeHub(hub, strategy) {
    return T.chain_(RM.makeReleaseMap, (releaseMap) => {
        return T.map_(P.make(), (promise) => {
            return unsafeMakeHub(hub, makeSubscribersHashSet(), releaseMap, promise, new AB.AtomicBoolean(false), strategy);
        });
    });
}
/**
 * Unsafely creates a hub with the specified strategy.
 */
function unsafeMakeHub(hub, subscribers, releaseMap, shutdownHook, shutdownFlag, strategy) {
    return new UnsafeMakeHubImplementation(hub, subscribers, releaseMap, shutdownHook, shutdownFlag, strategy);
}
/**
 * Creates a subscription with the specified strategy.
 */
function makeSubscription(hub, subscribers, strategy) {
    return T.map_(P.make(), (promise) => {
        return unsafeMakeSubscription(hub, subscribers, hub.subscribe(), new MQ.Unbounded(), promise, new AB.AtomicBoolean(false), strategy);
    });
}
class UnsafeMakeSubscriptionImplementation extends Queue_1.XQueueInternal {
    constructor(hub, subscribers, subscription, pollers, shutdownHook, shutdownFlag, strategy) {
        super();
        this.hub = hub;
        this.subscribers = subscribers;
        this.subscription = subscription;
        this.pollers = pollers;
        this.shutdownHook = shutdownHook;
        this.shutdownFlag = shutdownFlag;
        this.strategy = strategy;
        this.awaitShutdown = P.await(this.shutdownHook);
        this.capacity = this.hub.capacity;
        this.isShutdown = T.succeedWith(() => this.shutdownFlag.get);
        this.shutdown = T.uninterruptible(T.suspend((_, fiberId) => {
            this.shutdownFlag.set(true);
            return T.whenM_(T.zipRight_(T.forEachPar_(U.unsafePollAllQueue(this.pollers), (_) => {
                return P.interruptAs(fiberId)(_);
            }), T.succeedWith(() => this.subscription.unsubscribe())), P.succeed_(this.shutdownHook, undefined));
        }));
        this.size = T.suspend(() => {
            if (this.shutdownFlag.get) {
                return T.interrupt;
            }
            return T.succeed(this.subscription.size());
        });
        this.take = T.suspend((_, fiberId) => {
            if (this.shutdownFlag.get) {
                return T.interrupt;
            }
            const empty = null;
            const message = this.pollers.isEmpty ? this.subscription.poll(empty) : empty;
            if (message === null) {
                const promise = P.unsafeMake(fiberId);
                return T.onInterrupt_(T.suspend(() => {
                    this.pollers.offer(promise);
                    this.subscribers.add(new HP.HashedPair(this.subscription, this.pollers));
                    this.strategy.unsafeCompletePollers(this.hub, this.subscribers, this.subscription, this.pollers);
                    if (this.shutdownFlag.get) {
                        return T.interrupt;
                    }
                    else {
                        return P.await(promise);
                    }
                }), () => T.succeedWith(() => {
                    U.unsafeRemove(this.pollers, promise);
                }));
            }
            else {
                this.strategy.unsafeOnHubEmptySpace(this.hub, this.subscribers);
                return T.succeed(message);
            }
        });
        this.takeAll = T.suspend(() => {
            if (this.shutdownFlag.get) {
                return T.interrupt;
            }
            const as = this.pollers.isEmpty
                ? U.unsafePollAllSubscription(this.subscription)
                : Chunk.empty();
            this.strategy.unsafeOnHubEmptySpace(this.hub, this.subscribers);
            return T.succeed(as);
        });
    }
    offer(_) {
        return T.succeed(false);
    }
    offerAll(_) {
        return T.succeed(false);
    }
    takeUpTo(n) {
        return T.suspend(() => {
            if (this.shutdownFlag.get) {
                return T.interrupt;
            }
            const as = this.pollers.isEmpty
                ? U.unsafePollN(this.subscription, n)
                : Chunk.empty();
            this.strategy.unsafeOnHubEmptySpace(this.hub, this.subscribers);
            return T.succeed(as);
        });
    }
}
/**
 * Unsafely creates a subscription with the specified strategy.
 */
function unsafeMakeSubscription(hub, subscribers, subscription, pollers, shutdownHook, shutdownFlag, strategy) {
    return new UnsafeMakeSubscriptionImplementation(hub, subscribers, subscription, pollers, shutdownHook, shutdownFlag, strategy);
}
function makeSubscribersHashSet() {
    return HS.make();
}
//# sourceMappingURL=core.js.map