"use strict";
// *****************************************************************************
// Copyright (C) 2022 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.InjectablePreferenceProxy = exports.PreferenceProxyChange = exports.PreferenceProxyFactory = exports.PreferenceProxySchema = void 0;
const inversify_1 = require("inversify");
const common_1 = require("../../common");
const preference_proxy_1 = require("./preference-proxy");
const preference_service_1 = require("./preference-service");
const preference_language_override_service_1 = require("./preference-language-override-service");
exports.PreferenceProxySchema = Symbol('PreferenceProxySchema');
exports.PreferenceProxyFactory = Symbol('PreferenceProxyFactory');
class PreferenceProxyChange extends preference_service_1.PreferenceChangeImpl {
    constructor(change, overrideIdentifier) {
        super(change);
        this.overrideIdentifier = overrideIdentifier;
    }
    affects(resourceUri, overrideIdentifier) {
        if (overrideIdentifier !== this.overrideIdentifier) {
            return false;
        }
        return super.affects(resourceUri);
    }
}
exports.PreferenceProxyChange = PreferenceProxyChange;
let InjectablePreferenceProxy = class InjectablePreferenceProxy {
    constructor() {
        this.toDispose = new common_1.DisposableCollection();
    }
    get prefix() {
        var _a;
        return (_a = this.options.prefix) !== null && _a !== void 0 ? _a : '';
    }
    get style() {
        var _a;
        return (_a = this.options.style) !== null && _a !== void 0 ? _a : 'flat';
    }
    get resourceUri() {
        return this.options.resourceUri;
    }
    get overrideIdentifier() {
        return this.options.overrideIdentifier;
    }
    get isDeep() {
        const { style } = this;
        return style === 'deep' || style === 'both';
    }
    get isFlat() {
        const { style } = this;
        return style === 'flat' || style === 'both';
    }
    get onPreferenceChangedEmitter() {
        if (!this._onPreferenceChangedEmitter) {
            this._onPreferenceChangedEmitter = new common_1.Emitter();
            this.subscribeToChangeEvents();
            this.toDispose.push(this._onPreferenceChangedEmitter);
        }
        return this._onPreferenceChangedEmitter;
    }
    get onPreferenceChanged() {
        return this.onPreferenceChangedEmitter.event;
    }
    init() {
        const schema = this.promisedSchema();
        if (schema instanceof Promise) {
            schema.then(resolvedSchema => this.schema = resolvedSchema);
        }
        else {
            this.schema = schema;
        }
    }
    get(target, property, receiver) {
        if (typeof property !== 'string') {
            throw new Error(`Unexpected property: ${String(property)}`);
        }
        const preferenceName = this.prefix + property;
        if (this.schema && (this.isFlat || !property.includes('.')) && this.schema.properties[preferenceName]) {
            const { overrideIdentifier } = this;
            const toGet = overrideIdentifier ? this.preferences.overridePreferenceName({ overrideIdentifier, preferenceName }) : preferenceName;
            return this.getValue(toGet, undefined);
        }
        switch (property) {
            case 'onPreferenceChanged':
                return this.onPreferenceChanged;
            case 'dispose':
                return this.dispose.bind(this);
            case 'ready':
                return Promise.all([this.preferences.ready, this.promisedSchema]).then(() => undefined);
            case 'get':
                return this.getValue.bind(this);
            case 'toJSON':
                return this.toJSON.bind(this);
            case 'ownKeys':
                return this.ownKeys.bind(this);
        }
        if (this.schema && this.isDeep) {
            const prefix = `${preferenceName}.`;
            if (Object.keys(this.schema.properties).some(key => key.startsWith(prefix))) {
                const { style, resourceUri, overrideIdentifier } = this;
                return this.factory(this.schema, { prefix, resourceUri, style, overrideIdentifier });
            }
            let value; // eslint-disable-line @typescript-eslint/no-explicit-any
            let parentSegment = preferenceName;
            const segments = [];
            do {
                const index = parentSegment.lastIndexOf('.');
                segments.push(parentSegment.substring(index + 1));
                parentSegment = parentSegment.substring(0, index);
                if (parentSegment in this.schema.properties) {
                    value = this.get(target, parentSegment, receiver);
                }
            } while (parentSegment && value === undefined);
            let segment;
            while ((0, common_1.isObject)(value) && (segment = segments.pop())) {
                value = value[segment];
            }
            return segments.length ? undefined : value;
        }
    }
    set(target, property, value, receiver) {
        if (typeof property !== 'string') {
            throw new Error(`Unexpected property: ${String(property)}`);
        }
        const { style, schema, prefix, resourceUri, overrideIdentifier } = this;
        if (style === 'deep' && property.indexOf('.') !== -1) {
            return false;
        }
        if (schema) {
            const fullProperty = prefix ? prefix + property : property;
            if (schema.properties[fullProperty]) {
                this.preferences.set(fullProperty, value, preference_service_1.PreferenceScope.Default);
                return true;
            }
            const newPrefix = fullProperty + '.';
            for (const p of Object.keys(schema.properties)) {
                if (p.startsWith(newPrefix)) {
                    const subProxy = this.factory(schema, {
                        prefix: newPrefix,
                        resourceUri,
                        overrideIdentifier,
                        style
                    }); // eslint-disable-line @typescript-eslint/no-explicit-any
                    const valueAsContainer = value;
                    for (const k of Object.keys(valueAsContainer)) {
                        subProxy[k] = valueAsContainer[k];
                    }
                }
            }
        }
        return false;
    }
    ownKeys() {
        const properties = [];
        if (this.schema) {
            const { isDeep, isFlat, prefix } = this;
            for (const property of Object.keys(this.schema.properties)) {
                if (property.startsWith(prefix)) {
                    const idx = property.indexOf('.', prefix.length);
                    if (idx !== -1 && isDeep) {
                        const pre = property.substring(prefix.length, idx);
                        if (properties.indexOf(pre) === -1) {
                            properties.push(pre);
                        }
                    }
                    const prop = property.substring(prefix.length);
                    if (isFlat || prop.indexOf('.') === -1) {
                        properties.push(prop);
                    }
                }
            }
        }
        return properties;
    }
    getOwnPropertyDescriptor(target, property) {
        if (this.ownKeys().includes(property)) {
            return {
                enumerable: true,
                configurable: true
            };
        }
        return {};
    }
    deleteProperty() {
        throw new Error('Unsupported operation');
    }
    defineProperty() {
        throw new Error('Unsupported operation');
    }
    toJSON() {
        const result = {};
        for (const key of this.ownKeys()) {
            result[key] = this.get(undefined, key, undefined);
        }
        return result;
    }
    ;
    subscribeToChangeEvents() {
        this.toDispose.push(this.preferences.onPreferencesChanged(changes => this.handlePreferenceChanges(changes)));
    }
    handlePreferenceChanges(changes) {
        if (this.schema) {
            for (const change of Object.values(changes)) {
                const overrideInfo = this.preferences.overriddenPreferenceName(change.preferenceName);
                if (this.isRelevantChange(change, overrideInfo)) {
                    this.fireChangeEvent(this.buildNewChangeEvent(change, overrideInfo));
                }
            }
        }
    }
    isRelevantChange(change, overrideInfo) {
        var _a, _b;
        const preferenceName = (_a = overrideInfo === null || overrideInfo === void 0 ? void 0 : overrideInfo.preferenceName) !== null && _a !== void 0 ? _a : change.preferenceName;
        return preferenceName.startsWith(this.prefix)
            && (!this.overrideIdentifier || (overrideInfo === null || overrideInfo === void 0 ? void 0 : overrideInfo.overrideIdentifier) === this.overrideIdentifier)
            && Boolean((_b = this.schema) === null || _b === void 0 ? void 0 : _b.properties[preferenceName]);
    }
    fireChangeEvent(change) {
        this.onPreferenceChangedEmitter.fire(change);
    }
    buildNewChangeEvent(change, overrideInfo) {
        var _a;
        const preferenceName = ((_a = overrideInfo === null || overrideInfo === void 0 ? void 0 : overrideInfo.preferenceName) !== null && _a !== void 0 ? _a : change.preferenceName);
        const { newValue, oldValue, scope, domain } = change;
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        return new PreferenceProxyChange({ newValue, oldValue, preferenceName, scope, domain }, overrideInfo === null || overrideInfo === void 0 ? void 0 : overrideInfo.overrideIdentifier);
    }
    getValue(preferenceIdentifier, defaultValue, resourceUri = this.resourceUri) {
        const preferenceName = preference_language_override_service_1.OverridePreferenceName.is(preferenceIdentifier) ? this.preferences.overridePreferenceName(preferenceIdentifier) : preferenceIdentifier;
        return this.preferences.get(preferenceName, defaultValue, resourceUri);
    }
    dispose() {
        if (this.options.isDisposable) {
            this.toDispose.dispose();
        }
    }
};
__decorate([
    (0, inversify_1.inject)(preference_proxy_1.PreferenceProxyOptions),
    __metadata("design:type", Object)
], InjectablePreferenceProxy.prototype, "options", void 0);
__decorate([
    (0, inversify_1.inject)(preference_service_1.PreferenceService),
    __metadata("design:type", Object)
], InjectablePreferenceProxy.prototype, "preferences", void 0);
__decorate([
    (0, inversify_1.inject)(exports.PreferenceProxySchema),
    __metadata("design:type", Function)
], InjectablePreferenceProxy.prototype, "promisedSchema", void 0);
__decorate([
    (0, inversify_1.inject)(exports.PreferenceProxyFactory),
    __metadata("design:type", Function)
], InjectablePreferenceProxy.prototype, "factory", void 0);
__decorate([
    (0, inversify_1.postConstruct)(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], InjectablePreferenceProxy.prototype, "init", null);
InjectablePreferenceProxy = __decorate([
    (0, inversify_1.injectable)()
], InjectablePreferenceProxy);
exports.InjectablePreferenceProxy = InjectablePreferenceProxy;
//# sourceMappingURL=injectable-preference-proxy.js.map