"use strict";
// *****************************************************************************
// Copyright (C) 2023 TypeFox 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-only WITH Classpath-exception-2.0
// *****************************************************************************
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CellOutputWebviewImpl = exports.createCellOutputWebviewContainer = void 0;
const tslib_1 = require("tslib");
const React = require("@theia/core/shared/react");
const inversify_1 = require("@theia/core/shared/inversify");
const uuid_1 = require("@theia/core/lib/common/uuid");
const browser_1 = require("@theia/notebook/lib/browser");
const notebook_cell_model_1 = require("@theia/notebook/lib/browser/view-model/notebook-cell-model");
const webview_1 = require("../../webview/webview");
const browser_2 = require("@theia/core/lib/browser");
const output_webview_internal_1 = require("./output-webview-internal");
const browser_3 = require("@theia/workspace/lib/browser");
const common_1 = require("@theia/notebook/lib/common");
const core_1 = require("@theia/core");
const notebook_model_1 = require("@theia/notebook/lib/browser/view-model/notebook-model");
const CellModel = Symbol('CellModel');
const Notebook = Symbol('NotebookModel');
function createCellOutputWebviewContainer(ctx, cell, notebook) {
    const child = ctx.createChild();
    child.bind(CellModel).toConstantValue(cell);
    child.bind(Notebook).toConstantValue(notebook);
    child.bind(CellOutputWebviewImpl).toSelf().inSingletonScope();
    return child;
}
exports.createCellOutputWebviewContainer = createCellOutputWebviewContainer;
let CellOutputWebviewImpl = class CellOutputWebviewImpl {
    constructor() {
        this.id = (0, uuid_1.generateUuid)();
        this.elementRef = React.createRef();
        this.outputPresentationListeners = new core_1.DisposableCollection();
        this.toDispose = new core_1.DisposableCollection();
    }
    async init() {
        var _a;
        this.editor = this.notebookEditorWidgetService.getNotebookEditor(browser_1.NOTEBOOK_EDITOR_ID_PREFIX + ((_a = common_1.CellUri.parse(this.cell.uri)) === null || _a === void 0 ? void 0 : _a.notebook));
        this.toDispose.push(this.cell.onDidChangeOutputs(outputChange => this.updateOutput(outputChange)));
        this.toDispose.push(this.cell.onDidChangeOutputItems(output => {
            this.updateOutput({ start: this.cell.outputs.findIndex(o => o.outputId === output.outputId), deleteCount: 1, newOutputs: [output] });
        }));
        if (this.editor) {
            this.toDispose.push(this.editor.onDidPostKernelMessage(message => {
                this.webviewWidget.sendMessage({
                    type: 'customKernelMessage',
                    message
                });
            }));
            this.toDispose.push(this.editor.onPostRendererMessage(messageObj => {
                this.webviewWidget.sendMessage({
                    type: 'customRendererMessage',
                    ...messageObj
                });
            }));
        }
        this.webviewWidget = await this.widgetManager.getOrCreateWidget(webview_1.WebviewWidget.FACTORY_ID, { id: this.id });
        this.webviewWidget.setContentOptions({ allowScripts: true });
        this.webviewWidget.setHTML(await this.createWebviewContent());
        this.webviewWidget.onMessage((message) => {
            this.handleWebviewMessage(message);
        });
    }
    render() {
        return React.createElement("div", { className: 'theia-notebook-cell-output-webview', ref: this.elementRef });
    }
    attachWebview() {
        if (this.elementRef.current) {
            this.webviewWidget.processMessage(new browser_2.Message('before-attach'));
            this.elementRef.current.appendChild(this.webviewWidget.node);
            this.webviewWidget.processMessage(new browser_2.Message('after-attach'));
            this.webviewWidget.setIframeHeight(0);
        }
    }
    isAttached() {
        var _a, _b;
        return (_b = (_a = this.elementRef.current) === null || _a === void 0 ? void 0 : _a.contains(this.webviewWidget.node)) !== null && _b !== void 0 ? _b : false;
    }
    updateOutput(update) {
        if (this.webviewWidget.isHidden) {
            this.webviewWidget.show();
        }
        this.outputPresentationListeners.dispose();
        this.outputPresentationListeners = new core_1.DisposableCollection();
        for (const output of this.cell.outputs) {
            this.outputPresentationListeners.push(output.onRequestOutputPresentationChange(() => this.requestOutputPresentationUpdate(output)));
        }
        const updateOutputMessage = {
            type: 'outputChanged',
            newOutputs: update.newOutputs.map(output => ({
                id: output.outputId,
                items: output.outputs.map(item => ({ mime: item.mime, data: item.data.buffer })),
                metadata: output.metadata
            })),
            deleteStart: update.start,
            deleteCount: update.deleteCount
        };
        this.webviewWidget.sendMessage(updateOutputMessage);
    }
    async requestOutputPresentationUpdate(output) {
        const selectedMime = await this.quickPickService.show(output.outputs.map(item => ({ label: item.mime })), { description: core_1.nls.localizeByDefault('Select mimetype to render for current output') });
        if (selectedMime) {
            this.webviewWidget.sendMessage({
                type: 'changePreferredMimetype',
                outputId: output.outputId,
                mimeType: selectedMime.label
            });
        }
    }
    handleWebviewMessage(message) {
        if (!this.editor) {
            throw new Error('No editor found for cell output webview');
        }
        switch (message.type) {
            case 'initialized':
                this.updateOutput({ newOutputs: this.cell.outputs, start: 0, deleteCount: 0 });
                break;
            case 'customRendererMessage':
                this.messagingService.getScoped(this.editor.id).postMessage(message.rendererId, message.message);
                break;
            case 'didRenderOutput':
                this.webviewWidget.setIframeHeight(message.contentHeight + 5);
                break;
            case 'did-scroll-wheel':
                this.editor.node.children[0].children[1].scrollBy(message.deltaX, message.deltaY);
                break;
            case 'customKernelMessage':
                this.editor.recieveKernelMessage(message.message);
                break;
        }
    }
    getPreloads() {
        var _a;
        const kernel = this.notebookKernelService.getSelectedOrSuggestedKernel(this.notebook);
        const kernelPreloads = (_a = kernel === null || kernel === void 0 ? void 0 : kernel.preloadUris.map(uri => uri.toString())) !== null && _a !== void 0 ? _a : [];
        const staticPreloads = this.notebookRendererRegistry.staticNotebookPreloads
            .filter(preload => preload.type === this.notebook.viewType)
            .map(preload => preload.entrypoint);
        return kernelPreloads.concat(staticPreloads);
    }
    async createWebviewContent() {
        const isWorkspaceTrusted = await this.workspaceTrustService.getWorkspaceTrust();
        const preloads = this.preloadsScriptString(isWorkspaceTrusted);
        const content = `
                <html>
                    <head>
                        <meta charset="UTF-8">
                    </head>
                    <body>
                        <script type="module">${preloads}</script>
                    </body>
                </html>
                `;
        return content;
    }
    preloadsScriptString(isWorkspaceTrusted) {
        const ctx = {
            isWorkspaceTrusted,
            rendererData: this.notebookRendererRegistry.notebookRenderers,
            renderOptions: {
                lineLimit: 30,
                outputScrolling: false,
                outputWordWrap: false,
            },
            staticPreloadsData: this.getPreloads()
        };
        // TS will try compiling `import()` in webviewPreloads, so use a helper function instead
        // of using `import(...)` directly
        return `
            const __import = (x) => import(x);
                (${output_webview_internal_1.outputWebviewPreload})(JSON.parse(decodeURIComponent("${encodeURIComponent(JSON.stringify(ctx))}")))`;
    }
    dispose() {
        this.toDispose.dispose();
        this.outputPresentationListeners.dispose();
        this.webviewWidget.dispose();
    }
};
(0, tslib_1.__decorate)([
    (0, inversify_1.inject)(browser_1.NotebookRendererMessagingService),
    (0, tslib_1.__metadata)("design:type", browser_1.NotebookRendererMessagingService)
], CellOutputWebviewImpl.prototype, "messagingService", void 0);
(0, tslib_1.__decorate)([
    (0, inversify_1.inject)(CellModel),
    (0, tslib_1.__metadata)("design:type", notebook_cell_model_1.NotebookCellModel)
], CellOutputWebviewImpl.prototype, "cell", void 0);
(0, tslib_1.__decorate)([
    (0, inversify_1.inject)(Notebook),
    (0, tslib_1.__metadata)("design:type", notebook_model_1.NotebookModel)
], CellOutputWebviewImpl.prototype, "notebook", void 0);
(0, tslib_1.__decorate)([
    (0, inversify_1.inject)(browser_2.WidgetManager),
    (0, tslib_1.__metadata)("design:type", browser_2.WidgetManager)
], CellOutputWebviewImpl.prototype, "widgetManager", void 0);
(0, tslib_1.__decorate)([
    (0, inversify_1.inject)(browser_3.WorkspaceTrustService),
    (0, tslib_1.__metadata)("design:type", browser_3.WorkspaceTrustService)
], CellOutputWebviewImpl.prototype, "workspaceTrustService", void 0);
(0, tslib_1.__decorate)([
    (0, inversify_1.inject)(browser_1.NotebookRendererRegistry),
    (0, tslib_1.__metadata)("design:type", browser_1.NotebookRendererRegistry)
], CellOutputWebviewImpl.prototype, "notebookRendererRegistry", void 0);
(0, tslib_1.__decorate)([
    (0, inversify_1.inject)(browser_1.NotebookEditorWidgetService),
    (0, tslib_1.__metadata)("design:type", browser_1.NotebookEditorWidgetService)
], CellOutputWebviewImpl.prototype, "notebookEditorWidgetService", void 0);
(0, tslib_1.__decorate)([
    (0, inversify_1.inject)(browser_1.NotebookKernelService),
    (0, tslib_1.__metadata)("design:type", browser_1.NotebookKernelService)
], CellOutputWebviewImpl.prototype, "notebookKernelService", void 0);
(0, tslib_1.__decorate)([
    (0, inversify_1.inject)(core_1.QuickPickService),
    (0, tslib_1.__metadata)("design:type", Object)
], CellOutputWebviewImpl.prototype, "quickPickService", void 0);
(0, tslib_1.__decorate)([
    (0, inversify_1.postConstruct)(),
    (0, tslib_1.__metadata)("design:type", Function),
    (0, tslib_1.__metadata)("design:paramtypes", []),
    (0, tslib_1.__metadata)("design:returntype", Promise)
], CellOutputWebviewImpl.prototype, "init", null);
CellOutputWebviewImpl = (0, tslib_1.__decorate)([
    (0, inversify_1.injectable)()
], CellOutputWebviewImpl);
exports.CellOutputWebviewImpl = CellOutputWebviewImpl;
//# sourceMappingURL=cell-output-webview.js.map