import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { CompareResult, ArrayQueue } from '../../../../base/common/arrays.js';
import { onUnexpectedError, BugIndicatingError } from '../../../../base/common/errors.js';
import { toDisposable, DisposableStore } from '../../../../base/common/lifecycle.js';
import '../../../../base/common/observableInternal/derived.js';
import { autorunOpts } from '../../../../base/common/observableInternal/autorun.js';
import { observableFromEvent } from '../../../../base/common/observableInternal/utils.js';
import '../../../../base/common/cancellation.js';
import { IStorageService } from '../../../../platform/storage/common/storage.js';
class ReentrancyBarrier {
    constructor() {
        this._isActive = false;
    }
    get isActive() {
        return this._isActive;
    }
    makeExclusive(fn) {
        return ((...args) => {
            if (this._isActive) {
                return;
            }
            this._isActive = true;
            try {
                return fn(...args);
            }
            finally {
                this._isActive = false;
            }
        });
    }
    runExclusively(fn) {
        if (this._isActive) {
            return;
        }
        this._isActive = true;
        try {
            fn();
        }
        finally {
            this._isActive = false;
        }
    }
    runExclusivelyOrThrow(fn) {
        if (this._isActive) {
            throw new BugIndicatingError();
        }
        this._isActive = true;
        try {
            fn();
        }
        finally {
            this._isActive = false;
        }
    }
}
function setStyle(element, style) {
    Object.entries(style).forEach(([key, value]) => {
        element.style.setProperty(key, toSize(value));
    });
}
function toSize(value) {
    return typeof value === 'number' ? `${value}px` : value;
}
function applyObservableDecorations(editor, decorations) {
    const d = ( new DisposableStore());
    let decorationIds = [];
    d.add(autorunOpts({ debugName: () => `Apply decorations from ${decorations.debugName}` }, reader => {
        const d = decorations.read(reader);
        editor.changeDecorations(a => {
            decorationIds = a.deltaDecorations(decorationIds, d);
        });
    }));
    d.add({
        dispose: () => {
            editor.changeDecorations(a => {
                decorationIds = a.deltaDecorations(decorationIds, []);
            });
        }
    });
    return d;
}
function* leftJoin(left, right, compare) {
    const rightQueue = ( new ArrayQueue(right));
    for (const leftElement of left) {
        rightQueue.takeWhile(rightElement => CompareResult.isGreaterThan(compare(leftElement, rightElement)));
        const equals = rightQueue.takeWhile(rightElement => CompareResult.isNeitherLessOrGreaterThan(compare(leftElement, rightElement)));
        yield { left: leftElement, rights: equals || [] };
    }
}
function* join(left, right, compare) {
    const rightQueue = ( new ArrayQueue(right));
    for (const leftElement of left) {
        const skipped = rightQueue.takeWhile(rightElement => CompareResult.isGreaterThan(compare(leftElement, rightElement)));
        if (skipped) {
            yield { rights: skipped };
        }
        const equals = rightQueue.takeWhile(rightElement => CompareResult.isNeitherLessOrGreaterThan(compare(leftElement, rightElement)));
        yield { left: leftElement, rights: equals || [] };
    }
}
function concatArrays(...arrays) {
    return [].concat(...arrays);
}
function thenIfNotDisposed(promise, then) {
    let disposed = false;
    promise.then(() => {
        if (disposed) {
            return;
        }
        then();
    });
    return toDisposable(() => {
        disposed = true;
    });
}
function setFields(obj, fields) {
    return Object.assign(obj, fields);
}
function deepMerge(source1, source2) {
    const result = {};
    for (const key in source1) {
        result[key] = source1[key];
    }
    for (const key in source2) {
        const source2Value = source2[key];
        if (typeof result[key] === 'object' && source2Value && typeof source2Value === 'object') {
            result[key] = deepMerge(result[key], source2Value);
        }
        else {
            result[key] = source2Value;
        }
    }
    return result;
}
let PersistentStore = class PersistentStore {
    constructor(key, storageService) {
        this.key = key;
        this.storageService = storageService;
        this.hasValue = false;
        this.value = undefined;
    }
    get() {
        if (!this.hasValue) {
            const value = this.storageService.get(this.key, 0 );
            if (value !== undefined) {
                try {
                    this.value = JSON.parse(value);
                }
                catch (e) {
                    onUnexpectedError(e);
                }
            }
            this.hasValue = true;
        }
        return this.value;
    }
    set(newValue) {
        this.value = newValue;
        this.storageService.store(this.key, JSON.stringify(this.value), 0 , 0 );
    }
};
PersistentStore = ( __decorate([
    ( __param(1, IStorageService))
], PersistentStore));
function observableConfigValue(key, defaultValue, configurationService) {
    return observableFromEvent((handleChange) => configurationService.onDidChangeConfiguration(e => {
        if (e.affectsConfiguration(key)) {
            handleChange(e);
        }
    }), () => configurationService.getValue(key) ?? defaultValue);
}
export { PersistentStore, ReentrancyBarrier, applyObservableDecorations, concatArrays, deepMerge, join, leftJoin, observableConfigValue, setFields, setStyle, thenIfNotDisposed };
