"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.withRuntime = exports.withRuntimeM = exports.runtime = exports.runPromiseExit = exports.runPromise = exports.runFiber = exports.runCancel = exports.run = exports.defaultRuntime = exports.makeCustomRuntime = exports.CustomRuntime = exports.defaultPlatform = exports.prettyReporter = exports.defaultEnv = void 0;
// ets_tracing: off
// option
// cause
const Cause = __importStar(require("../Cause/core"));
const Pretty_1 = require("../Cause/Pretty");
// exit
const Clock_1 = require("../Clock");
const core_1 = require("../Exit/core");
// fiber
const context_1 = require("../Fiber/context");
const core_2 = require("../Fiber/core");
const id_1 = require("../Fiber/id");
const platform_1 = require("../Fiber/platform");
const Function_1 = require("../Function");
const O = __importStar(require("../Option"));
const Option_1 = require("../Option");
const Random_1 = require("../Random");
const Scope = __importStar(require("../Scope"));
// supervisor
const Supervisor = __importStar(require("../Supervisor"));
const Tracing_1 = require("../Tracing");
const core = __importStar(require("./core"));
const primitives_1 = require("./primitives");
// empty function
const empty = () => {
    //
};
exports.defaultEnv = {
    [Clock_1.HasClock.key]: new Clock_1.LiveClock(),
    [Random_1.HasRandom.key]: Random_1.defaultRandom
};
const prettyReporter = (e) => {
    console.error((0, Pretty_1.pretty)(e, Pretty_1.defaultRenderer));
};
exports.prettyReporter = prettyReporter;
exports.defaultPlatform = new platform_1.Platform({
    executionTraceLength: 25,
    stackTraceLength: 25,
    traceExecution: (0, Tracing_1.isTracingEnabled)(),
    traceStack: (0, Tracing_1.isTracingEnabled)(),
    traceEffects: (0, Tracing_1.isTracingEnabled)(),
    initialTracingStatus: (0, Tracing_1.isTracingEnabled)(),
    ancestorExecutionTraceLength: 25,
    ancestorStackTraceLength: 25,
    ancestryLength: 25,
    renderer: Pretty_1.defaultRenderer,
    reportFailure: Function_1.constVoid,
    maxOp: 500,
    supervisor: Supervisor.trackMainFibers
});
class CustomRuntime {
    constructor(env, platform) {
        this.env = env;
        this.platform = platform;
        this.traceExecution = this.traceExecution.bind(this);
        this.executionTraceLength = this.executionTraceLength.bind(this);
        this.traceStack = this.traceStack.bind(this);
        this.stackTraceLength = this.stackTraceLength.bind(this);
        this.traceEffect = this.traceEffect.bind(this);
        this.initialTracingStatus = this.initialTracingStatus.bind(this);
        this.ancestorExecutionTraceLength = this.ancestorExecutionTraceLength.bind(this);
        this.ancestorStackTraceLength = this.ancestorStackTraceLength.bind(this);
        this.ancestryLength = this.ancestryLength.bind(this);
        this.fiberContext = this.fiberContext.bind(this);
        this.run = this.run.bind(this);
        this.runCancel = this.runCancel.bind(this);
        this.runPromise = this.runPromise.bind(this);
        this.runPromiseExit = this.runPromiseExit.bind(this);
        this.traceRenderer = this.traceRenderer.bind(this);
        this.runFiber = this.runFiber.bind(this);
    }
    fiberContext(effect) {
        const initialIS = core_2.interruptible;
        const fiberId = (0, id_1.newFiberId)();
        const scope = Scope.unsafeMakeScope();
        const supervisor = Supervisor.none;
        const context = new context_1.FiberContext(fiberId, this.env, initialIS, new Map(), supervisor, scope, this.platform.value.maxOp, this.platform.value.reportFailure, this.platform, Option_1.none, this.platform.value.initialTracingStatus);
        if (supervisor !== Supervisor.none) {
            supervisor.unsafeOnStart(this.env, effect, O.none, context);
            context.onDone((exit) => supervisor.unsafeOnEnd((0, core_1.flatten)(exit), context));
        }
        context.evaluateLater(effect);
        return context;
    }
    supervised(supervisor) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, supervisor }));
    }
    runFiber(self) {
        const context = this.fiberContext(self);
        return context;
    }
    /**
     * Runs effect until completion, calling cb with the eventual exit state
     */
    run(self, cb) {
        const context = this.fiberContext(self);
        context.runAsync(cb || empty);
    }
    /**
     * Runs effect until completion returing a cancel effecr that when executed
     * triggers cancellation of the process
     */
    runCancel(self, cb) {
        const context = this.fiberContext(self);
        context.runAsync(cb || empty);
        return context.interruptAs(context.id);
    }
    /**
     * Run effect as a Promise, throwing a the first error or exception
     */
    runPromise(self) {
        const context = this.fiberContext(self);
        return new Promise((res, rej) => {
            context.runAsync((exit) => {
                switch (exit._tag) {
                    case "Success": {
                        res(exit.value);
                        break;
                    }
                    case "Failure": {
                        rej(Cause.squash(Function_1.identity)(exit.cause));
                        break;
                    }
                }
            });
        });
    }
    /**
     * Run effect as a Promise of the Exit state
     * in case of error.
     */
    runPromiseExit(self) {
        const context = this.fiberContext(self);
        return new Promise((res) => {
            context.runAsync((exit) => {
                res(exit);
            });
        });
    }
    withEnvironment(f) {
        return new CustomRuntime(f(this.env), this.platform);
    }
    traceRenderer(renderer) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, renderer }));
    }
    traceExecution(b) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, traceExecution: b }));
    }
    executionTraceLength(n) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, executionTraceLength: n }));
    }
    traceStack(b) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, traceStack: b }));
    }
    stackTraceLength(n) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, stackTraceLength: n }));
    }
    traceEffect(b) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, traceEffects: b }));
    }
    initialTracingStatus(b) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, initialTracingStatus: b }));
    }
    ancestorExecutionTraceLength(n) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, ancestorExecutionTraceLength: n }));
    }
    ancestorStackTraceLength(n) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, ancestorStackTraceLength: n }));
    }
    ancestryLength(n) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, ancestryLength: n }));
    }
    reportFailure(reportFailure) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, reportFailure }));
    }
    maxOp(maxOp) {
        return new CustomRuntime(this.env, new platform_1.Platform({ ...this.platform.value, maxOp }));
    }
}
exports.CustomRuntime = CustomRuntime;
/**
 * Construct custom runtime
 */
