"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.poll = exports.mapM_ = exports.mapM = exports.map = exports.map_ = exports.filterInput_ = exports.filterInput = exports.filterOutput = exports.filterOutput_ = exports.filterOutputM = exports.filterOutputM_ = exports.filterInputM_ = exports.filterInputM = exports.contramap = exports.contramap_ = exports.contramapM = exports.contramapM_ = exports.dimapM_ = exports.dimapM = exports.dimap_ = exports.dimap = exports.both_ = exports.both = exports.bothWith_ = exports.bothWith = exports.bothWithM_ = exports.bothWithM = exports.takeBetween_ = exports.takeBetween = exports.makeDropping = exports.makeUnbounded = exports.makeSliding = exports.BackPressureStrategy = exports.unsafeCreate = exports.makeBounded = exports.createQueue = void 0;
// ets_tracing: off
const Chunk = __importStar(require("../Collections/Immutable/Chunk"));
const core_1 = require("../Effect/core");
const exclForEach = __importStar(require("../Effect/excl-forEach"));
const excl_forEach_1 = require("../Effect/excl-forEach");
Object.defineProperty(exports, "BackPressureStrategy", { enumerable: true, get: function () { return excl_forEach_1.BackPressureStrategy; } });
Object.defineProperty(exports, "createQueue", { enumerable: true, get: function () { return excl_forEach_1.createQueue; } });
Object.defineProperty(exports, "makeBounded", { enumerable: true, get: function () { return excl_forEach_1.makeBoundedQueue; } });
Object.defineProperty(exports, "unsafeCreate", { enumerable: true, get: function () { return excl_forEach_1.unsafeCreateQueue; } });
const Function_1 = require("../Function");
const O = __importStar(require("../Option"));
const MutableQueue_1 = require("../Support/MutableQueue");
const core_2 = require("./core");
const T = __importStar(require("./effect-api"));
const xqueue_1 = require("./xqueue");
/**
 * Creates a sliding queue
 */
function makeSliding(capacity) {
    return T.chain_(T.succeedWith(() => new MutableQueue_1.Bounded(capacity)), exclForEach.createQueue(new core_2.SlidingStrategy()));
}
exports.makeSliding = makeSliding;
/**
 * Creates a unbouded queue
 */
function makeUnbounded() {
    return T.chain_(T.succeedWith(() => new MutableQueue_1.Unbounded()), exclForEach.createQueue(new core_2.DroppingStrategy()));
}
exports.makeUnbounded = makeUnbounded;
/**
 * Creates a dropping queue
 */
function makeDropping(capacity) {
    return T.chain_(T.succeedWith(() => new MutableQueue_1.Bounded(capacity)), exclForEach.createQueue(new core_2.DroppingStrategy()));
}
exports.makeDropping = makeDropping;
function takeRemainderLoop(self, n) {
    ;
    if (n <= 0) {
        return T.succeed(Chunk.empty());
    }
    else {
        return T.chain_(self.take, (a) => T.map_(takeRemainderLoop(self, n - 1), (_) => Chunk.append_(_, a)));
    }
}
/**
 * Takes between min and max number of values from the queue. If there
 * is less than min items available, it'll block until the items are
 * collected.
 *
 * @ets_data_first takeBetween_
 */
function takeBetween(min, max) {
    return (self) => takeBetween_(self, min, max);
}
exports.takeBetween = takeBetween;
/**
 * Takes between min and max number of values from the queue. If there
 * is less than min items available, it'll block until the items are
 * collected.
 */
function takeBetween_(self, min, max) {
    ;
    if (max < min) {
        return T.succeed(Chunk.empty());
    }
    else {
        return (T.chain_(self.takeUpTo(max), (bs) => {
            const remaining = min - Chunk.size(bs);
            if (remaining === 1) {
                return T.map_(self.take, (b) => Chunk.append_(bs, b));
            }
            else if (remaining > 1) {
                return T.map_(takeRemainderLoop(self, remaining), (list) => Chunk.concat_(bs, list));
            }
            else {
                return T.succeed(bs);
            }
        }));
    }
}
exports.takeBetween_ = takeBetween_;
/**
 * Creates a new queue from this queue and another. Offering to the composite queue
 * will broadcast the elements to both queues; taking from the composite queue
 * will dequeue elements from both queues and apply the function point-wise.
 *
 * Note that using queues with different strategies may result in surprising behavior.
 * For example, a dropping queue and a bounded queue composed together may apply `f`
 * to different elements.
 *
 * @ets_data_first bothWithM_
 */
