import { EditorView } from 'prosemirror-view';
import { EditorState } from 'prosemirror-state';
import { keymap } from 'prosemirror-keymap';
import { baseKeymap, toggleMark } from 'prosemirror-commands';
import { schema } from './schema_rts';
import { redo, undo } from 'prosemirror-history';
import { StepMap } from 'prosemirror-transform';

export class FootnoteView {
    innerView: any;
    outerView: any;
    node: any;
    dom: any;
    getPos: any;

    constructor(node: any, view: any, getPos: any) {
        // We'll need these later
        this.node = node;
        this.outerView = view;
        this.getPos = getPos;

        // The node's representation in the editor (empty, for now)
        this.dom = document.createElement('footnote');

        this.dom.addEventListener('pointerup', this.toggle, true);
        // These are used when the footnote is selected
        this.innerView = null;
    }

    selectNode() {
        this.dom.classList.add('ProseMirror-selectednode');
        if (!this.innerView) this.open();
    }

    deselectNode() {
        this.dom.classList.remove('ProseMirror-selectednode');
        if (this.innerView) this.close();
    }

    open() {
        // Append a tooltip to the outer node
        const tooltip = this.dom.appendChild(document.createElement('div'));
        tooltip.className = 'footnote-tooltip';
        // And put a sub-ProseMirror into that
        this.innerView = new EditorView(tooltip, {
            // You can use any node as an editor document
            state: EditorState.create({
                doc: this.node,
                plugins: [
                    keymap(baseKeymap),
                    keymap({
                        'Mod-z': () => undo(this.outerView.state, this.outerView.dispatch),
                        'Mod-y': () => redo(this.outerView.state, this.outerView.dispatch),
                        'Mod-b': toggleMark(schema.marks.strong),
                    }),
                    // new Plugin({
                    //     view(newView) {
                    //         // TODO -- make this work with RichTextMenu
                    //         // return FormattedTextBox.getToolTip(newView);
                    //     }
                    // })
                ],
            }),
            // This is the magic part
            dispatchTransaction: this.dispatchInner.bind(this),
            handleDOMEvents: {
                pointerdown: ((view: any, e: PointerEvent) => {
                    // Kludge to prevent issues due to the fact that the whole
                    // footnote is node-selected (and thus DOM-selected) when
                    // the parent editor is focused.
                    e.stopPropagation();
                    document.addEventListener('pointerup', this.ignore, true);
                    if (this.outerView.hasFocus()) this.innerView.focus();
                }) as any,
            },
        });
        setTimeout(() => this.innerView?.docView.setSelection(0, 0, this.innerView.root, true), 0);
    }

    ignore = (e: PointerEvent) => {
        e.stopPropagation();
        document.removeEventListener('pointerup', this.ignore, true);
    };

    toggle = () => {
        console.log('TOGGLE');
        if (this.innerView) this.close();
        else this.open();
    };

    close() {
        console.log('CLOSE');
        this.innerView?.destroy();
        this.innerView = null;
        this.dom.textContent = '';
    }

    dispatchInner(tr: any) {
        const { state, transactions } = this.innerView.state.applyTransaction(tr);
        this.innerView.updateState(state);

        if (!tr.getMeta('fromOutside')) {
            const outerTr = this.outerView.state.tr,
                offsetMap = StepMap.offset(this.getPos() + 1);
            for (const transaction of transactions) {
                for (const step of transaction.steps) {
                    outerTr.step(step.map(offsetMap));
                }
            }
            if (outerTr.docChanged) this.outerView.dispatch(outerTr);
        }
    }

    update(node: any) {
        if (!node.sameMarkup(this.node)) return false;
        this.node = node;
        if (this.innerView) {
            const state = this.innerView.state;
            const start = node.content.findDiffStart(state.doc.content);
            if (start !== null) {
                let { a: endA, b: endB } = node.content.findDiffEnd(state.doc.content);
                const overlap = start - Math.min(endA, endB);
                if (overlap > 0) {
                    endA += overlap;
                    endB += overlap;
                }
                this.innerView.dispatch(state.tr.replace(start, endB, node.slice(start, endA)).setMeta('fromOutside', true));
            }
        }
        return true;
    }

    destroy() {
        if (this.innerView) this.close();
    }

    stopEvent(event: any) {
        return this.innerView?.dom.contains(event.target);
    }

    ignoreMutation() {
        return true;
    }
}