function makeCustomRuntime(env, platform) {
    return new CustomRuntime(env, platform);
}
exports.makeCustomRuntime = makeCustomRuntime;
/**
 * Default runtime
 */
exports.defaultRuntime = makeCustomRuntime(exports.defaultEnv, exports.defaultPlatform);
/**
 * Exports of default runtime
 */
exports.run = exports.defaultRuntime.run, exports.runCancel = exports.defaultRuntime.runCancel, exports.runFiber = exports.defaultRuntime.runFiber, exports.runPromise = exports.defaultRuntime.runPromise, exports.runPromiseExit = exports.defaultRuntime.runPromiseExit;
/**
 * Use current environment to build a runtime that is capable of
 * providing its content to other effects.
 *
 * NOTE: in should be used in a region where current environment
 * is valid (i.e. keep attention to closed resources)
 */
function runtime() {
    return core.accessM((r0) => new primitives_1.IPlatform((platform) => core.succeedWith(() => {
        return makeCustomRuntime(r0, platform);
    })));
}
exports.runtime = runtime;
function withRuntimeM(f) {
    return core.chain_(runtime(), f);
}
exports.withRuntimeM = withRuntimeM;
function withRuntime(f) {
    return core.chain_(runtime(), (r) => core.succeed(f(r)));
}
exports.withRuntime = withRuntime;
//# sourceMappingURL=runtime.js.map