"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.unwrapManaged = exports.unwrap = exports.unfoldChunksEffect = exports.toPull = exports.take = exports.take_ = exports.succeedWith = exports.succeed = exports.runDrain = exports.runCollect = exports.run = exports.run_ = exports.map = exports.map_ = exports.loopOnPartialChunksElements_ = exports.loopOnPartialChunks_ = exports.loopOnChunks_ = exports.flatten = exports.mapEffect = exports.mapEffect_ = exports.managed = exports.effectOption = exports.effect = exports.fromChunkWith = exports.fromChunk = exports.forever = exports.failWith = exports.fail = exports.dieWith = exports.die = exports.combineChunks = exports.combineChunks_ = exports.unfoldChunkEffect = exports.chain = exports.chain_ = exports.isStream = exports.empty = exports.Stream = exports.StreamTypeId = void 0;
// ets_tracing: off
require("../../../Operator");
const Cause = __importStar(require("../../../Cause"));
const Chunk = __importStar(require("../../../Collections/Immutable/Chunk"));
const Tp = __importStar(require("../../../Collections/Immutable/Tuple"));
const T = __importStar(require("../../../Effect"));
const Function_1 = require("../../../Function");
const M = __importStar(require("../../../Managed"));
const O = __importStar(require("../../../Option"));
const HO = __importStar(require("../_internal/Handoff"));
const C = __importStar(require("../Channel"));
const Sink = __importStar(require("../Sink"));
const Take = __importStar(require("../Take"));
exports.StreamTypeId = Symbol();
/**
 * A `Stream<R, E, A>` is a description of a program that, when evaluated,
 * may emit 0 or more values of type `A`, may fail with errors of type `E`
 * and uses an environment of type `R`.
 * One way to think of `Stream` is as a `Effect` program that could emit multiple values.
 *
 * `Stream` is a purely functional *pull* based stream. Pull based streams offer
 * inherent laziness and backpressure, relieving users of the need to manage buffers
 * between operators. As an optimization, `Stream` does not emit single values, but
 * rather an array of values. This allows the cost of effect evaluation to be
 * amortized.
 *
 * `Stream` forms a monad on its `A` type parameter, and has error management
 * facilities for its `E` type parameter, modeled similarly to `Effect` (with some
 * adjustments for the multiple-valued nature of `Stream`). These aspects allow
 * for rich and expressive composition of streams.
 */
class Stream {
    constructor(channel) {
        this.channel = channel;
        this._typeId = exports.StreamTypeId;
    }
}
exports.Stream = Stream;
T._R, T._E, T._A;
/**
 * Empty stream
 */
exports.empty = fromChunk(Chunk.empty());
function isStream(u) {
    return typeof u === "object" && u != null && exports.StreamTypeId in u;
}
exports.isStream = isStream;
/**
 * Returns a stream made of the concatenation in strict order of all the streams
 * produced by passing each element of this stream to `f`
 */
function chain_(self, f) {
    return new Stream(C.concatMap_(self.channel, (o) => Chunk.reduce_(Chunk.map_(o, (x) => f(x).channel), C.unit, (s, a) => C.chain_(s, () => a))));
}
exports.chain_ = chain_;
/**
 * Returns a stream made of the concatenation in strict order of all the streams
 * produced by passing each element of this stream to `f`
 *
 * @ets_data_first chain_
 */
function chain(f) {
    return (self) => chain_(self, f);
}
exports.chain = chain;
/**
 * Creates a stream by effectfully peeling off the "layers" of a value of type `S`
 */
function unfoldChunkEffect(s, f) {
    const loop = (s) => C.unwrap(T.map_(f(s), O.fold(() => C.end(undefined), ({ tuple: [as, s] }) => C.zipRight_(C.write(as), loop(s)))));
    return new Stream(loop(s));
}
exports.unfoldChunkEffect = unfoldChunkEffect;
/**
 * Combines the chunks from this stream and the specified stream by repeatedly applying the
 * function `f` to extract a chunk using both sides and conceptually "offer"
 * it to the destination stream. `f` can maintain some internal state to control
 * the combining process, with the initial state being specified by `s`.
 */
