"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtensionDocumentDataManagerImpl = void 0;
const ide_core_common_1 = require("@opensumi/ide-core-common");
const vscode_1 = require("../../../../common/vscode");
const converter_1 = require("../../../../common/vscode/converter");
const ext_types_1 = require("../../../../common/vscode/ext-types");
const ext_data_host_1 = require("./ext-data.host");
const OPEN_TEXT_DOCUMENT_TIMEOUT = 5000;
class ExtensionDocumentDataManagerImpl {
    constructor(rpcProtocol) {
        this._documents = new Map();
        this._contentProviders = new Map();
        this._onDidOpenTextDocument = new ide_core_common_1.Emitter();
        this._onDidCloseTextDocument = new ide_core_common_1.Emitter();
        this._onDidChangeTextDocument = new ide_core_common_1.Emitter();
        this._onWillSaveTextDocument = new ide_core_common_1.Emitter();
        this._onDidSaveTextDocument = new ide_core_common_1.Emitter();
        this.onDidOpenTextDocument = this._onDidOpenTextDocument.event;
        this.onDidCloseTextDocument = this._onDidCloseTextDocument.event;
        this.onDidChangeTextDocument = this._onDidChangeTextDocument.event;
        this.onWillSaveTextDocument = this._onWillSaveTextDocument.event;
        this.onDidSaveTextDocument = this._onDidSaveTextDocument.event;
        this.rpcProtocol = rpcProtocol;
        this._proxy = this.rpcProtocol.getProxy(vscode_1.MainThreadAPIIdentifier.MainThreadDocuments);
        this._workspaceProxy = this.rpcProtocol.getProxy(vscode_1.MainThreadAPIIdentifier.MainThreadWorkspace);
    }
    get allDocumentData() {
        return Array.from(this._documents.values());
    }
    // 这里直接转成string，省的在vscode-uri和内部uri之间转换
    getDocumentData(path) {
        const uri = path.toString();
        return this._documents.get(uri);
    }
    getAllDocument() {
        return this.allDocumentData.map((data) => data.document);
    }
    getDocument(uri) {
        const data = this.getDocumentData(uri);
        if (!(data === null || data === void 0 ? void 0 : data.document)) {
            throw new Error(`Unable to retrieve document from URI '${uri}'`);
        }
        return data.document;
    }
    async openTextDocument(uriOrFileNameOrOptions) {
        let uri;
        const options = uriOrFileNameOrOptions;
        if (typeof uriOrFileNameOrOptions === 'string') {
            uri = ext_types_1.Uri.file(uriOrFileNameOrOptions);
        }
        else if (ide_core_common_1.URI.isUri(uriOrFileNameOrOptions)) {
            uri = ext_types_1.Uri.parse(uriOrFileNameOrOptions.toString());
        }
        else if (!options || typeof options === 'object') {
            uri = ext_types_1.Uri.parse(await this._proxy.$tryCreateDocument(options));
        }
        else {
            throw new Error('illegal argument - uriOrFileNameOrOptions');
        }
        const docUrl = (0, ide_core_common_1.normalizeFileUrl)(uri.toString());
        const doc = this._documents.get(docUrl);
        if (doc) {
            return doc.document;
        }
        else {
            await this._proxy.$tryOpenDocument(docUrl);
            const doc = this._documents.get(docUrl);
            if (doc) {
                return doc.document;
            }
            else {
                return new Promise((resolve, reject) => {
                    let resolved = false;
                    setTimeout(() => {
                        if (!resolved) {
                            reject(`Open Text Document ${docUrl} Timeout. Current Timeout is ${OPEN_TEXT_DOCUMENT_TIMEOUT / 1000} seconds.`);
                        }
                    }, OPEN_TEXT_DOCUMENT_TIMEOUT);
                    const disposer = this.onDidOpenTextDocument((document) => {
                        if (docUrl === document.uri.toString()) {
                            resolve(document);
                            disposer.dispose();
                            resolved = true;
                        }
                    });
                });
            }
        }
    }
    registerTextDocumentContentProvider(scheme, provider) {
        let changeDispose;
        const onDidChangeEvent = provider.onDidChange;
        this._contentProviders.set(scheme, provider);
        if (onDidChangeEvent) {
            changeDispose = onDidChangeEvent(async (uri) => {
                const source = new ide_core_common_1.CancellationTokenSource();
                const content = await provider.provideTextDocumentContent(uri, source.token);
                if (content) {
                    this._proxy.$fireTextDocumentChangedEvent(uri.toString(), content);
                }
            });
        }
        this._proxy.$registerDocumentProviderWithScheme(scheme);
        return {
            dispose: () => {
                this._proxy.$unregisterDocumentProviderWithScheme(scheme);
                this._contentProviders.delete(scheme);
                if (changeDispose) {
                    changeDispose.dispose();
                }
            },
        };
    }
    async $provideTextDocumentContent(path, encoding) {
        const uri = ext_types_1.Uri.parse(path);
        const scheme = uri.scheme;
        const provider = this._contentProviders.get(scheme);
        if (provider) {
            // cancellation token 暂时还没接入，以后可能优化
            let content = (await provider.provideTextDocumentContent(uri, new ide_core_common_1.CancellationTokenSource().token)) || '';
            if (content && encoding && !(0, ide_core_common_1.isUTF8)(encoding)) {
                // 默认 encoding 为 UTF8，所以仅在非 UTF8 的情况下做转换
                const buffer = ide_core_common_1.BinaryBuffer.wrap(Buffer.from(content));
                content = buffer.toString(encoding);
            }
            return content;
        }
        throw new Error('new document provider for ' + path);
    }
    $fireModelOptionsChangedEvent(e) {
        const document = this._documents.get(e.uri);
        if (document) {
            if ((0, ide_core_common_1.isDefined)(e.dirty)) {
                document._acceptIsDirty(e.dirty);
            }
            // 和 vscode 表现保持一致，接收到 languages 变更时，发送一个 close 和一个 open 事件
            if ((0, ide_core_common_1.isDefined)(e.languageId) && e.languageId !== document._getLanguageId()) {
                document._acceptLanguageId(e.languageId);
                this._onDidCloseTextDocument.fire(document.document);
                this._onDidOpenTextDocument.fire(document.document);
            }
        }
    }
    $fireModelChangedEvent(e) {
        const { uri, changes, versionId, eol, dirty, isRedoing, isUndoing } = e;
        const document = this._documents.get(uri);
        if (!document) {
            return;
        }
        document._acceptIsDirty(dirty);
        document.onEvents({
            eol,
            versionId,
            changes,
            isRedoing,
            isUndoing,
        });
        let reason;
        if (isRedoing) {
            reason = ext_types_1.TextDocumentChangeReason.Redo;
        }
        else if (isUndoing) {
            reason = ext_types_1.TextDocumentChangeReason.Undo;
        }
        this._onDidChangeTextDocument.fire({
            document: document.document,
            contentChanges: changes.map((change) => ({
                ...change,
                range: (0, converter_1.toRange)(change.range),
            })),
            reason,
        });
    }
    $fireModelOpenedEvent(e) {
        const { uri, eol, languageId, versionId, lines, dirty } = e;
        const document = new ext_data_host_1.ExtHostDocumentData(this._proxy, ext_types_1.Uri.parse(uri), lines, eol, languageId, versionId, dirty);
        this._documents.set(uri, document);
        this._onDidOpenTextDocument.fire(document.document);
    }
    $fireModelRemovedEvent(e) {
        const { uri } = e;
        const document = this._documents.get(uri.toString());
        if (document) {
            this._documents.delete(uri);
            this._onDidCloseTextDocument.fire(document.document);
        }
    }
    $fireModelSavedEvent(e) {
        const { uri } = e;
        const document = this._documents.get(uri);
        if (document) {
            document._acceptIsDirty(false);
            this._onDidSaveTextDocument.fire(document.document);
        }
    }
    async $fireModelWillSaveEvent(e) {
        const { uri } = e;
        const document = this._documents.get(uri);
        if (document) {
            document._acceptIsDirty(e.dirty);
            const promises = [];
            const event = {
                document: document.document,
                reason: e.reason,
                waitUntil: (promise) => {
                    promises.push(this.createWaitUntil(document.document.uri, promise));
                },
            };
            this._onWillSaveTextDocument.fire(event);
            await Promise.all(promises);
            return;
        }
        throw new Error('document not found: ' + uri.toString());
    }
    async createWaitUntil(uri, promise) {
        const res = await promise;
        if (res instanceof Array && res[0] && res[0] instanceof ext_types_1.TextEdit) {
            await this.applyEdit(uri, res.map(converter_1.TextEdit.from)[0]);
        }
    }
    setWordDefinitionFor(modeId, wordDefinition) {
        (0, ext_data_host_1.setWordDefinitionFor)(modeId, wordDefinition);
    }
    applyEdit(uri, edit) {
        const dto = {
            edits: [
                {
                    resource: uri,
                    textEdit: edit,
                    versionId: undefined,
                },
            ],
        };
        return this._workspaceProxy.$tryApplyWorkspaceEdit(dto);
    }
}
exports.ExtensionDocumentDataManagerImpl = ExtensionDocumentDataManagerImpl;
//# sourceMappingURL=doc-manager.host.js.map