import { action, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { NodeSelection } from 'prosemirror-state';
import * as ReactDOM from 'react-dom';
import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { Docs, DocUtils } from '../../../documents/Documents';
import { ColorScheme } from '../../../util/SettingsManager';
import { Transform } from '../../../util/Transform';
import { DocumentView } from '../DocumentView';
import { FormattedTextBox } from './FormattedTextBox';
import React = require('react');

export class DashDocView {
    dom: HTMLSpanElement; // container for label and value

    constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
        this.dom = document.createElement('span');
        this.dom.style.position = 'relative';
        this.dom.style.textIndent = '0';
        this.dom.style.border = '1px solid ' + StrCast(tbox.layoutDoc.color, Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'lightGray');
        this.dom.style.width = node.attrs.width;
        this.dom.style.height = node.attrs.height;
        this.dom.style.display = node.attrs.hidden ? 'none' : 'inline-block';
        (this.dom.style as any).float = node.attrs.float;
        this.dom.onkeypress = function (e: any) {
            e.stopPropagation();
        };
        this.dom.onkeydown = function (e: any) {
            e.stopPropagation();
        };
        this.dom.onkeyup = function (e: any) {
            e.stopPropagation();
        };
        this.dom.onmousedown = function (e: any) {
            e.stopPropagation();
        };

        ReactDOM.render(
            <DashDocViewInternal docid={node.attrs.docid} alias={node.attrs.alias} width={node.attrs.width} height={node.attrs.height} hidden={node.attrs.hidden} fieldKey={node.attrs.fieldKey} tbox={tbox} view={view} node={node} getPos={getPos} />,
            this.dom
        );
        (this as any).dom = this.dom;
    }
    destroy() {
        ReactDOM.unmountComponentAtNode(this.dom);
    }
    selectNode() {}
}