function combineChunks_(self, that, s, f) {
    const producer = (handoff, latch) => C.zipRight_(C.fromEffect(HO.take(latch)), C.readWithCause((chunk) => C.zipRight_(C.fromEffect(HO.offer(handoff, Take.chunk(chunk))), producer(handoff, latch)), (cause) => C.fromEffect(HO.offer(handoff, Take.failCause(cause))), (_) => C.zipRight_(C.fromEffect(HO.offer(handoff, Take.end)), producer(handoff, latch))));
    return new Stream(C.managed_((M.map_(M.tap_(M.tap_(M.bind_(M.bind_(M.bind_(M.bind_(M.do, "left", () => T.toManaged(HO.make())), "right", () => T.toManaged(HO.make())), "latchL", () => T.toManaged(HO.make())), "latchR", () => T.toManaged(HO.make())), ({ latchL, left }) => (M.fork(C.runManaged(self.channel[">>>"](producer(left, latchL)))))), ({ latchR, right }) => (M.fork(C.runManaged(that.channel[">>>"](producer(right, latchR)))))), ({ latchL, latchR, left, right }) => Tp.tuple(left, right, latchL, latchR))), ({ tuple: [left, right, latchL, latchR] }) => {
        const pullLeft = T.zipRight_(HO.offer(latchL, undefined), T.chain_(HO.take(left), Take.done));
        const pullRight = T.zipRight_(HO.offer(latchR, undefined), T.chain_(HO.take(right), Take.done));
        return unfoldChunkEffect(s, (s) => T.chain_(f(s, pullLeft, pullRight), (_) => T.unoption(T.done(_)))).channel;
    }));
}
exports.combineChunks_ = combineChunks_;
/**
 * Combines the chunks from this stream and the specified stream by repeatedly applying the
 * function `f` to extract a chunk using both sides and conceptually "offer"
 * it to the destination stream. `f` can maintain some internal state to control
 * the combining process, with the initial state being specified by `s`.
 *
 * @ets_data_first combineChunks_
 */
function combineChunks(that, s, f) {
    return (self) => combineChunks_(self, that, s, f);
}
exports.combineChunks = combineChunks;
/**
 * Halt a stream with the specified exception
 */
function die(u) {
    return new Stream(C.die(u));
}
exports.die = die;
/**
 * Halt a stream with the specified exception
 */
function dieWith(u) {
    return new Stream(C.dieWith(u));
}
exports.dieWith = dieWith;
/**
 * Halt a stream with the specified error
 */
function fail(error) {
    return new Stream(C.fail(error));
}
exports.fail = fail;
/**
 * Halt a stream with the specified error
 */
function failWith(error) {
    return new Stream(C.failWith(error));
}
exports.failWith = failWith;
/**
 * Repeats this stream forever.
 */
function forever(self) {
    return new Stream(C.repeated(self.channel));
}
exports.forever = forever;
/**
 * Creates a stream from a `Chunk` of values
 *
 * @param c a chunk of values
 * @return a finite stream of values
 */
function fromChunk(c) {
    return new Stream(C.unwrap(T.succeedWith(() => C.write(c))));
}
exports.fromChunk = fromChunk;
/**
 * Creates a stream from a `Chunk` of values
 *
 * @param c a chunk of values
 * @return a finite stream of values
 */
function fromChunkWith(c) {
    return new Stream(C.unwrap(T.succeedWith(() => C.writeWith(c))));
}
exports.fromChunkWith = fromChunkWith;
/**
 * Creates a stream from an effect producing a value of type `A`
 */
function effect(self) {
    return new Stream(C.unwrap(T.fold_(self, C.fail, (x) => C.write(Chunk.single(x)))));
}
exports.effect = effect;
/**
 * Creates a stream from an effect producing a value of type `A` or an empty Stream
 */
function effectOption(self) {
    return new Stream(C.unwrap(T.fold_(self, O.fold(() => C.unit, C.fail), (x) => C.write(Chunk.single(x)))));
}
exports.effectOption = effectOption;
/**
 * Creates a single-valued stream from a managed resource
 */
function managed(self) {
    return new Stream(C.managedOut(M.map_(self, Chunk.single)));
}
exports.managed = managed;
/**
 * Maps over elements of the stream with the specified effectful function.
 */
function mapEffect_(self, f) {
    return loopOnPartialChunksElements_(self, (a, emit) => T.chain_(f(a), emit));
}
exports.mapEffect_ = mapEffect_;
/**
 * Maps over elements of the stream with the specified effectful function.
 *
 * @ets_data_first mapEffect_
 */
function mapEffect(f) {
    return (self) => mapEffect_(self, f);
}
exports.mapEffect = mapEffect;
/**
 * Flattens this stream-of-streams into a stream made of the concatenation in
 * strict order of all the streams.
 */
function flatten(self) {
    return chain_(self, Function_1.identity);
}
exports.flatten = flatten;
/**
 * Loops over the stream chunks concatenating the result of f
 */
function loopOnChunks_(self, f) {
    const loop = C.readWithCause((chunk) => C.chain_(f(chunk), (cont) => (cont ? loop : C.end(false))), C.failCause, (_) => C.end(false));
    return new Stream(self.channel[">>>"](loop));
}
exports.loopOnChunks_ = loopOnChunks_;
/**
 * Loops on chunks emitting partially
 */
