import { autorun } from './autorun.js';
import { observableValue, transaction } from './base.js';
import { derived } from './derived.js';
import '../cancellation.js';
class ObservableLazy {
    get cachedValue() { return this._value; }
    constructor(_computeValue) {
        this._computeValue = _computeValue;
        this._value = observableValue(this, undefined);
    }
    getValue() {
        let v = this._value.get();
        if (!v) {
            v = this._computeValue();
            this._value.set(v, undefined);
        }
        return v;
    }
}
class ObservablePromise {
    constructor(promise) {
        this._value = observableValue(this, undefined);
        this.promiseResult = this._value;
        this.promise = promise.then(value => {
            transaction(tx => {
                this._value.set(( new PromiseResult(value, undefined)), tx);
            });
            return value;
        }, error => {
            transaction(tx => {
                this._value.set(( new PromiseResult(undefined, error)), tx);
            });
            throw error;
        });
    }
}
class PromiseResult {
    constructor(
    data,
    error) {
        this.data = data;
        this.error = error;
    }
    getDataOrThrow() {
        if (this.error) {
            throw this.error;
        }
        return this.data;
    }
}
class ObservableLazyPromise {
    constructor(_computePromise) {
        this._computePromise = _computePromise;
        this._lazyValue = ( new ObservableLazy(() => ( new ObservablePromise(this._computePromise()))));
        this.cachedPromiseResult = derived(this, reader => this._lazyValue.cachedValue.read(reader)?.promiseResult.read(reader));
    }
    getPromise() {
        return this._lazyValue.getValue().promise;
    }
}
function waitForState(observable, predicate, isError) {
    return ( new Promise((resolve, reject) => {
        let isImmediateRun = true;
        let shouldDispose = false;
        const stateObs = ( observable.map(state => {
            return {
                isFinished: predicate(state),
                error: isError ? isError(state) : false,
                state
            };
        }));
        const d = autorun(reader => {
            const { isFinished, error, state } = stateObs.read(reader);
            if (isFinished || error) {
                if (isImmediateRun) {
                    shouldDispose = true;
                }
                else {
                    d.dispose();
                }
                if (error) {
                    reject(error === true ? state : error);
                }
                else {
                    resolve(state);
                }
            }
        });
        isImmediateRun = false;
        if (shouldDispose) {
            d.dispose();
        }
    }));
}
export { ObservableLazy, ObservableLazyPromise, ObservablePromise, PromiseResult, waitForState };
