<script lang="ts">
    import { onMount, onDestroy, createEventDispatcher } from "svelte";
    import * as monaco from "monaco-editor";
    import type { editor } from "monaco-editor";

    import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
    import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
    import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
    import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
    import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";

    export let value: string = "";
    export let language: string = "html";
    export let theme: string = "vs-dark";
    export let readOnly: boolean = false;

    const dispatch = createEventDispatcher();

    let editorContainer: HTMLDivElement;
    let editorInstance: editor.IStandaloneCodeEditor | null = null;
    let isInternalUpdate = false;

    (self as any).MonacoEnvironment = {
        getWorker: function (_: any, label: string) {
            switch (label) {
                case "json":
                    return new jsonWorker();
                case "css":
                case "scss":
                case "less":
                    return new cssWorker();
                case "html":
                case "handlebars":
                case "razor":
                case "xml":
                    return new htmlWorker();
                case "typescript":
                case "javascript":
                    return new tsWorker();
                default:
                    return new editorWorker();
            }
        },
    };

    onMount(() => {
        editorInstance = monaco.editor.create(editorContainer, {
            value: value,
            language: language,
            theme: theme,
            minimap: {
                enabled: false,
            },
            fontSize: 13,
            fontFamily: '"Monaco", "Menlo", "Ubuntu Mono", monospace',
            lineHeight: 20,
            wordWrap: "on",
            scrollBeyondLastLine: false,
            renderWhitespace: "selection",
            automaticLayout: true,
            tabSize: 2,
            insertSpaces: true,
            formatOnPaste: true,
            formatOnType: true,
            suggestOnTriggerCharacters: true,
            quickSuggestions: {
                other: true,
                comments: false,
                strings: true,
            },
            scrollbar: {
                verticalScrollbarSize: 10,
                horizontalScrollbarSize: 10,
            },
        });

        editorInstance.onDidChangeModelContent(() => {
            if (!isInternalUpdate) {
                const newValue = editorInstance?.getValue() || "";
                dispatch("change", newValue);
            }
        });

        const resizeObserver = new ResizeObserver(() => {
            editorInstance?.layout();
        });
        resizeObserver.observe(editorContainer);

        return () => {
            resizeObserver.disconnect();
        };
    });

    onDestroy(() => {
        editorInstance?.dispose();
    });

    $: if (editorInstance && value !== editorInstance.getValue()) {
        isInternalUpdate = true;
        editorInstance.setValue(value);
        isInternalUpdate = false;
    }

    $: if (editorInstance) {
        const model = editorInstance.getModel();
        if (model) {
            monaco.editor.setModelLanguage(model, language);
        }
    }

    $: if (editorInstance && theme) {
        monaco.editor.setTheme(theme);
    }

    $: if (editorInstance) {
        editorInstance.updateOptions({ readOnly });
    }
</script>

<div bind:this={editorContainer} class="monaco-editor-container"></div>

<style>
    .monaco-editor-container {
        width: 100%;
        height: 100%;
        min-height: 200px;
    }
</style>
