import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc } from '../../../fields/Doc';
import { StrCast } from '../../../fields/Types';
import { emptyFunction, returnFalse, setupMoveUpEvents, StopEvent } from '../../../Utils';
import { DocUtils } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { Hypothesis } from '../../util/HypothesisUtils';
import { LinkManager } from '../../util/LinkManager';
import { undoBatch, UndoManager } from '../../util/UndoManager';
import './DocumentLinksButton.scss';
import { DocumentView } from './DocumentView';
import { LinkDescriptionPopup } from './LinkDescriptionPopup';
import { TaskCompletionBox } from './TaskCompletedBox';
import { PinProps } from './trails';
import * as React from 'react';
import _ = require('lodash');

interface DocumentLinksButtonProps {
    View: DocumentView;
    Bottom?: boolean;
    AlwaysOn?: boolean;
    InMenu?: boolean;
    OnHover?: boolean;
    StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed)
    ShowCount?: boolean;
    scaling?: () => number; // how uch doc is scaled so that link buttons can invert it
    hideCount?: () => boolean;
}
@observer
export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
    private _linkButton = React.createRef<HTMLDivElement>();
    @observable public static StartLink: Doc | undefined; //origin's Doc, if defined
    @observable public static StartLinkView: DocumentView | undefined;
    @observable public static AnnotationId: string | undefined;
    @observable public static AnnotationUri: string | undefined;
    @observable public static LinkEditorDocView: DocumentView | undefined;

    @action
    @undoBatch
    onLinkButtonMoved = (e: PointerEvent) => {
        if (this.props.InMenu && this.props.StartLink) {
            if (this._linkButton.current !== null) {
                const linkDrag = UndoManager.StartBatch('Drag Link');
                this.props.View &&
                    DragManager.StartLinkDrag(this._linkButton.current, this.props.View, this.props.View.ComponentView?.getAnchor, e.pageX, e.pageY, {
                        dragComplete: dropEv => {
                            if (this.props.View && dropEv.linkDocument) {
                                // dropEv.linkDocument equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
                                !dropEv.linkDocument.link_relationship && (Doc.GetProto(dropEv.linkDocument).link_relationship = 'hyperlink');
                            }
                            linkDrag?.end();
                        },
                        hideSource: false,
                    });
                return true;
            }
            return false;
        }
        return false;
    };

    onLinkMenuOpen = (e: React.PointerEvent): void => {
        setupMoveUpEvents(
            this,
            e,
            this.onLinkButtonMoved,
            emptyFunction,
            action((e, doubleTap) => {
                doubleTap && DocumentView.showBackLinks(this.props.View.Document);
            }),
            undefined,
            undefined,
            action(() => (DocumentLinksButton.LinkEditorDocView = this.props.View))
        );
    };

    onLinkButtonDown = (e: React.PointerEvent): void => {
        setupMoveUpEvents(
            this,
            e,
            this.onLinkButtonMoved,
            emptyFunction,
            action((e, doubleTap) => {
                if (doubleTap && this.props.InMenu && this.props.StartLink) {
                    //action(() => Doc.BrushDoc(this.props.View.Document));
                    if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
                        DocumentLinksButton.StartLink = undefined;
                        DocumentLinksButton.StartLinkView = undefined;
                    } else {
                        DocumentLinksButton.StartLink = this.props.View.props.Document;
                        DocumentLinksButton.StartLinkView = this.props.View;
                    }
                }
            })
        );
    };

    @action
    @undoBatch
    onLinkClick = (e: React.MouseEvent): void => {
        if (this.props.InMenu && this.props.StartLink) {
            DocumentLinksButton.AnnotationId = undefined;
            DocumentLinksButton.AnnotationUri = undefined;
            if (DocumentLinksButton.StartLink === this.props.View.props.Document) {
                DocumentLinksButton.StartLink = undefined;
                DocumentLinksButton.StartLinkView = undefined;
            } else {
                //if this LinkButton's Document is undefined
                DocumentLinksButton.StartLink = this.props.View.props.Document;
                DocumentLinksButton.StartLinkView = this.props.View;
            }
        }
    };

    completeLink = (e: React.PointerEvent): void => {
        setupMoveUpEvents(
            this,
            e,
            returnFalse,
            emptyFunction,
            action(e => DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View))
        );
    };

    @undoBatch
    public static finishLinkClick(screenX: number, screenY: number, startLink: Doc | undefined, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView, pinProps?: PinProps) {
        runInAction(() => {
            if (startLink === endLink || !startLink) {
                DocumentLinksButton.StartLink = undefined;
                DocumentLinksButton.StartLinkView = undefined;
                DocumentLinksButton.AnnotationId = undefined;
                DocumentLinksButton.AnnotationUri = undefined;
                //!this.props.StartLink
            } else if (startLink !== endLink) {
                endLink = endLinkView?.docView?._componentView?.getAnchor?.(true, pinProps) || endLink;
                startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.(true) || startLink;
                const linkDoc = DocUtils.MakeLink(startLink, endLink, { link_relationship: DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined });

                LinkManager.currentLink = linkDoc;

                if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) {
                    // if linking from a Hypothes.is annotation
                    Doc.GetProto(linkDoc as Doc).linksToAnnotation = true;
                    Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId;
                    Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri;
                    const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink);
                    Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId, startIsAnnotation ? startLink : endLink); // edit annotation to add a Dash hyperlink to the linked doc
                }

                if (linkDoc) {
                    TaskCompletionBox.textDisplayed = 'Link Created';
                    TaskCompletionBox.popupX = screenX;
                    TaskCompletionBox.popupY = screenY - 133;
                    TaskCompletionBox.taskCompleted = true;

                    if (LinkDescriptionPopup.showDescriptions === 'ON' || !LinkDescriptionPopup.showDescriptions) {
                        LinkDescriptionPopup.popupX = screenX;
                        LinkDescriptionPopup.popupY = screenY - 100;
                        LinkDescriptionPopup.descriptionPopup = true;
                    }

                    const rect = document.body.getBoundingClientRect();
                    if (LinkDescriptionPopup.popupX + 200 > rect.width) {
                        LinkDescriptionPopup.popupX -= 190;
                        TaskCompletionBox.popupX -= 40;
                    }
                    if (LinkDescriptionPopup.popupY + 100 > rect.height) {
                        LinkDescriptionPopup.popupY -= 40;
                        TaskCompletionBox.popupY -= 40;
                    }

                    setTimeout(
                        action(() => {
                            TaskCompletionBox.taskCompleted = false;
                        }),
                        2500
                    );
                }
            }
        });
    }

    @action clearLinks() {
        DocumentLinksButton.StartLink = undefined;
        DocumentLinksButton.StartLinkView = undefined;
    }

    @computed get filteredLinks() {
        const results = [] as Doc[];
        const filters = this.props.View.props.childFilters();
        Array.from(new Set<Doc>(this.props.View.allLinks)).forEach(link => {
            if (DocUtils.FilterDocs([link], filters, []).length || DocUtils.FilterDocs([link.link_anchor_2 as Doc], filters, []).length || DocUtils.FilterDocs([link.link_anchor_1 as Doc], filters, []).length) {
                results.push(link);
            }
        });
        return results;
    }

    /**
     * gets the JSX of the link button (btn used to start/complete links) OR the link-view button (btn on bottom left of each linked node)
     *
     * todo:glr / anh seperate functionality such as onClick onPointerDown of link menu button
     */
    @computed get linkButtonInner() {
        const btnDim = 30;
        const isActive = DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.StartLink;
        const scaling = Math.min(1, this.props.scaling?.() || 1);
        const showLinkCount = (onHover?: boolean, offset?: boolean) => (
            <div
                className="documentLinksButton-showCount"
                onPointerDown={this.onLinkMenuOpen}
                style={{
                    fontSize: (onHover ? btnDim / 2 : 20) * scaling,
                    width: (onHover ? btnDim / 2 : btnDim) * scaling,
                    height: (onHover ? btnDim / 2 : btnDim) * scaling,
                    bottom: offset ? 5 * scaling : onHover ? (-btnDim / 2) * scaling : undefined,
                }}>
                <span style={{ width: '100%', display: 'inline-block', textAlign: 'center' }}>{Array.from(this.filteredLinks).length}</span>
            </div>
        );
        return this.props.ShowCount ? (
            showLinkCount(this.props.OnHover, this.props.Bottom)
        ) : (
            <div className="documentLinksButton-menu">
                {this.props.StartLink ? ( //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
                    <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} ref={this._linkButton} onPointerDown={isActive ? StopEvent : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
                        <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
                    </div>
                ) : null}
                {!this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? ( //if the origin node is not this node
                    <div className={'documentLinksButton-endLink'} ref={this._linkButton} onPointerDown={DocumentLinksButton.StartLink && this.completeLink}>
                        <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
                    </div>
                ) : null}
            </div>
        );
    }

    render() {
        if (this.props.hideCount?.()) return null;
        const menuTitle = this.props.StartLink ? 'Drag or tap to start link' : 'Tap to complete link';
        const buttonTitle = 'Tap to view links; double tap to open link collection';
        const title = this.props.ShowCount ? buttonTitle : menuTitle;

        //render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu
        return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? null : (
            <div
                className="documentLinksButton-wrapper"
                style={{
                    position: this.props.InMenu ? 'relative' : 'absolute',
                    top: 0,
                    pointerEvents: 'none',
                }}>
                {DocumentLinksButton.LinkEditorDocView ? this.linkButtonInner : <Tooltip title={<div className="dash-tooltip">{title}</div>}>{this.linkButtonInner}</Tooltip>}
            </div>
        );
    }
}
