import * as monaco from 'monaco-editor';
import * as React from 'react';
import MonacoEditor from 'react-monaco-editor';
import * as _ from 'lodash';
import { createRoot } from 'react-dom/client';
import { loadWASM } from 'onigasm';
import { Registry } from 'monaco-textmate';
import { wireTmGrammars } from 'monaco-editor-textmate';
import { GrammerMapping } from '../languages/languages';
import { StandaloneServices } from 'monaco-editor/esm/vs/editor/standalone/browser/standaloneServices';
import { IThemeService } from 'monaco-editor/esm/vs/platform/theme/common/themeService';
import { AbstractStore } from '@journeyapps-labs/reactor-mod';
export class MonacoStore extends AbstractStore {
    constructor() {
        super({
            name: 'MONACO_STORE'
        });
        this.editors = {};
        this.enabledLanguages = [];
        this.focusedEditor = null;
        this.languages = GrammerMapping;
    }
    registerLanguage(key, language) {
        this.languages[key] = language;
    }
    enableLanguages(languages) {
        this.enabledLanguages = languages;
    }
    getLanguage(languageID) {
        const lang = _.find(this.languages, { monaco: languageID });
        if (!lang) {
            console.error(`could not find language with ID: ${languageID}`);
        }
        return lang;
    }
    getLanguageScope(languageID) {
        return _.findKey(this.languages, { monaco: languageID });
    }
    async init() {
        var _a;
        // setup json
        await loadWASM(require('onigasm/lib/onigasm.wasm'));
        const registry = new Registry({
            getGrammarDefinition: async (scopeName) => {
                if (this.languages[scopeName]) {
                    return {
                        format: this.languages[scopeName].format,
                        content: await (await fetch(this.languages[scopeName].url)).text()
                    };
                }
                console.error('Could not find mapping for textmate scope: ', scopeName);
            }
        });
        // map of monaco "language id's" to TextMate scopeNames
        const grammars = new Map();
        for (let language of this.enabledLanguages) {
            const mapping = this.getLanguage(language);
            grammars.set(language, this.getLanguageScope(language));
            if (mapping.language) {
                monaco.languages.register(Object.assign({ id: mapping.monaco }, mapping.language));
            }
            (_a = mapping.hook) === null || _a === void 0 ? void 0 : _a.call(mapping);
        }
        const themeService = StandaloneServices.get(IThemeService);
        // TextMate gives multiple scopes per token, but Monaco needs a single one.
        // wireTmGrammars needs access to the theme as a hack to get the correct tokens (returns the first token that is
        // matched in the theme).
        // If the editor is not provided, it doesn't break, but non-optimal tokens are returned.
        // We don't want to do this separately for each editor, so we setup a mock instance that provides access
        // to the global theme.
        const mockEditor = {
            _themeService: {
                getColorTheme() {
                    return themeService._theme;
                }
            }
        };
        await wireTmGrammars(monaco, registry, grammars, mockEditor);
        return true;
    }
    async createHeadlessEditorInstance() {
        const m = document.createElement('div');
        m.style.display = 'none';
        document.body.append(m);
        const reactRoot = createRoot(m);
        const editor = await new Promise((resolve) => {
            reactRoot.render(React.createElement(MonacoEditor, { editorDidMount: (editor) => {
                    resolve(editor);
                } }));
        });
        return {
            editor,
            dispose: () => {
                reactRoot.unmount();
                m.remove();
            }
        };
    }
    registerEditor(editor) {
        this.editors[editor.getId()] = editor;
        const listener1 = editor.onDidFocusEditorText(() => {
            this.focusedEditor = editor;
        });
        const listener2 = editor.onDidDispose(() => {
            listener1.dispose();
            listener2.dispose();
        });
        this.iterateListeners((list) => {
            list === null || list === void 0 ? void 0 : list.gotEditor(editor);
        });
    }
    deregisterEditor(editor) {
        delete this.editors[editor.getId()];
        if (this.focusedEditor === editor) {
            this.focusedEditor = null;
        }
    }
}
//# sourceMappingURL=MonacoStore.js.map