/*
This file is part of the Notesnook project (https://notesnook.com/)

Copyright (C) 2023 Streetwriters (Private) Limited

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
import { createDocument, resolveFocusPosition } from "@tiptap/core";
import { useEffect, useMemo, useRef, useState } from "react";
import { Editor } from "../types";
import { EditorState } from "@tiptap/pm/state";
function useForceUpdate() {
    const [, setValue] = useState(0);
    return () => setValue((value) => value + 1);
}
export const useEditor = (options = {}, deps = []) => {
    const editor = useMemo(() => new Editor(options), []);
    const forceUpdate = useForceUpdate();
    const editorRef = useRef(editor);
    useEffect(() => {
        var _a, _b;
        if (editor.view.isDestroyed)
            return;
        let isMounted = true;
        let updateTimeout;
        const oldContent = editor.options.content;
        editor.options = Object.assign(Object.assign({}, editor.options), options);
        (_a = options.onBeforeCreate) === null || _a === void 0 ? void 0 : _a.call(options, { editor });
        // we try very hard not to create a new editor, instead
        // we just update the props & other things. This is dangerous but faster
        // than creating a new editor
        // This part below is copied from @tiptap/core
        if (options.editorProps)
            editor.view.setProps(options.editorProps);
        if (options.content !== undefined && options.content !== oldContent) {
            const doc = createDocument(options.content, editor.schema, options.parseOptions);
            const selection = editor.state.selection ||
                resolveFocusPosition(doc, options.autofocus);
            const oldIsFocused = editor.isFocused;
            editor.view.updateState(EditorState.create({
                doc,
                plugins: editor.extensionManager.plugins,
                selection: selection.from > 0 &&
                    selection.from <= doc.content.size &&
                    selection.to > 0 &&
                    selection.to <= doc.content.size
                    ? selection
                    : undefined
            }));
            if (oldIsFocused && !editor.isFocused)
                editor.commands.focus();
        }
        (_b = options.onCreate) === null || _b === void 0 ? void 0 : _b.call(options, { editor: editor });
        function onTransaction({ editor }) {
            editorRef.current = editor;
            clearTimeout(updateTimeout);
            updateTimeout = setTimeout(() => {
                if (isMounted) {
                    forceUpdate();
                }
            }, 200);
        }
        editor.on("transaction", onTransaction);
        return () => {
            isMounted = false;
            editor.off("transaction", onTransaction);
            clearTimeout(updateTimeout);
        };
    }, 
    // eslint-disable-next-line react-hooks/exhaustive-deps
    deps);
    useEffect(() => {
        return () => {
            editor.view.destroy();
            editor.destroy();
        };
    }, [editor]);
    useEffect(() => {
        // this is required for the drag/drop to work properly
        // in the editor.
        function onDragEnter(event) {
            if (editor.view.dragging) {
                event.preventDefault();
                return true;
            }
        }
        editor.view.dom.addEventListener("dragenter", onDragEnter);
        return () => {
            editor.view.dom.removeEventListener("dragenter", onDragEnter);
        };
    }, [editor.view.dom, editor.view.dragging]);
    return editor;
};
