import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, Opt } from '../../../fields/Doc';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { emptyFunction, returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { SnappingManager } from '../../util/SnappingManager';
import { undoBatch } from '../../util/UndoManager';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
import { StyleProp } from '../StyleProvider';
import './ComparisonBox.scss';
import { DocumentView, DocumentViewProps } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { PinProps, PresBox } from './trails';
import React = require('react');

@observer
export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(ComparisonBox, fieldKey);
    }
    protected _multiTouchDisposer?: import('../../util/InteractionUtils').InteractionUtils.MultiTouchEventDisposer | undefined;
    private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined];

    @observable _animating = '';

    @computed get clipWidth() {
        return NumCast(this.layoutDoc[this.clipWidthKey], 50);
    }
    get clipWidthKey() {
        return '_' + this.props.fieldKey + '_clipWidth';
    }

    componentDidMount() {
        this.props.setContentView?.(this);
    }

    protected createDropTarget = (ele: HTMLDivElement | null, fieldKey: string, disposerId: number) => {
        this._disposers[disposerId]?.();
        if (ele) {
            // create disposers identified by disposerId to remove drag & drop listeners
            this._disposers[disposerId] = DragManager.MakeDropTarget(ele, (e, dropEvent) => this.internalDrop(e, dropEvent, fieldKey), this.layoutDoc);
        }
    };

    @undoBatch
    private internalDrop = (event: Event, dropEvent: DragManager.DropEvent, fieldKey: string) => {
        if (dropEvent.complete.docDragData) {
            event.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
            const droppedDocs = dropEvent.complete.docDragData?.droppedDocuments;
            droppedDocs.lastElement().embedContainer = this.dataDoc;
            this.dataDoc[fieldKey] = droppedDocs.lastElement();
        }
    };

    private registerSliding = (e: React.PointerEvent<HTMLDivElement>, targetWidth: number) => {
        e.button !== 2 &&
            setupMoveUpEvents(
                this,
                e,
                this.onPointerMove,
                emptyFunction,
                action(() => {
                    // on click, animate slider movement to the targetWidth
                    this._animating = 'all 200ms';
                    this.layoutDoc[this.clipWidthKey] = (targetWidth * 100) / this.props.PanelWidth();
                    setTimeout(
                        action(() => (this._animating = '')),
                        200
                    );
                }),
                false
            );
    };

    @action
    private onPointerMove = ({ movementX }: PointerEvent) => {
        const width = movementX * this.props.ScreenToLocalTransform().Scale + (this.clipWidth / 100) * this.props.PanelWidth();
        if (width && width > 5 && width < this.props.PanelWidth()) {
            this.layoutDoc[this.clipWidthKey] = (width * 100) / this.props.PanelWidth();
        }
        return false;
    };

    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
        const anchor = Docs.Create.ComparisonConfigDocument({
            title: 'ImgAnchor:' + this.rootDoc.title,
            // set presentation timing properties for restoring view
            presTransition: 1000,
            annotationOn: this.rootDoc,
        });
        if (anchor) {
            if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
            /* addAsAnnotation &&*/ this.addDocument(anchor);
            PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), clippable: true } }, this.rootDoc);
            return anchor;
        }
        return this.rootDoc;
    };

    @undoBatch
    clearDoc = (e: React.MouseEvent, fieldKey: string) => {
        e.stopPropagation; // prevent click event action (slider movement) in registerSliding
        delete this.dataDoc[fieldKey];
    };

    docStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
        if (property === StyleProp.PointerEvents) return 'none';
        return this.props.styleProvider?.(doc, props, property);
    };

    render() {
        const clearButton = (which: string) => {
            return (
                <div
                    className={`clear-button ${which}`}
                    onPointerDown={e => e.stopPropagation()} // prevent triggering slider movement in registerSliding
                    onClick={e => this.clearDoc(e, which)}>
                    <FontAwesomeIcon className={`clear-button ${which}`} icon={'times'} size="sm" />
                </div>
            );
        };
        const displayDoc = (which: string) => {
            const whichDoc = Cast(this.dataDoc[which], Doc, null);
            // if (whichDoc?.type === DocumentType.MARKER) whichDoc = Cast(whichDoc.annotationOn, Doc, null);
            const targetDoc = Cast(whichDoc?.annotationOn, Doc, null) ?? whichDoc;
            return whichDoc ? (
                <>
                    <DocumentView
                        {...this.props}
                        NativeWidth={returnZero}
                        NativeHeight={returnZero}
                        isContentActive={returnFalse}
                        isDocumentActive={returnFalse}
                        styleProvider={this.docStyleProvider}
                        Document={targetDoc}
                        DataDoc={undefined}
                        hideLinkButton={true}
                        pointerEvents={returnNone}
                    />
                    {clearButton(which)}
                </> // placeholder image if doc is missing
            ) : (
                <div className="placeholder">
                    <FontAwesomeIcon className="upload-icon" icon={'cloud-upload-alt'} size="lg" />
                </div>
            );
        };
        const displayBox = (which: string, index: number, cover: number) => {
            return (
                <div className={`${index === 0 ? 'before' : 'after'}Box-cont`} key={which} style={{ width: this.props.PanelWidth() }} onPointerDown={e => this.registerSliding(e, cover)} ref={ele => this.createDropTarget(ele, which, index)}>
                    {displayDoc(which)}
                </div>
            );
        };

        return (
            <div className={`comparisonBox${this.props.isContentActive() || SnappingManager.GetIsDragging() ? '-interactive' : ''}` /* change className to easily disable/enable pointer events in CSS */}>
                {displayBox(`${this.fieldKey}_2`, 1, this.props.PanelWidth() - 3)}
                <div className="clip-div" style={{ width: this.clipWidth + '%', transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, 'gray') }}>
                    {displayBox(`${this.fieldKey}_1`, 0, 0)}
                </div>

                <div
                    className="slide-bar"
                    style={{
                        left: `calc(${this.clipWidth + '%'} - 0.5px)`,
                        cursor: this.clipWidth < 5 ? 'e-resize' : this.clipWidth / 100 > (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? 'w-resize' : undefined,
                    }}
                    onPointerDown={e => this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */
                >
                    <div className="slide-handle" />
                </div>
            </div>
        );
    }
}
