import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
import { action, computed, IReactionDisposer, observable, ObservableMap, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { ColorState } from 'react-color';
import { Doc, Opt } from '../../../fields/Doc';
import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from '../../../Utils';
import { SelectionManager } from '../../util/SelectionManager';
import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu';
import { LinkPopup } from '../linking/LinkPopup';
import { ButtonDropdown } from '../nodes/formattedText/RichTextMenu';
import { gptSummarize } from '../../apis/gpt/Summarization';
import { GPTPopup } from './GPTPopup';
import './AnchorMenu.scss';

@observer
export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
    static Instance: AnchorMenu;

    private _disposer: IReactionDisposer | undefined;
    private _disposer2: IReactionDisposer | undefined;
    private _commentCont = React.createRef<HTMLButtonElement>();
    private _palette = [
        'rgba(208, 2, 27, 0.8)',
        'rgba(238, 0, 0, 0.8)',
        'rgba(245, 166, 35, 0.8)',
        'rgba(248, 231, 28, 0.8)',
        'rgba(245, 230, 95, 0.616)',
        'rgba(139, 87, 42, 0.8)',
        'rgba(126, 211, 33, 0.8)',
        'rgba(65, 117, 5, 0.8)',
        'rgba(144, 19, 254, 0.8)',
        'rgba(238, 169, 184, 0.8)',
        'rgba(224, 187, 228, 0.8)',
        'rgba(225, 223, 211, 0.8)',
        'rgba(255, 255, 255, 0.8)',
        'rgba(155, 155, 155, 0.8)',
        'rgba(0, 0, 0, 0.8)',
    ];

    @observable private highlightColor: string = 'rgba(245, 230, 95, 0.616)';
    @observable private _showLinkPopup: boolean = false;

    @observable public Highlighting: boolean = false;
    @observable public Status: 'marquee' | 'annotation' | '' = '';

    // GPT additions
    @observable private summarizedText: string = '';
    @observable private loadingSummary: boolean = false;
    @observable private showGPTPopup: boolean = false;
    @action
    setGPTPopupVis = (vis: boolean) => {
        this.showGPTPopup = vis;
    };
    @action
    setSummarizedText = (txt: string) => {
        this.summarizedText = txt;
    };
    @action
    setLoading = (loading: boolean) => {
        this.loadingSummary = loading;
    };

    private selectedText: string = '';
    public setSelectedText = (txt: string) => {
        this.selectedText = txt;
    };

    public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search

    public OnCrop: (e: PointerEvent) => void = unimplementedFunction;
    public OnClick: (e: PointerEvent) => void = unimplementedFunction;
    public OnAudio: (e: PointerEvent) => void = unimplementedFunction;
    public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
    public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
    public Highlight: (color: string, isTargetToggler: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => Opt<Doc> = (color: string, isTargetToggler: boolean) => undefined;
    public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => undefined;
    public Delete: () => void = unimplementedFunction;
    public PinToPres: () => void = unimplementedFunction;
    public MakeTargetToggle: () => void = unimplementedFunction;
    public ShowTargetTrail: () => void = unimplementedFunction;
    public IsTargetToggler: () => boolean = returnFalse;
    public get Active() {
        return this._left > 0;
    }

    constructor(props: Readonly<{}>) {
        super(props);

        AnchorMenu.Instance = this;
        AnchorMenu.Instance._canFade = false;
    }

    componentWillUnmount() {
        this._disposer?.();
        this._disposer2?.();
    }

    componentDidMount() {
        this._disposer2 = reaction(
            () => this._opacity,
            opacity => {
                if (!opacity) {
                    this._showLinkPopup = false;
                    this.setGPTPopupVis(false);
                    this.setSummarizedText('');
                }
            },
            { fireImmediately: true }
        );
        this._disposer = reaction(
            () => SelectionManager.Views(),
            selected => {
                this._showLinkPopup = false;
                this.setGPTPopupVis(false);
                this.setSummarizedText('');
                AnchorMenu.Instance.fadeOut(true);
            }
        );
    }

    /**
     * Returns  a mock summary simulating an API call.
     * @returns A Promise that resolves into a string
     */
    mockSummarize = async (): Promise<string> => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('Mock summary. This is a summary of the highlighted text.');
            }, 1000);
        });
    };

    /**
     * Invokes the API with the selected text and stores it in the summarized text.
     * @param e pointer down event
     */
    invokeGPT = async (e: React.PointerEvent) => {
        this.setGPTPopupVis(true);
        this.setLoading(true);
        const res = await gptSummarize(this.selectedText);
        // const res = await this.mockSummarize();
        if (res) {
            this.setSummarizedText(res);
        } else {
            this.setSummarizedText('Something went wrong.');
        }
        this.setLoading(false);
    };

    pointerDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(
            this,
            e,
            (e: PointerEvent) => {
                this.StartDrag(e, this._commentCont.current!);
                return true;
            },
            returnFalse,
            e => this.OnClick?.(e)
        );
    };

    audioDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(this, e, returnFalse, returnFalse, e => this.OnAudio?.(e));
    };

    cropDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(
            this,
            e,
            (e: PointerEvent) => {
                this.StartCropDrag(e, this._commentCont.current!);
                return true;
            },
            returnFalse,
            e => this.OnCrop?.(e)
        );
    };

    @action
    highlightClicked = (e: React.MouseEvent) => {
        if (!this.Highlight(this.highlightColor, false, undefined, true) && this.Pinned) {
            this.Highlighting = !this.Highlighting;
        }
        AnchorMenu.Instance.fadeOut(true);
    };

    @action
    toggleLinkPopup = (e: React.MouseEvent) => {
        //ignore the potential null type error because this method cannot be called unless the user selects text and clicks the link button
        //change popup visibility field to visible
        this._showLinkPopup = !this._showLinkPopup;
    };

    @computed get highlighter() {
        const button = (
            <button className="antimodeMenu-button anchor-color-preview-button" title="" key="highlighter-button" onClick={this.highlightClicked}>
                <div className="anchor-color-preview">
                    <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: 'transform 0.1s', transform: this.Highlighting ? '' : 'rotate(-45deg)' }} />
                    <div className="color-preview" style={{ backgroundColor: this.highlightColor }}></div>
                </div>
            </button>
        );

        const dropdownContent = (
            <div className="dropdown">
                <p>Change highlighter color:</p>
                <div className="color-wrapper">
                    {this._palette.map(color => {
                        if (color) {
                            return this.highlightColor === color ? (
                                <button className="color-button active" key={`active ${color}`} style={{ backgroundColor: color }} onPointerDown={e => this.changeHighlightColor(color, e)}></button>
                            ) : (
                                <button className="color-button" key={`inactive ${color}`} style={{ backgroundColor: color }} onPointerDown={e => this.changeHighlightColor(color, e)}></button>
                            );
                        }
                    })}
                </div>
            </div>
        );
        return (
            <Tooltip key="highlighter" title={<div className="dash-tooltip">{'Click to Highlight'}</div>}>
                <div className="anchorMenu-highlighter">
                    <ButtonDropdown key={'highlighter'} button={button} dropdownContent={dropdownContent} pdf={true} />
                </div>
            </Tooltip>
        );
    }

    @action changeHighlightColor = (color: string, e: React.PointerEvent) => {
        const col: ColorState = {
            hex: color,
            hsl: { a: 0, h: 0, s: 0, l: 0, source: '' },
            hsv: { a: 0, h: 0, s: 0, v: 0, source: '' },
            rgb: { a: 0, r: 0, b: 0, g: 0, source: '' },
            oldHue: 0,
            source: '',
        };
        e.preventDefault();
        e.stopPropagation();
        this.highlightColor = Utils.colorString(col);
    };

    render() {
        const buttons =
            this.Status === 'marquee' ? (
                <>
                    {this.highlighter}
                    <Tooltip key="annotate" title={<div className="dash-tooltip">Drag to Place Annotation</div>}>
                        <button className="antimodeMenu-button annotate" ref={this._commentCont} onPointerDown={this.pointerDown} style={{ cursor: 'grab' }}>
                            <FontAwesomeIcon icon="comment-alt" size="lg" />
                        </button>
                    </Tooltip>
                    {/* GPT Summarize icon only shows up when text is highlighted, not on marquee selection*/}
                    {AnchorMenu.Instance.StartCropDrag === unimplementedFunction && (
                        <Tooltip key="gpt" title={<div className="dash-tooltip">Summarize with AI</div>}>
                            <button className="antimodeMenu-button annotate" onPointerDown={this.invokeGPT} style={{ cursor: 'grab' }}>
                                <FontAwesomeIcon icon="comment-dots" size="lg" />
                            </button>
                        </Tooltip>
                    )}
                    <GPTPopup key="gptpopup" visible={this.showGPTPopup} text={this.summarizedText} loadingSummary={this.loadingSummary} callApi={this.invokeGPT} />
                    {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
                        <Tooltip key="annoaudiotate" title={<div className="dash-tooltip">Click to Record Annotation</div>}>
                            <button className="antimodeMenu-button annotate" onPointerDown={this.audioDown} style={{ cursor: 'grab' }}>
                                <FontAwesomeIcon icon="microphone" size="lg" />
                            </button>
                        </Tooltip>
                    )}
                    <Tooltip key="link" title={<div className="dash-tooltip">Find document to link to selected text</div>}>
                        <button className="antimodeMenu-button link" onPointerDown={this.toggleLinkPopup}>
                            <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(1.5)' }} icon={'search'} size="lg" />
                            <FontAwesomeIcon style={{ position: 'absolute', transform: 'scale(0.5)', transformOrigin: 'top left', top: 12, left: 12 }} icon={'link'} size="lg" />
                        </button>
                    </Tooltip>
                    <LinkPopup key="popup" showPopup={this._showLinkPopup} linkCreateAnchor={this.onMakeAnchor} />,
                    {AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : (
                        <Tooltip key="crop" title={<div className="dash-tooltip">Click/Drag to create cropped image</div>}>
                            <button className="antimodeMenu-button annotate" onPointerDown={this.cropDown} style={{ cursor: 'grab' }}>
                                <FontAwesomeIcon icon="image" size="lg" />
                            </button>
                        </Tooltip>
                    )}
                </>
            ) : (
                <>
                    <Tooltip key="trash" title={<div className="dash-tooltip">Remove Link Anchor</div>}>
                        <button className="antimodeMenu-button" onPointerDown={this.Delete}>
                            <FontAwesomeIcon icon="trash-alt" size="lg" />
                        </button>
                    </Tooltip>
                    <Tooltip key="Pin" title={<div className="dash-tooltip">Pin to Presentation</div>}>
                        <button className="antimodeMenu-button" onPointerDown={this.PinToPres}>
                            <FontAwesomeIcon icon="map-pin" size="lg" />
                        </button>
                    </Tooltip>
                    <Tooltip key="trail" title={<div className="dash-tooltip">Show Linked Trail</div>}>
                        <button className="antimodeMenu-button" onPointerDown={this.ShowTargetTrail}>
                            <FontAwesomeIcon icon="taxi" size="lg" />
                        </button>
                    </Tooltip>
                    <Tooltip key="toggle" title={<div className="dash-tooltip">make target visibility toggle on click</div>}>
                        <button className="antimodeMenu-button" style={{ color: this.IsTargetToggler() ? 'black' : 'white', backgroundColor: this.IsTargetToggler() ? 'white' : 'black' }} onPointerDown={this.MakeTargetToggle}>
                            <FontAwesomeIcon icon="thumbtack" size="lg" />
                        </button>
                    </Tooltip>
                </>
            );

        return this.getElement(buttons);
    }
}
