import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';
import { useCollaborationContext } from '@lexical/react/LexicalCollaborationContext';
import { CollaborationPlugin } from '@lexical/react/LexicalCollaborationPlugin';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { HashtagPlugin } from '@lexical/react/LexicalHashtagPlugin';
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
import { LexicalNestedComposer } from '@lexical/react/LexicalNestedComposer';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection';
import { mergeRegister } from '@lexical/utils';
import { $getNodeByKey, $getSelection, $isNodeSelection, $isRangeSelection, $setSelection, CLICK_COMMAND, COMMAND_PRIORITY_LOW, createCommand, DRAGSTART_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND, SELECTION_CHANGE_COMMAND, } from 'lexical';
import * as React from 'react';
import { Suspense, useCallback, useEffect, useRef, useState } from 'react';
import { useSettings } from '../context/SettingsContext';
import { useSharedHistoryContext } from '../context/SharedHistoryContext';
import { createWebsocketProvider } from '../lib/collaboration';
import LinkPlugin from '../plugins/LinkPlugin';
import TreeViewPlugin from '../plugins/TreeViewPlugin';
import ContentEditable from '../ui/ContentEditable';
import Placeholder from '../ui/Placeholder';
import { $isImageNode } from './ImageNode';
import { LexicalErrorBoundary } from '../shared/LexicalErrorBoundary';
const imageCache = new Set();
export const RIGHT_CLICK_IMAGE_COMMAND = createCommand('RIGHT_CLICK_IMAGE_COMMAND');
function useSuspenseImage(src) {
    if (!imageCache.has(src)) {
        throw new Promise((resolve) => {
            const img = new Image();
            img.src = src;
            img.onload = () => {
                imageCache.add(src);
                resolve(null);
            };
        });
    }
}
function LazyImage({ altText, className, height, imageRef, maxWidth, src, width, }) {
    useSuspenseImage(src);
    return (React.createElement("img", { ref: imageRef, alt: altText, className: className || undefined, draggable: 'false', src: src, style: {
            height,
            maxWidth,
            width,
        } }));
}
export default function ImageComponent({ altText, caption, height, maxWidth, nodeKey, showCaption, src, width, }) {
    const imageRef = useRef(null);
    const buttonRef = useRef(null);
    const [isSelected, setSelected, clearSelection] = useLexicalNodeSelection(nodeKey);
    const [isResizing] = useState(false);
    const { isCollabActive } = useCollaborationContext();
    const [editor] = useLexicalComposerContext();
    const [selection, setSelection] = useState(null);
    const activeEditorRef = useRef(null);
    const onDelete = useCallback((payload) => {
        if (isSelected && $isNodeSelection($getSelection())) {
            const event = payload;
            event.preventDefault();
            const node = $getNodeByKey(nodeKey);
            if ($isImageNode(node)) {
                node.remove();
            }
        }
        return false;
    }, [isSelected, nodeKey]);
    const onEnter = useCallback((event) => {
        const latestSelection = $getSelection();
        const buttonElem = buttonRef.current;
        if (isSelected && $isNodeSelection(latestSelection) && latestSelection.getNodes().length === 1) {
            if (showCaption) {
                // Move focus into nested editor
                $setSelection(null);
                event.preventDefault();
                caption.focus();
                return true;
            }
            else if (buttonElem !== null && buttonElem !== document.activeElement) {
                event.preventDefault();
                buttonElem.focus();
                return true;
            }
        }
        return false;
    }, [caption, isSelected, showCaption]);
    const onEscape = useCallback((event) => {
        if (activeEditorRef.current === caption || buttonRef.current === event.target) {
            $setSelection(null);
            editor.update(() => {
                setSelected(true);
                const parentRootElement = editor.getRootElement();
                if (parentRootElement !== null) {
                    parentRootElement.focus();
                }
            });
            return true;
        }
        return false;
    }, [caption, editor, setSelected]);
    const onClick = useCallback((payload) => {
        const event = payload;
        if (isResizing) {
            return true;
        }
        if (event.target === imageRef.current) {
            if (event.shiftKey) {
                setSelected(!isSelected);
            }
            else {
                clearSelection();
                setSelected(true);
            }
            return true;
        }
        return false;
    }, [isResizing, isSelected, setSelected, clearSelection]);
    const onRightClick = useCallback((event) => {
        editor.getEditorState().read(() => {
            const latestSelection = $getSelection();
            const domElement = event.target;
            if (domElement.tagName === 'IMG' &&
                $isRangeSelection(latestSelection) &&
                latestSelection.getNodes().length === 1) {
                editor.dispatchCommand(RIGHT_CLICK_IMAGE_COMMAND, event);
            }
        });
    }, [editor]);
    useEffect(() => {
        let isMounted = true;
        const rootElement = editor.getRootElement();
        const unregister = mergeRegister(editor.registerUpdateListener(({ editorState }) => {
            if (isMounted) {
                setSelection(editorState.read(() => $getSelection()));
            }
        }), editor.registerCommand(SELECTION_CHANGE_COMMAND, (_, activeEditor) => {
            activeEditorRef.current = activeEditor;
            return false;
        }, COMMAND_PRIORITY_LOW), editor.registerCommand(CLICK_COMMAND, onClick, COMMAND_PRIORITY_LOW), editor.registerCommand(RIGHT_CLICK_IMAGE_COMMAND, onClick, COMMAND_PRIORITY_LOW), editor.registerCommand(DRAGSTART_COMMAND, (event) => {
            if (event.target === imageRef.current) {
                // TODO This is just a temporary workaround for FF to behave like other browsers.
                // Ideally, this handles drag & drop too (and all browsers).
                event.preventDefault();
                return true;
            }
            return false;
        }, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_DELETE_COMMAND, onDelete, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_BACKSPACE_COMMAND, onDelete, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ENTER_COMMAND, onEnter, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ESCAPE_COMMAND, onEscape, COMMAND_PRIORITY_LOW));
        rootElement === null || rootElement === void 0 ? void 0 : rootElement.addEventListener('contextmenu', onRightClick);
        return () => {
            isMounted = false;
            unregister();
            rootElement === null || rootElement === void 0 ? void 0 : rootElement.removeEventListener('contextmenu', onRightClick);
        };
    }, [
        clearSelection,
        editor,
        isResizing,
        isSelected,
        nodeKey,
        onDelete,
        onEnter,
        onEscape,
        onClick,
        onRightClick,
        setSelected,
    ]);
    const { historyState } = useSharedHistoryContext();
    const { settings: { showNestedEditorTreeView }, } = useSettings();
    const draggable = isSelected && $isNodeSelection(selection) && !isResizing;
    const isFocused = isSelected || isResizing;
    return (React.createElement(Suspense, { fallback: null },
        React.createElement(React.Fragment, null,
            React.createElement("div", { draggable: draggable },
                React.createElement(LazyImage, { altText: altText, className: isFocused ? `focused ${$isNodeSelection(selection) ? 'draggable' : ''}` : null, height: height, imageRef: imageRef, maxWidth: maxWidth, src: src, width: width })),
            showCaption && (React.createElement("div", { className: 'image-caption-container' },
                React.createElement(LexicalNestedComposer, { initialEditor: caption },
                    React.createElement(AutoFocusPlugin, null),
                    React.createElement(LinkPlugin, null),
                    React.createElement(HashtagPlugin, null),
                    isCollabActive ? (React.createElement(CollaborationPlugin, { id: caption.getKey(), providerFactory: createWebsocketProvider, shouldBootstrap: true })) : (React.createElement(HistoryPlugin, { externalHistoryState: historyState })),
                    React.createElement(RichTextPlugin, { contentEditable: React.createElement(ContentEditable, { className: 'ImageNode__contentEditable' }), ErrorBoundary: LexicalErrorBoundary, placeholder: React.createElement(Placeholder, { className: 'ImageNode__placeholder' }, "Enter a caption...") }),
                    showNestedEditorTreeView === true ? React.createElement(TreeViewPlugin, null) : null))))));
}
//# sourceMappingURL=ImageComponent.js.map