function bothWithM(that, f) {
    return (self) => bothWithM_(self, that, f);
}
exports.bothWithM = bothWithM;
/**
 * Creates a new queue from this queue and another. Offering to the composite queue
 * will broadcast the elements to both queues; taking from the composite queue
 * will dequeue elements from both queues and apply the function point-wise.
 *
 * Note that using queues with different strategies may result in surprising behavior.
 * For example, a dropping queue and a bounded queue composed together may apply `f`
 * to different elements.
 */
function bothWithM_(self, that, f) {
    ;
    ;
    return new BothWithM(self, that, f);
}
exports.bothWithM_ = bothWithM_;
class BothWithM extends xqueue_1.XQueueInternal {
    constructor(self, that, f) {
        super();
        this.self = self;
        this.that = that;
        this.f = f;
        this.awaitShutdown = T.chain_(this.self.awaitShutdown, () => this.that.awaitShutdown);
        this.capacity = Math.min(this.self.capacity, this.that.capacity);
        this.isShutdown = this.self.isShutdown;
        this.shutdown = T.zipWithPar_(this.self.shutdown, this.that.shutdown, () => undefined);
        this.size = T.zipWithPar_(this.self.size, this.that.size, (x, y) => Math.max(x, y));
        this.take = T.chain_(T.zipPar_(this.self.take, this.that.take), ({ tuple: [b, c] }) => this.f(b, c));
        this.takeAll = T.chain_(T.zipPar_(this.self.takeAll, this.that.takeAll), ({ tuple: [bs, cs] }) => Chunk.mapM_(Chunk.zip_(bs, cs), ({ tuple: [b, c] }) => this.f(b, c)));
    }
    offer(a) {
        return T.zipWithPar_(this.self.offer(a), this.that.offer(a), (x, y) => x && y);
    }
    offerAll(as) {
        return T.zipWithPar_(this.self.offerAll(as), this.that.offerAll(as), (x, y) => x && y);
    }
    takeUpTo(max) {
        return T.chain_(T.zipPar_(this.self.takeUpTo(max), this.that.takeUpTo(max)), ({ tuple: [bs, cs] }) => Chunk.mapM_(Chunk.zip_(bs, cs), ({ tuple: [b, c] }) => this.f(b, c)));
    }
}
/**
 * Like `bothWithM`, but uses a pure function.
 *
 * @ets_data_first bothWith_
 */
function bothWith(that, f) {
    return (self) => bothWithM_(self, that, (b, c) => T.succeed(f(b, c)));
}
exports.bothWith = bothWith;
/**
 * Like `bothWithM`, but uses a pure function.
 */
function bothWith_(self, that, f) {
    return bothWithM_(self, that, (b, c) => T.succeed(f(b, c)));
}
exports.bothWith_ = bothWith_;
/**
 * Like `bothWith`, but tuples the elements instead of applying a function.
 *
 * @ets_data_first both_
 */
function both(that) {
    return (self) => bothWith_(self, that, (b, c) => (0, Function_1.tuple)(b, c));
}
exports.both = both;
/**
 * Like `bothWith`, but tuples the elements instead of applying a function.
 */
function both_(self, that) {
    return bothWith_(self, that, (b, c) => (0, Function_1.tuple)(b, c));
}
exports.both_ = both_;
/**
 * Transforms elements enqueued into and dequeued from this queue with the
 * specified effectual functions.
 *
 * @ets_data_first dimap_
 */
function dimap(f, g) {
    return (self) => dimap_(self, f, g);
}
exports.dimap = dimap;
/**
 * Transforms elements enqueued into and dequeued from this queue with the
 * specified effectual functions.
 */