interface IDashDocViewInternal {
    docid: string;
    alias: string;
    tbox: FormattedTextBox;
    width: string;
    height: string;
    hidden: boolean;
    fieldKey: string;
    view: any;
    node: any;
    getPos: any;
}
@observer
export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
    _spanRef = React.createRef<HTMLDivElement>();
    _disposers: { [name: string]: IReactionDisposer } = {};
    _textBox: FormattedTextBox;
    @observable _dashDoc: Doc | undefined;
    @observable _finalLayout: any;
    @observable _resolvedDataDoc: any;
    @observable _width: number = 0;
    @observable _height: number = 0;

    updateDoc = action((dashDoc: Doc) => {
        this._dashDoc = dashDoc;
        this._finalLayout = this.props.docid ? dashDoc : Doc.expandTemplateLayout(Doc.Layout(dashDoc), dashDoc, this.props.fieldKey);

        if (this._finalLayout) {
            if (!Doc.AreProtosEqual(this._finalLayout, dashDoc)) {
                this._finalLayout.rootDocument = dashDoc.aliasOf;
            }
            this._resolvedDataDoc = Cast(this._finalLayout.resolvedDataDoc, Doc, null);
        }
        if (this.props.width !== (this._dashDoc?._width ?? '') + 'px' || this.props.height !== (this._dashDoc?._height ?? '') + 'px') {
            try {
                this._width = NumCast(this._dashDoc?._width);
                this._height = NumCast(this._dashDoc?._height);
                // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made
                this.props.view.dispatch(
                    this.props.view.state.tr.setNodeMarkup(this.props.getPos(), null, {
                        ...this.props.node.attrs,
                        width: this._width + 'px',
                        height: this._height + 'px',
                    })
                );
            } catch (e) {
                console.log('DashDocView:' + e);
            }
        }
    });

    constructor(props: IDashDocViewInternal) {
        super(props);
        this._textBox = this.props.tbox;

        DocServer.GetRefField(this.props.docid + this.props.alias).then(async dashDoc => {
            if (!(dashDoc instanceof Doc)) {
                this.props.alias &&
                    DocServer.GetRefField(this.props.docid).then(async dashDocBase => {
                        if (dashDocBase instanceof Doc) {
                            const aliasedDoc = Doc.MakeAlias(dashDocBase, this.props.docid + this.props.alias);
                            aliasedDoc.layoutKey = 'layout';
                            this.props.fieldKey && DocUtils.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, this.props.fieldKey, undefined);
                            this.updateDoc(aliasedDoc);
                        }
                    });
            } else {
                this.updateDoc(dashDoc);
            }
        });
    }

    componentDidMount() {
        this._disposers.upater = reaction(
            () => this._dashDoc && NumCast(this._dashDoc._height) + NumCast(this._dashDoc._width),
            action(() => {
                if (this._dashDoc) {
                    this._width = NumCast(this._dashDoc._width);
                    this._height = NumCast(this._dashDoc._height);
                    const parent = this._spanRef.current?.parentNode as HTMLElement;
                    if (parent) {
                        parent.style.width = this._width + 'px';
                        parent.style.height = this._height + 'px';
                    }
                }
            })
        );
    }

    removeDoc = () => {
        this.props.view.dispatch(this.props.view.state.tr.setSelection(new NodeSelection(this.props.view.state.doc.resolve(this.props.getPos()))).deleteSelection());
        return true;
    };

    getDocTransform = () => {
        if (!this._spanRef.current) return Transform.Identity();
        const { scale, translateX, translateY } = Utils.GetScreenTransform(this._spanRef.current);
        return new Transform(-translateX, -translateY, 1).scale(1 / scale);
    };
    outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document, {}); // ideally, this would scroll to show the focus target

    onKeyDown = (e: any) => {
        e.stopPropagation();
        if (e.key === 'Tab' || e.key === 'Enter') {
            e.preventDefault();
        }
    };

    onPointerLeave = () => {
        const ele = document.getElementById('DashDocCommentView-' + this.props.docid) as HTMLDivElement;
        ele && (ele.style.backgroundColor = '');
    };

    onPointerEnter = () => {
        const ele = document.getElementById('DashDocCommentView-' + this.props.docid) as HTMLDivElement;
        ele && (ele.style.backgroundColor = 'orange');
    };

    componentWillUnmount = () => Object.values(this._disposers).forEach(disposer => disposer?.());

    render() {
        return !this._dashDoc || !this._finalLayout || this.props.hidden ? null : (
            <div
                ref={this._spanRef}
                className="dash-span"
                style={{
                    width: this._width,
                    height: this._height,
                    position: 'absolute',
                    display: 'inline-block',
                }}
                onPointerLeave={this.onPointerLeave}
                onPointerEnter={this.onPointerEnter}
                onKeyDown={this.onKeyDown}
                onKeyPress={e => e.stopPropagation()}
                onKeyUp={e => e.stopPropagation()}
                onWheel={e => e.preventDefault()}>
                <DocumentView
                    Document={this._finalLayout}
                    DataDoc={this._resolvedDataDoc}
                    addDocument={returnFalse}
                    rootSelected={this._textBox.props.isSelected}
                    removeDocument={this.removeDoc}
                    isDocumentActive={returnFalse}
                    isContentActive={this._textBox.props.isContentActive}
                    styleProvider={this._textBox.props.styleProvider}
                    docViewPath={this._textBox.props.docViewPath}
                    ScreenToLocalTransform={this.getDocTransform}
                    addDocTab={this._textBox.props.addDocTab}
                    pinToPres={returnFalse}
                    renderDepth={this._textBox.props.renderDepth + 1}
                    PanelWidth={this._finalLayout[WidthSym]}
                    PanelHeight={this._finalLayout[HeightSym]}
                    focus={this.outerFocus}
                    whenChildContentsActiveChanged={returnFalse}
                    bringToFront={emptyFunction}
                    dontRegisterView={false}
                    docFilters={this.props.tbox?.props.docFilters}
                    docRangeFilters={this.props.tbox?.props.docRangeFilters}
                    searchFilterDocs={this.props.tbox?.props.searchFilterDocs}
                    ContainingCollectionView={this._textBox.props.ContainingCollectionView}
                    ContainingCollectionDoc={this._textBox.props.ContainingCollectionDoc}
                />
            </div>
        );
    }
}