function loopOnPartialChunks_(self, f) {
    return loopOnChunks_(self, (chunk) => C.unwrap(T.suspend(() => {
        let outputChunk = Chunk.empty();
        return T.catchAll_(T.map_(f(chunk, (a) => T.succeedWith(() => {
            outputChunk = Chunk.append_(outputChunk, a);
        })), (cont) => C.chain_(C.write(outputChunk), () => C.end(cont))), (failure) => T.succeedWith(() => {
            if (Chunk.isEmpty(outputChunk)) {
                return C.fail(failure);
            }
            else {
                return C.chain_(C.write(outputChunk), () => C.fail(failure));
            }
        }));
    })));
}
exports.loopOnPartialChunks_ = loopOnPartialChunks_;
/**
 * Loops on chunks elements emitting partially
 */
function loopOnPartialChunksElements_(self, f) {
    return loopOnPartialChunks_(self, (a, emit) => T.as_(Chunk.mapM_(a, (a) => f(a, emit)), true));
}
exports.loopOnPartialChunksElements_ = loopOnPartialChunksElements_;
/**
 * Transforms the elements of this stream using the supplied function.
 */
function map_(self, f) {
    return new Stream(C.mapOut_(self.channel, (o) => Chunk.map_(o, f)));
}
exports.map_ = map_;
/**
 * Transforms the elements of this stream using the supplied function.
 *
 * @ets_data_first map_
 */
function map(f) {
    return (self) => map_(self, f);
}
exports.map = map;
/**
 * Runs the sink on the stream to produce either the sink's result or an error.
 */
function run_(self, sink) {
    return C.runDrain(self.channel[">>>"](sink.channel));
}
exports.run_ = run_;
/**
 * Runs the sink on the stream to produce either the sink's result or an error.
 *
 * @ets_data_first run_
 */
function run(sink) {
    return (self) => run_(self, sink);
}
exports.run = run;
/**
 * Runs the stream and collects all of its elements to a chunk.
 */
function runCollect(self) {
    return run_(self, Sink.collectAll());
}
exports.runCollect = runCollect;
/**
 * Runs the stream and collects ignore its elements.
 */
function runDrain(self) {
    return run_(self, Sink.drain());
}
exports.runDrain = runDrain;
/**
 * Creates a single-valued pure stream
 */
function succeed(o) {
    return fromChunk(Chunk.single(o));
}
exports.succeed = succeed;
/**
 * Creates a single-valued pure stream
 */
function succeedWith(o) {
    return fromChunkWith(() => Chunk.single(o()));
}
exports.succeedWith = succeedWith;
function takeLoop(n) {
    return C.readWith((i) => {
        const taken = Chunk.take_(i, n);
        const left = Math.max(n - Chunk.size(taken), 0);
        if (left > 0) {
            return C.chain_(C.write(taken), () => takeLoop(left));
        }
        else {
            return C.write(taken);
        }
    }, C.fail, C.end);
}
/**
 * Takes the specified number of elements from this stream.
 */
function take_(self, n) {
    if (n <= 0) {
        return exports.empty;
    }
    if (!Number.isInteger(n)) {
        return die(new Cause.IllegalArgumentException(`${n} should be an integer`));
    }
    return new Stream(self.channel[">>>"](takeLoop(n)));
}
exports.take_ = take_;
/**
 * Takes the specified number of elements from this stream.
 *
 * @ets_data_first take_
 */
function take(n) {
    return (self) => take_(self, n);
}
exports.take = take;
/**
 * Interpret the stream as a managed pull
 */
function toPull(self) {
    return M.map_(C.toPull(self.channel), (pull) => T.mapError_(pull, (e) => (e._tag === "Left" ? O.some(e.left) : O.none)));
}
exports.toPull = toPull;
function unfoldChunksLoop(s, f) {
    return C.unwrap(T.map_(f(s), O.fold(() => C.unit, ({ tuple: [as, s] }) => C.chain_(C.write(as), () => unfoldChunksLoop(s, f)))));
}
/**
 * Creates a stream by effectfully peeling off the "layers" of a value of type `S`
 */
function unfoldChunksEffect(s, f) {
    return new Stream(unfoldChunksLoop(s, f));
}
exports.unfoldChunksEffect = unfoldChunksEffect;
/**
 * Creates a stream produced from an effect
 */
function unwrap(self) {
    return flatten(effect(self));
}
exports.unwrap = unwrap;
/**
 * Creates a stream produced from a managed
 */
function unwrapManaged(self) {
    return flatten(managed(self));
}
exports.unwrapManaged = unwrapManaged;
//# sourceMappingURL=core.js.map