function dimap_(self, f, g) {
    return dimapM_(self, (c) => (0, core_1.succeed)(f(c)), (b) => (0, core_1.succeed)(g(b)));
}
exports.dimap_ = dimap_;
/**
 * Transforms elements enqueued into and dequeued from this queue with the
 * specified effectual functions.
 *
 * @ets_data_first dimapM_
 */
function dimapM(f, g) {
    return (self) => dimapM_(self, f, g);
}
exports.dimapM = dimapM;
/**
 * Transforms elements enqueued into and dequeued from this queue with the
 * specified effectual functions.
 */
function dimapM_(self, f, g) {
    ;
    return new DimapM(self, f, g);
}
exports.dimapM_ = dimapM_;
class DimapM extends xqueue_1.XQueueInternal {
    constructor(self, f, g) {
        super();
        this.self = self;
        this.f = f;
        this.g = g;
        this.awaitShutdown = this.self.awaitShutdown;
        this.capacity = this.self.capacity;
        this.isShutdown = this.self.isShutdown;
        this.shutdown = this.self.shutdown;
        this.size = this.self.size;
        this.take = T.chain_(this.self.take, this.g);
        this.takeAll = T.chain_(this.self.takeAll, (a) => Chunk.mapM_(a, this.g));
    }
    offer(a) {
        return T.chain_(this.f(a), (a) => this.self.offer(a));
    }
    offerAll(as) {
        return T.chain_(T.forEach_(as, this.f), (as) => this.self.offerAll(as));
    }
    takeUpTo(n) {
        return T.chain_(this.self.takeUpTo(n), (bs) => Chunk.mapM_(bs, this.g));
    }
}
/**
 * Transforms elements enqueued into this queue with an effectful function.
 */
function contramapM_(self, f) {
    return dimapM_(self, f, core_1.succeed);
}
exports.contramapM_ = contramapM_;
/**
 * Transforms elements enqueued into this queue with an effectful function.
 *
 * @ets_data_first contramapM_
 */
function contramapM(f) {
    return (self) => contramapM_(self, f);
}
exports.contramapM = contramapM;
/**
 * Transforms elements enqueued into this queue with a pure function.
 */
function contramap_(self, f) {
    return dimapM_(self, (c) => (0, core_1.succeed)(f(c)), core_1.succeed);
}
exports.contramap_ = contramap_;
/**
 * Transforms elements enqueued into this queue with a pure function.
 *
 * @ets_data_first contramap_
 */
function contramap(f) {
    return (self) => contramap_(self, f);
}
exports.contramap = contramap;
/**
 * Like `filterInput`, but uses an effectful function to filter the elements.
 *
 * @ets_data_first filterInputM_
 */
function filterInputM(f) {
    return (self) => filterInputM_(self, f);
}
exports.filterInputM = filterInputM;
/**
 * Like `filterInput`, but uses an effectful function to filter the elements.
 */
function filterInputM_(self, f) {
    ;
    return new FilterInputM(self, f);
}
exports.filterInputM_ = filterInputM_;
class FilterInputM extends xqueue_1.XQueueInternal {
    constructor(self, f) {
        super();
        this.self = self;
        this.f = f;
        this.awaitShutdown = this.self.awaitShutdown;
        this.capacity = this.self.capacity;
        this.isShutdown = this.self.isShutdown;
        this.shutdown = this.self.shutdown;
        this.size = this.self.size;
        this.take = this.self.take;
        this.takeAll = this.self.takeAll;
    }
    offer(a) {
        return T.chain_(this.f(a), (b) => (b ? this.self.offer(a) : T.succeed(false)));
    }
    offerAll(as) {
        return (T.chain_(T.forEach_(as, (a) => (T.map_(this.f(a), (b) => (b ? O.some(a) : O.none)))), (maybeAs) => {
            const filtered = Chunk.filterMap_(maybeAs, Function_1.identity);
            if (Chunk.isEmpty(filtered)) {
                return T.succeed(false);
            }
            else {
                return this.self.offerAll(filtered);
            }
        }));
    }
    takeUpTo(n) {
        return this.self.takeUpTo(n);
    }
}
/**
 * Filters elements dequeued from the queue using the specified effectual
 * predicate.
 */
