"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WalkthroughsService = void 0;
const tslib_1 = require("tslib");
const di_1 = require("@opensumi/di");
const ide_core_browser_1 = require("@opensumi/ide-core-browser");
const ide_core_common_1 = require("@opensumi/ide-core-common");
const ide_file_service_1 = require("@opensumi/ide-file-service");
const file_service_client_1 = require("@opensumi/ide-file-service/lib/browser/file-service-client");
const path_1 = require("@opensumi/ide-utils/lib/path");
const contextkey_1 = require("@opensumi/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey");
const common_1 = require("../common");
const types_1 = require("./types");
const parseDescription = (desc) => desc
    .split('\n')
    .filter((x) => x)
    .map((text) => (0, ide_core_browser_1.parseLinkedText)(text));
let WalkthroughsService = class WalkthroughsService extends ide_core_browser_1.Disposable {
    constructor() {
        super();
        this._onDidOpenWalkthrough = new ide_core_browser_1.Emitter();
        this.onDidOpenWalkthrough = this._onDidOpenWalkthrough.event;
        this._onDidAddWalkthrough = new ide_core_browser_1.Emitter();
        this.onDidAddWalkthrough = this._onDidAddWalkthrough.event;
        this._onDidProgressStep = new ide_core_browser_1.Emitter();
        this.onDidProgressStep = this._onDidProgressStep.event;
        this.sessionEvents = new Set();
        this.categoryVisibilityContextKeys = new Set();
        this.stepCompletionContextKeyExpressions = new Set();
        this.stepCompletionContextKeys = new Set();
        this.completionListeners = new Map();
        this.contributions = new Map();
        this.steps = new Map();
        this.extensionSteps = new Map();
        this.stepProgress = {};
        this.initCompletionEventListeners();
    }
    /**
     * 注册各种 completionEvents 的监听
     * 可能来自 command 命令调用完成后发出、onView 视图切换后发出等
     */
    initCompletionEventListeners() {
        this.addDispose(this.commandService.onDidExecuteCommand(({ commandId }) => {
            this.progressByEvent(`${common_1.CompletionEventsType.onCommand}:${commandId}`);
        }));
        this.addDispose(this.contextKeyService.onDidChangeContext(({ payload }) => {
            if (payload.affectsSome(this.stepCompletionContextKeys)) {
                this.stepCompletionContextKeyExpressions.forEach((expression) => {
                    if (payload.affectsSome(new Set(expression.keys())) && this.contextKeyService.match(expression)) {
                        this.progressByEvent(common_1.CompletionEventsType.onContext + ':' + expression.serialize());
                    }
                });
            }
        }));
        this.addDispose(this.eventBus.on(ide_core_common_1.ExtensionActivateEvent, ({ payload: { topic, data } }) => {
            if (topic === common_1.CompletionEventsType.onView) {
                this.progressByEvent(common_1.CompletionEventsType.onView + ':' + data);
            }
        }));
        this.addDispose(this.preferenceService.onPreferenceChanged(({ preferenceName }) => {
            if (preferenceName) {
                this.progressByEvent(common_1.CompletionEventsType.onSettingChanged + ':' + preferenceName);
            }
        }));
    }
    /**
     * 处理 completionEvents
     * 该事件主要用于处理当用户做了某些操作之后给当前 step 设置成 completion 的状态（也就是左边的 checkbox 打勾勾）
     */
    registerDoneListeners(step) {
        var _a;
        if (!step.completionEvents.length) {
            step.completionEvents.push(common_1.CompletionEventsType.stepSelected);
        }
        for (let event of step.completionEvents) {
            const [_, eventType, argument] = (_a = /^([^:]*):?(.*)$/.exec(event)) !== null && _a !== void 0 ? _a : [];
            if (!eventType) {
                this.logger.error(`Unknown completionEvent ${event} when registering step ${step.id}`);
                continue;
            }
            switch (eventType) {
                case common_1.CompletionEventsType.onLink:
                case common_1.CompletionEventsType.onEvent:
                case common_1.CompletionEventsType.onView:
                case common_1.CompletionEventsType.onSettingChanged:
                    break;
                case common_1.CompletionEventsType.onContext: {
                    const expression = contextkey_1.ContextKeyExpr.deserialize(argument);
                    if (expression) {
                        this.stepCompletionContextKeyExpressions.add(expression);
                        expression.keys().forEach((key) => this.stepCompletionContextKeys.add(key));
                        event = eventType + ':' + expression.serialize();
                        if (this.contextKeyService.match(expression)) {
                            this.sessionEvents.add(event);
                        }
                    }
                    else {
                        this.logger.error(`Unable to parse context key expression: ${expression} in walkthrough step ${step.id}`);
                    }
                    break;
                }
                case common_1.CompletionEventsType.onStepSelected:
                case common_1.CompletionEventsType.stepSelected:
                    event = common_1.CompletionEventsType.stepSelected + ':' + step.id;
                    break;
                case common_1.CompletionEventsType.onCommand:
                    event = eventType + ':' + argument.replace(/^toSide:/, '');
                    break;
                case common_1.CompletionEventsType.onExtensionInstalled:
                case common_1.CompletionEventsType.extensionInstalled:
                    event = common_1.CompletionEventsType.extensionInstalled + ':' + argument.toLowerCase();
                    break;
                default:
                    this.logger.error(`${event} Unknown`);
                    continue;
            }
            this.registerCompletionListener(event, step);
            if (this.sessionEvents.has(event)) {
                this.progressStep(step.id);
            }
        }
    }
    getStep(id) {
        const step = this.steps.get(id);
        return step;
    }
    registerCompletionListener(event, step) {
        var _a;
        if (!this.completionListeners.has(event)) {
            this.completionListeners.set(event, new Set());
        }
        (_a = this.completionListeners.get(event)) === null || _a === void 0 ? void 0 : _a.add(step.id);
    }
    registerWalkthrough(descriptor) {
        const pre = this.contributions.get(descriptor.id);
        if (pre) {
            return;
        }
        this.contributions.set(descriptor.id, descriptor);
        descriptor.steps.forEach((step) => {
            if (this.steps.has(step.id)) {
                this.logger.error(`${step.id} Repeat`);
                return;
            }
            this.steps.set(step.id, step);
            step.when.keys().forEach((key) => this.categoryVisibilityContextKeys.add(key));
            this.registerDoneListeners(step);
        });
        descriptor.when.keys().forEach((key) => this.categoryVisibilityContextKeys.add(key));
    }
    getStepProgress(step) {
        return {
            ...step,
            done: false,
            ...this.stepProgress[step.id],
        };
    }
    progressByEvent(event) {
        var _a;
        if (this.sessionEvents.has(event)) {
            return;
        }
        this.sessionEvents.add(event);
        (_a = this.completionListeners.get(event)) === null || _a === void 0 ? void 0 : _a.forEach((id) => this.progressStep(id));
    }
    progressStep(id) {
        const preProgress = this.stepProgress[id];
        if (!preProgress || preProgress.done !== true) {
            this.stepProgress[id] = { done: true };
            const step = this.getStep(id);
            if (!step) {
                return;
            }
            this._onDidProgressStep.fire(this.getStepProgress(step));
        }
    }
    openWalkthroughEditor(id) {
        this.stepProgress = {};
        this._onDidOpenWalkthrough.fire(id);
    }
    getWalkthrough(id) {
        return this.contributions.get(id);
    }
    getStepsByExtension(id) {
        return this.extensionSteps.get(id);
    }
    getWalkthroughs() {
        return Array.from(this.contributions.values());
    }
    // 直接读取插件本身的本地资源
    async getFileContent(extensionId, uri) {
        const empty = ide_core_common_1.BinaryBuffer.alloc(0);
        const extension = this.extensionManageService.getExtensionInstanceByExtId(extensionId);
        if (!extension || !extension.uri) {
            return empty;
        }
        const path = ide_core_browser_1.URI.from(extension.uri).resolve(uri).toString();
        const stat = await this.fileSystem.getFileStat(path);
        if (stat && stat.type === ide_core_common_1.FileType.File) {
            const { content } = await this.fileSystem.readFile(path);
            return content;
        }
        return empty;
    }
    async registerExtensionWalkthroughContributions(extensionId, walkthrough) {
        var _a, _b;
        const extensionDescriptor = this.extensionManageService.getExtensionInstanceByExtId(extensionId);
        if (!extensionDescriptor) {
            return;
        }
        const convertFileURI = (path) => path.startsWith('https://') ? ide_core_browser_1.URI.parse(path) : ide_core_browser_1.URI.from(extensionDescriptor.extensionLocation).resolve(path);
        const convertFileURIWithTheme = (path) => {
            var _a;
            if (typeof path === 'string') {
                const converted = convertFileURI(path);
                return { hcDark: converted, hcLight: converted, dark: converted, light: converted };
            }
            else {
                return {
                    hcDark: convertFileURI(path.hc),
                    hcLight: convertFileURI((_a = path.hcLight) !== null && _a !== void 0 ? _a : path.light),
                    light: convertFileURI(path.light),
                    dark: convertFileURI(path.dark),
                };
            }
        };
        const categoryID = extensionId + '#' + walkthrough.id;
        const steps = ((_a = walkthrough.steps) !== null && _a !== void 0 ? _a : [])
            .filter((step) => {
            if (!step.media) {
                this.logger.error('missing media in walkthrough step: ' + walkthrough.id + '@' + step.id);
                return;
            }
            if (!(step.media.markdown || step.media.svg || step.media.image)) {
                this.logger.error('Unknown walkthrough format detected for: ' + extensionId + '#' + walkthrough.id + '#' + step.id);
                return;
            }
            return step;
        })
            .map((step, index) => {
            var _a, _b, _c;
            const description = parseDescription(step.description || '');
            const fullyQualifiedID = extensionId + '#' + walkthrough.id + '#' + step.id;
            let media;
            if (step.media.image) {
                const altText = step.media.altText;
                media = { type: 'image', altText, path: convertFileURIWithTheme(step.media.image) };
            }
            else if (step.media.markdown) {
                media = {
                    type: 'markdown',
                    path: convertFileURI(step.media.markdown),
                    base: convertFileURI((0, path_1.dirname)(step.media.markdown)),
                    root: ide_core_browser_1.URI.from(extensionDescriptor.extensionLocation),
                };
            }
            else if (step.media.svg) {
                media = {
                    type: 'svg',
                    path: convertFileURI(step.media.svg),
                    altText: step.media.svg,
                };
            }
            this.extensionSteps.set(fullyQualifiedID, step);
            return {
                description,
                media: media,
                completionEvents: (_b = (_a = step.completionEvents) === null || _a === void 0 ? void 0 : _a.filter((x) => typeof x === 'string')) !== null && _b !== void 0 ? _b : [],
                id: fullyQualifiedID,
                title: step.title,
                when: (_c = contextkey_1.ContextKeyExpr.deserialize(step.when)) !== null && _c !== void 0 ? _c : contextkey_1.ContextKeyExpr.true(),
                category: categoryID,
                order: index,
            };
        });
        let icon = walkthrough.icon
            ? ide_core_browser_1.URI.from(extensionDescriptor.extensionLocation).resolve(walkthrough.icon)
            : extensionDescriptor.icon;
        if (!icon) {
            icon = await extensionDescriptor.getDefaultIcon();
        }
        const walkthoughDescriptor = {
            description: walkthrough.description,
            title: walkthrough.title,
            id: categoryID,
            isFeatured: false,
            source: extensionId,
            order: 0,
            steps,
            icon: {
                type: 'image',
                path: icon,
            },
            when: (_b = contextkey_1.ContextKeyExpr.deserialize(walkthrough.when)) !== null && _b !== void 0 ? _b : contextkey_1.ContextKeyExpr.true(),
        };
        this.registerWalkthrough(walkthoughDescriptor);
        this._onDidAddWalkthrough.fire(walkthoughDescriptor);
    }
};
exports.WalkthroughsService = WalkthroughsService;
tslib_1.__decorate([
    (0, di_1.Autowired)(types_1.AbstractExtInstanceManagementService),
    tslib_1.__metadata("design:type", types_1.AbstractExtInstanceManagementService)
], WalkthroughsService.prototype, "extensionManageService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_common_1.CommandService),
    tslib_1.__metadata("design:type", Object)
], WalkthroughsService.prototype, "commandService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_browser_1.PreferenceService),
    tslib_1.__metadata("design:type", Object)
], WalkthroughsService.prototype, "preferenceService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_file_service_1.IFileServiceClient),
    tslib_1.__metadata("design:type", file_service_client_1.FileServiceClient)
], WalkthroughsService.prototype, "fileSystem", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_browser_1.IContextKeyService),
    tslib_1.__metadata("design:type", Object)
], WalkthroughsService.prototype, "contextKeyService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_common_1.IEventBus),
    tslib_1.__metadata("design:type", Object)
], WalkthroughsService.prototype, "eventBus", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_common_1.ILogger),
    tslib_1.__metadata("design:type", Object)
], WalkthroughsService.prototype, "logger", void 0);
exports.WalkthroughsService = WalkthroughsService = tslib_1.__decorate([
    (0, di_1.Injectable)({ multiple: false }),
    tslib_1.__metadata("design:paramtypes", [])
], WalkthroughsService);
//# sourceMappingURL=walkthroughs.service.js.map