import { Plugin } from "prosemirror-state";
import translate from "../../../../../global/translations";
import { applyImageStyles } from "./node";
const MIN_WIDTH = 10;
export const createImageViewPlugin = (language) => {
    return new Plugin({
        props: {
            nodeViews: {
                image: (node, view, getPos) => {
                    return new ImageView(node, view, getPos, language);
                },
            },
        },
    });
};
class ImageView {
    constructor(node, view, getPos, language) {
        this.createResizeHandle = (position) => {
            const handle = document.createElement('div');
            handle.className = `resize-handle ${position}`;
            handle.setAttribute('role', 'slider');
            handle.setAttribute('aria-label', translate.get('editor-image-view.resize-handle', this.language));
            handle.setAttribute('tabindex', '0');
            handle.setAttribute('aria-valuemin', MIN_WIDTH.toString());
            handle.setAttribute('aria-valuenow', this.img.offsetWidth.toString());
            handle.setAttribute('aria-valuetext', `${this.img.offsetWidth} pixels`);
            handle.setAttribute('aria-grabbed', 'false');
            handle.addEventListener('pointerdown', (e) => {
                handle.setAttribute('aria-grabbed', 'true');
                this.onResizeStart(e, position);
            });
            return handle;
        };
        this.onResizeStart = (event, position) => {
            event.preventDefault();
            const handle = event.target;
            const startX = event.clientX;
            const startWidth = this.img.offsetWidth;
            this.img.style.height = '';
            const onPointerMove = (e) => {
                const delta = e.clientX - startX;
                const widthDelta = position === 'top-left' ? -delta : delta;
                const newWidth = Math.max(MIN_WIDTH, startWidth + widthDelta);
                this.img.style.width = `${newWidth}px`;
                const handles = this.dom.querySelectorAll('.resize-handle');
                for (const resizeHandle of handles) {
                    resizeHandle.setAttribute('aria-valuenow', newWidth.toString());
                    resizeHandle.setAttribute('aria-valuetext', `${newWidth} pixels`);
                }
            };
            const onPointerUp = () => {
                window.removeEventListener('pointermove', onPointerMove);
                window.removeEventListener('pointerup', onPointerUp);
                handle.setAttribute('aria-grabbed', 'false');
                this.persistDimensions();
            };
            window.addEventListener('pointermove', onPointerMove);
            window.addEventListener('pointerup', onPointerUp);
        };
        this.createLoadingState = () => {
            this.dom.setAttribute('aria-live', 'polite');
            this.dom.setAttribute('aria-busy', 'true');
            this.dom.setAttribute('aria-label', translate.get('editor-image-view.loading', this.language, {
                filename: this.node.attrs.alt || 'file',
            }));
            const spinnerElement = document.createElement('limel-linear-progress');
            spinnerElement.setAttribute('indeterminate', 'true');
            this.dom.append(spinnerElement);
        };
        this.createSuccessState = () => {
            this.dom.setAttribute('aria-live', 'polite');
            this.dom.setAttribute('aria-busy', 'false');
            this.dom.setAttribute('aria-label', translate.get('editor-image-view.success', this.language, {
                filename: this.node.attrs.alt || 'file',
            }));
            const bottomRightHandle = this.createResizeHandle('bottom-right');
            const topLeftHandle = this.createResizeHandle('top-left');
            this.dom.append(bottomRightHandle);
            this.dom.append(topLeftHandle);
        };
        this.createFailedState = () => {
            this.dom.setAttribute('aria-live', 'assertive');
            this.dom.setAttribute('aria-busy', 'false');
            this.dom.setAttribute('aria-label', translate.get('editor-image-view.failed', this.language, {
                filename: this.node.attrs.alt || 'file',
            }));
        };
        this.cleanUpPreviousState = () => {
            for (const child of this.dom.childNodes) {
                if (!(child instanceof HTMLImageElement)) {
                    child.remove();
                }
            }
        };
        this.transitionBetweenStates = () => {
            var _a;
            this.cleanUpPreviousState();
            this.dom.className = `image-wrapper state-${this.node.attrs.state}`;
            const stateHandlers = {
                loading: this.createLoadingState,
                success: this.createSuccessState,
                failed: this.createFailedState,
            };
            const state = this.node.attrs.state;
            (_a = stateHandlers[state]) === null || _a === void 0 ? void 0 : _a.call(stateHandlers);
        };
        this.transitioningBetweenSuccessStates = (newNode) => {
            return (this.node.attrs.state === 'success' &&
                newNode.attrs.state === 'success');
        };
        this.node = node;
        this.view = view;
        this.getPos = getPos;
        this.language = language;
        this.dom = document.createElement('div');
        this.dom.className = `image-wrapper state-${node.attrs.state}`;
        this.img = document.createElement('img');
        this.img.src = node.attrs.src;
        this.img.alt = node.attrs.alt;
        applyImageStyles(this.img, node);
        this.img.addEventListener('load', () => {
            this.persistDimensions();
        });
        this.dom.append(this.img);
        this.transitionBetweenStates();
    }
    persistDimensions() {
        this.view.dispatch(this.view.state.tr.setNodeMarkup(this.getPos(), undefined, Object.assign(Object.assign({}, this.node.attrs), { height: `${this.img.offsetHeight}px`, width: `${this.img.offsetWidth}px`, minHeight: `${this.img.offsetHeight}px`, minWidth: `${this.img.offsetWidth}px` })));
    }
    // Ensure that the existing NodeView is reused rather than recreated.
    // Recreating the NodeView will cause flickering between states.
    update(node) {
        if (!this.transitioningBetweenSuccessStates(node)) {
            this.img.src = node.attrs.src;
            this.img.alt = node.attrs.alt;
            applyImageStyles(this.img, node);
        }
        this.node = node;
        this.transitionBetweenStates();
        return true;
    }
}
