"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.debounce = exports.debounce_ = void 0;
// ets_tracing: off
const CL = __importStar(require("../../../../Clock"));
const CK = __importStar(require("../../../../Collections/Immutable/Chunk"));
const T = __importStar(require("../../../../Effect"));
const Ex = __importStar(require("../../../../Exit"));
const F = __importStar(require("../../../../Fiber"));
const Function_1 = require("../../../../Function");
const M = __importStar(require("../../../../Managed"));
const O = __importStar(require("../../../../Option"));
const CH = __importStar(require("../../Channel"));
const C = __importStar(require("../core"));
const HO = __importStar(require("../Handoff"));
const SER = __importStar(require("../SinkEndReason"));
const CrossRight = __importStar(require("./crossRight"));
const Managed = __importStar(require("./managed"));
const Unwrap = __importStar(require("./unwrap"));
const NotStartedTypeId = Symbol();
class NotStarted {
    constructor() {
        this._typeId = NotStartedTypeId;
    }
}
const PreviousTypeId = Symbol();
class Previous {
    constructor(fiber) {
        this.fiber = fiber;
        this._typeId = PreviousTypeId;
    }
}
const CurrentTypeId = Symbol();
class Current {
    constructor(fiber) {
        this.fiber = fiber;
        this._typeId = CurrentTypeId;
    }
}
/**
 * Delays the emission of values by holding new values for a set duration. If no new values
 * arrive during that time the value is emitted, however if a new value is received during the holding period
 * the previous value is discarded and the process is repeated with the new value.
 *
 * This operator is useful if you have a stream of "bursty" events which eventually settle down and you
 * only need the final event of the burst.
 *
 * @example A search engine may only want to initiate a search after a user has paused typing
 *          so as to not prematurely recommend results.
 */
function debounce_(self, d) {
    return Unwrap.unwrap((T.map_(T.bind_(T.bind_(T.do, "scope", () => T.forkScope), "handoff", () => HO.make()), ({ handoff, scope }) => {
        const enqueue = (last) => (T.map_(T.bind_(T.do, "f", () => (T.forkIn_(T.as_(CL.sleep(d), last), scope))), ({ f }) => consumer(new Previous(f))));
        const producer = CH.readWithCause((in_) => O.fold_(CK.last(in_), () => producer, (last) => CH.zipRight_(CH.fromEffect(HO.offer(handoff, new HO.Emit(CK.single(last)))), producer)), (cause) => CH.fromEffect(HO.offer(handoff, new HO.Halt(cause))), (_) => CH.fromEffect(HO.offer(handoff, new HO.End(new SER.UpstreamEnd()))));
        const consumer = (state) => CH.unwrap((() => {
            switch (state._typeId) {
                case NotStartedTypeId:
                    return T.map_(HO.take(handoff), (sig) => {
                        switch (sig._typeId) {
                            case HO.EmitTypeId:
                                return CH.unwrap(enqueue(sig.els));
                            case HO.HaltTypeId:
                                return CH.failCause(sig.error);
                            case HO.EndTypeId:
                                return CH.unit;
                        }
                    });
                case CurrentTypeId:
                    return T.map_(F.join(state.fiber), (sig) => {
                        switch (sig._typeId) {
                            case HO.EmitTypeId:
                                return CH.unwrap(enqueue(sig.els));
                            case HO.HaltTypeId:
                                return CH.failCause(sig.error);
                            case HO.EndTypeId:
                                return CH.unit;
                        }
                    });
                case PreviousTypeId:
                    return T.raceWith_(F.join(state.fiber), HO.take(handoff), (ex, current) => {
                        if (Ex.succeeded(ex)) {
                            return T.succeed(CH.zipRight_(CH.write(ex.value), consumer(new Current(current))));
                        }
                        else {
                            return T.as_(F.interrupt(current), CH.failCause(ex.cause));
                        }
                    }, (ex, previous) => {
                        if (Ex.succeeded(ex)) {
                            const sig = ex.value;
                            switch (sig._typeId) {
                                case HO.EmitTypeId:
                                    return T.zipRight_(F.interrupt(previous), enqueue(sig.els));
                                case HO.HaltTypeId:
                                    return T.as_(F.interrupt(previous), CH.failCause(sig.error));
                                case HO.EndTypeId:
                                    return T.map_(F.join(previous), (_) => CH.zipRight_(CH.write(_), CH.unit));
                            }
                        }
                        else {
                            return T.as_(F.interrupt(previous), CH.failCause(ex.cause));
                        }
                    });
            }
        })());
        return CrossRight.crossRight_(Managed.managed(M.fork(CH.runManaged(self.channel[">>>"](producer)))), new C.Stream(consumer(new NotStarted())));
    })));
}
exports.debounce_ = debounce_;
/**
 * Delays the emission of values by holding new values for a set duration. If no new values
 * arrive during that time the value is emitted, however if a new value is received during the holding period
 * the previous value is discarded and the process is repeated with the new value.
 *
 * This operator is useful if you have a stream of "bursty" events which eventually settle down and you
 * only need the final event of the burst.
 *
 * @example A search engine may only want to initiate a search after a user has paused typing
 *          so as to not prematurely recommend results.
 *
 * @ets_data_first debounce_
 */
function debounce(d) {
    return (self) => debounce_(self, d);
}
exports.debounce = debounce;
//# sourceMappingURL=debounce.js.map