<template>
    <div ref="editorRef" style="height: 600px"></div>
</template>

<script setup>
    import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue';
    import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';

    const props = defineProps({
        /** v-model */
        modelValue: String,
        /** 语言 */
        language: String,
        /** 主题 */
        theme: String,
        /** 原始内容 */
        original: String,
        /** 原始内容语言 */
        originalLanguage: String,
        /** 配置 */
        config: Object
    });

    const emit = defineEmits(['update:modelValue', 'update:original']);

    /** 编辑器实例 */
    let editorIns = null;
    /** 内容模型 */
    let modifiedModel = null;
    /** 原始内容模型 */
    let originalModel = null;

    /** 编辑器节点 */
    const editorRef = ref(null);

    /** 更新modelValue */
    const updateModelValue = (value) => {
        emit('update:modelValue', value);
    };

    /** 更新original */
    const updateOriginal = (original) => {
        emit('update:original', original);
    };

    /** 渲染编辑器 */
    const render = () => {
        if (!editorRef.value) {
            return;
        }
        editorIns = monaco.editor.createDiffEditor(editorRef.value, {
            theme: props.theme,
            automaticLayout: true,
            ...(props.config || {})
        });
        if (modifiedModel == null) {
            modifiedModel = monaco.editor.createModel(
                props.modelValue || '',
                props.language
            );
            modifiedModel.onDidChangeContent(() => {
                updateModelValue(modifiedModel?.getValue?.());
            });
        }
        if (originalModel == null) {
            originalModel = monaco.editor.createModel(
                props.original || '',
                props.originalLanguage || props.language
            );
            originalModel.onDidChangeContent(() => {
                updateOriginal(originalModel?.getValue?.());
            });
        }
        editorIns.setModel({
            original: originalModel,
            modified: modifiedModel
        });
    };

    /** 销毁编辑器 */
    const destory = () => {
        if (editorIns != null) {
            editorIns.dispose();
            editorIns = null;
        }
    };

    /** 销毁模型 */
    const destoryModel = () => {
        if (modifiedModel != null) {
            modifiedModel.dispose();
            modifiedModel = null;
        }
        if (originalModel != null) {
            originalModel.dispose();
            originalModel = null;
        }
    };

    /** 获取编辑器实例 */
    const getEditorIns = () => {
        return editorIns;
    };

    /** 更新代码语言和编辑器内容 */
    watch(
        [
            () => props.modelValue,
            () => props.language,
            () => props.original,
            () => props.originalLanguage
        ],
        () => {
            if (
                modifiedModel == null ||
                originalModel == null ||
                modifiedModel.getLanguageId() !== props.language ||
                originalModel.getLanguageId() !== props.originalLanguage
            ) {
                destoryModel();
                destory();
                nextTick(() => {
                    render();
                });
                return;
            }
            const content = props.modelValue || '';
            if (content !== modifiedModel.getValue()) {
                modifiedModel.setValue(content);
            }
            const originalContent = props.original || '';
            if (originalContent !== originalModel.getValue()) {
                originalModel.setValue(originalContent);
            }
        }
    );

    /** 更新主题和配置 */
    watch(
        [() => props.theme, () => props.config],
        () => {
            destory();
            nextTick(() => {
                render();
            });
        },
        { deep: true }
    );

    onMounted(() => {
        render();
    });

    onBeforeUnmount(() => {
        destory();
    });

    defineExpose({ getEditorIns });
</script>
