import { jsx as _jsx } from "react/jsx-runtime";
import debounce from 'debounce';
import { useEffect, useMemo, useRef } from 'react';
import { EditorView } from '@codemirror/view';
import { useSafeLayoutEffect } from '@vertesia/ui/core';
import { getSharedEditorState } from './CodemirrorStateSingleton';
export class EditorApi {
    editor;
    constructor(editor) {
        this.editor = editor;
    }
    setValue(value) {
        this.editor.dispatch({
            changes: {
                from: 0,
                to: this.editor.state.doc.length,
                insert: value || "",
            },
        });
    }
    getValue() {
        return this.editor.state.doc.toString();
    }
}
const customTheme = EditorView.theme({
    "&": { fontSize: "14px", fontFamily: "monospace" },
    ".cm-content": { fontFamily: "inherit" },
    ".cm-scroller": { fontFamily: "inherit" },
    ".cm-gutters": { backgroundColor: "transparent", color: "#ddd" },
    ".cm-focused": { outline: "none" },
    ".cm-cursor": {
        borderLeft: "2px solid #808080",
        borderLeftColor: "#808080",
        animation: "blink 1.2s step-start infinite"
    },
    "@keyframes blink": { "50%": { opacity: 0 } }
});
const baseExtensions = [
    customTheme,
    EditorView.lineWrapping,
];
function updateListenerExtension(cb) {
    return EditorView.updateListener.of((update) => {
        if (update.docChanged) {
            cb(update);
        }
    });
}
;
/**
 * The className is only used on the first rendering - the editor class will not be updated if the property changes
**/
export function CodeMirrorEditor({ onChange, value, className, editorRef, extensions, debounceTimeout = 0 }) {
    const ref = useRef(null);
    // the onChange property may change so we need to store it in a
    // reference to be able to update it when it changes without
    // re-registering the listener on codemirror since codemirror can only register
    // listeners at creation
    const onChangeRef = useRef(undefined);
    useEffect(() => {
        if (onChange) {
            onChangeRef.current = debounceTimeout > 0
                ? debounce(onChange, debounceTimeout)
                : onChange;
            return () => {
                // WE MUST NOT CANCEL THE DEBOUNCE even if we unset the onChange impl
                // otherwise some updates will never happens
                onChangeRef.current = undefined;
            };
        }
    }, [onChange, debounceTimeout]);
    const component = useMemo(() => _jsx("div", { ref: ref, className: className }), [className]);
    useSafeLayoutEffect(() => {
        if (ref.current) {
            let actualExtensions = [...baseExtensions, updateListenerExtension((update) => {
                    onChangeRef.current?.(update);
                })];
            if (Array.isArray(extensions)) {
                actualExtensions = actualExtensions.concat(extensions);
            }
            else if (extensions) {
                actualExtensions.push(extensions);
            }
            const sharedState = getSharedEditorState(value, actualExtensions);
            const editor = new EditorView({
                state: sharedState,
                parent: ref.current,
            });
            const api = new EditorApi(editor);
            if (editorRef) {
                editorRef.current = api;
            }
            return () => {
                editor.destroy();
                if (editorRef) {
                    editorRef.current = undefined;
                }
            };
        }
    }, [component, extensions]);
    return component;
}
//# sourceMappingURL=CodeMirrorEditor.js.map