"use strict";
const io_1 = require("jabz/io");
const Future_1 = require("./Future");
const Behavior_1 = require("./Behavior");
/**
 * The Now monad represents a computation that takes place in a given
 * momemt and where the moment will always be now when the computation
 * is run.
 */
class Now {
    of(b) {
        return new OfNow(b);
    }
    static of(b) {
        return new OfNow(b);
    }
    chain(f) {
        return new ChainNow(this, f);
    }
    flatten(now) {
        return now.chain((n) => n);
    }
    map(f) {
        return this.chain((a) => this.of(f(a)));
    }
    mapTo(b) {
        return this.chain((_) => this.of(b));
    }
    lift(f, ...ms) {
        const { of } = ms[0];
        switch (f.length) {
            case 1:
                return ms[0].map(f);
            case 2:
                return ms[0].chain((a) => ms[1].chain((b) => of(f(a, b))));
            case 3:
                return ms[0].chain((a) => ms[1].chain((b) => ms[2].chain((c) => of(f(a, b, c)))));
        }
    }
}
exports.Now = Now;
class OfNow extends Now {
    constructor(value) {
        super();
        this.value = value;
    }
    run() {
        return this.value;
    }
}
class ChainNow extends Now {
    constructor(first, f) {
        super();
        this.first = first;
        this.f = f;
    }
    run() {
        return this.f(this.first.run()).run();
    }
}
class AsyncNow extends Now {
    constructor(comp) {
        super();
        this.comp = comp;
    }
    run() {
        return Future_1.fromPromise(io_1.runIO(this.comp));
    }
}
/**
 * Run an asynchronous IO action and return a future in the Now monad
 * that resolves with the eventual result of the IO action once it
 * completes. This function is what allows the Now monad to execute
 * imperative actions in a way that is pure and integrated with FRP.
 */
function async(comp) {
    return new AsyncNow(comp);
}
exports.async = async;
class SampleNow extends Now {
    constructor(b) {
        super();
        this.b = b;
    }
    run() {
        return Behavior_1.at(this.b);
    }
}
/**
 * Returns the current value of a behavior in the Now monad. This is
 * possible because computations in the Now monad has an associated
 * point in time.
 */
function sample(b) {
    return new SampleNow(b);
}
exports.sample = sample;
function run(now) {
    return now.run();
}
class PlanNow extends Now {
    constructor(future) {
        super();
        this.future = future;
    }
    run() {
        return this.future.map(run);
    }
}
/**
 * Convert a future now computation into a now computation of a
 * future. This function is what allows a Now-computation to reach
 * beyond the current moment that it is running in.
 */
function plan(future) {
    return new PlanNow(future);
}
exports.plan = plan;
/**
 * Run the given Now-computation. The returned promise resolves once
 * the future that is the result of running the now computation
 * occurs. This is an impure function and should not be used in normal
 * application code.
 */
function runNow(now) {
    return new Promise((resolve, reject) => {
        now.run().subscribe(resolve);
    });
}
exports.runNow = runNow;
//# sourceMappingURL=Now.js.map