function filterOutputM_(self, f) {
    ;
    return new FilterOutputM(self, f);
}
exports.filterOutputM_ = filterOutputM_;
class FilterOutputM extends xqueue_1.XQueueInternal {
    constructor(self, f) {
        super();
        this.self = self;
        this.f = f;
        this.awaitShutdown = this.self.awaitShutdown;
        this.capacity = this.self.capacity;
        this.isShutdown = this.self.isShutdown;
        this.shutdown = this.self.shutdown;
        this.size = this.self.size;
        this.take = T.chain_(this.self.take, (b) => {
            return T.chain_(this.f(b), (p) => {
                return p ? T.succeed(b) : this.take;
            });
        });
        this.takeAll = T.chain_(this.self.takeAll, (bs) => Chunk.filterM_(bs, this.f));
    }
    offer(a) {
        return this.self.offer(a);
    }
    offerAll(as) {
        return this.self.offerAll(as);
    }
    loop(max, acc) {
        return T.chain_(this.self.takeUpTo(max), (bs) => {
            if (Chunk.isEmpty(bs)) {
                return T.succeed(acc);
            }
            return T.chain_(Chunk.filterM_(bs, this.f), (filtered) => {
                const length = Chunk.size(filtered);
                if (length === max) {
                    return T.succeed(Chunk.concat_(acc, filtered));
                }
                else {
                    return this.loop(max - length, Chunk.concat_(acc, filtered));
                }
            });
        });
    }
    takeUpTo(n) {
        return T.suspend(() => {
            return this.loop(n, Chunk.empty());
        });
    }
}
/**
 * Filters elements dequeued from the queue using the specified effectual
 * predicate.
 *
 * @ets_data_first filterOutputM_
 */
function filterOutputM(f) {
    return (self) => filterOutputM_(self, f);
}
exports.filterOutputM = filterOutputM;
/**
 * Filters elements dequeued from the queue using the specified predicate.
 */
function filterOutput_(self, f) {
    return filterOutputM_(self, (b) => T.succeed(f(b)));
}
exports.filterOutput_ = filterOutput_;
/**
 * Filters elements dequeued from the queue using the specified predicate.
 *
 * @ets_data_first filterOutput_
 */
function filterOutput(f) {
    return (self) => filterOutput_(self, f);
}
exports.filterOutput = filterOutput;
/**
 * Applies a filter to elements enqueued into this queue. Elements that do not
 * pass the filter will be immediately dropped.
 *
 * @ets_data_first filterInput_
 */
function filterInput(f) {
    return (self) => filterInput_(self, f);
}
exports.filterInput = filterInput;
/**
 * Applies a filter to elements enqueued into this queue. Elements that do not
 * pass the filter will be immediately dropped.
 */
function filterInput_(self, f) {
    return filterInputM_(self, (a) => T.succeed(f(a)));
}
exports.filterInput_ = filterInput_;
/**
 * Transforms elements dequeued from this queue with a function.
 */
function map_(self, f) {
    return mapM_(self, (_) => T.succeed(f(_)));
}
exports.map_ = map_;
/**
 * Transforms elements dequeued from this queue with a function.
 *
 * @ets_data_first map_
 */
function map(f) {
    return (self) => map_(self, f);
}
exports.map = map;
/**
 * Transforms elements dequeued from this queue with an effectful function.
 *
 * @ets_data_first mapM_
 */
function mapM(f) {
    return (self) => mapM_(self, f);
}
exports.mapM = mapM;
/**
 * Transforms elements dequeued from this queue with an effectful function.
 */
function mapM_(self, f) {
    return dimapM_(self, (a) => T.succeed(a), f);
}
exports.mapM_ = mapM_;
/**
 * Take the head option of values in the queue.
 */
function poll(self) {
    ;
    return T.map_(self.takeUpTo(1), (x) => Chunk.unsafeGet_(x, 0));
}
exports.poll = poll;
//# sourceMappingURL=api.js.map