import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnTrue, StopEvent, Utils } from '../../../../Utils';
import { CollectionViewType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
import { DocumentView } from '../../nodes/DocumentView';
import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
import { StyleProp } from '../../StyleProvider';
import { CollectionStackedTimeline } from '../CollectionStackedTimeline';
import { CollectionSubView } from '../CollectionSubView';
import './CollectionLinearView.scss';

/**
 * CollectionLinearView is the class for rendering the horizontal collection
 * of documents, it useful for horizontal menus. It can either be expandable
 * or not using the linearViewExpandable field.
 * It is used in the following locations:
 * - It is used in the popup menu on the bottom left (see docButtons() in MainView.tsx)
 * - It is used for the context sensitive toolbar at the top (see contMenuButtons() in CollectionMenu.tsx)
 */
@observer
export class CollectionLinearView extends CollectionSubView() {
    @observable public addMenuToggle = React.createRef<HTMLInputElement>();
    @observable private _selectedIndex = -1;
    private _dropDisposer?: DragManager.DragDropDisposer;
    private _widthDisposer?: IReactionDisposer;
    private _selectedDisposer?: IReactionDisposer;

    componentWillUnmount() {
        this._dropDisposer?.();
        this._widthDisposer?.();
        this._selectedDisposer?.();
        this.childLayoutPairs.map((pair, ind) => ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log));
    }

    componentDidMount() {
        this._widthDisposer = reaction(
            () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[WidthSym]() || this.dimension()) + tot + 4, 0) : 0),
            width => this.childDocs.length && (this.layoutDoc._width = width),
            { fireImmediately: true }
        );

        this._selectedDisposer = reaction(
            () => NumCast(this.layoutDoc.selectedIndex),
            i =>
                runInAction(() => {
                    this._selectedIndex = i;
                    let selected: any = undefined;
                    this.childLayoutPairs.map(async (pair, ind) => {
                        const isSelected = this._selectedIndex === ind;
                        if (isSelected) {
                            selected = pair;
                        } else {
                            ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
                        }
                    });
                    if (selected && selected.layout) {
                        ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
                    }
                }),
            { fireImmediately: true }
        );
    }
    protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
        //used for stacking and masonry view
        this._dropDisposer && this._dropDisposer();
        if (ele) {
            this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
        }
    };

    dimension = () => NumCast(this.rootDoc._height);
    getTransform = (ele: Opt<HTMLDivElement>) => {
        if (!ele) return Transform.Identity();
        const { scale, translateX, translateY } = Utils.GetScreenTransform(ele);
        return new Transform(-translateX, -translateY, 1);
    };

    @action
    exitLongLinks = () => {
        if (DocumentLinksButton.StartLink) {
            if (DocumentLinksButton.StartLink.Document) {
                action((e: React.PointerEvent<HTMLDivElement>) => {
                    Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc);
                });
            }
        }
        DocumentLinksButton.StartLink = undefined;
        DocumentLinksButton.StartLinkView = undefined;
    };

    @action
    changeDescriptionSetting = () => {
        if (LinkDescriptionPopup.showDescriptions) {
            if (LinkDescriptionPopup.showDescriptions === 'ON') {
                LinkDescriptionPopup.showDescriptions = 'OFF';
                LinkDescriptionPopup.descriptionPopup = false;
            } else {
                LinkDescriptionPopup.showDescriptions = 'ON';
            }
        } else {
            LinkDescriptionPopup.showDescriptions = 'OFF';
            LinkDescriptionPopup.descriptionPopup = false;
        }
    };

    myContextMenu = (e: React.MouseEvent) => {
        e.stopPropagation();
        e.preventDefault();
    };

    getLinkUI = () => {
        return !DocumentLinksButton.StartLink ? null : (
            <span className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
                <span className="bottomPopup-text">
                    Creating link from:{' '}
                    <b>
                        {(DocumentLinksButton.AnnotationId ? 'Annotation in ' : ' ') +
                            (StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...')}
                    </b>
                </span>

                <Tooltip title={<div className="dash-tooltip">{'Toggle description pop-up'} </div>} placement="top">
                    <span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
                        Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : 'ON'}
                    </span>
                </Tooltip>

                <Tooltip title={<div className="dash-tooltip">Exit linking mode</div>} placement="top">
                    <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
                        Stop
                    </span>
                </Tooltip>
            </span>
        );
    };
    getCurrentlyPlayingUI = () => {
        return !CollectionStackedTimeline.CurrentlyPlaying?.length ? null : (
            <span className="bottomPopup-background">
                <span className="bottomPopup-text">
                    Currently playing:
                    {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => (
                        <>
                            <span className="audio-title" onPointerDown={() => DocumentManager.Instance.showDocument(clip.rootDoc, { willZoomCentered: true })}>
                                {clip.rootDoc.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? '  ' : ',')}
                            </span>
                            <FontAwesomeIcon icon={!clip.ComponentView?.IsPlaying?.() ? 'play' : 'pause'} size="lg" onPointerDown={() => clip.ComponentView?.TogglePause?.()} />{' '}
                            <FontAwesomeIcon icon="times" size="lg" onPointerDown={() => clip.ComponentView?.Pause?.()} />{' '}
                        </>
                    ))}
                </span>
            </span>
        );
    };
    getDisplayDoc = (doc: Doc, preview: boolean = false) => {
        if (doc.icon === 'linkui') return this.getLinkUI();
        if (doc.icon === 'currentlyplayingui') return this.getCurrentlyPlayingUI();

        const nested = doc._viewType === CollectionViewType.Linear;
        const hidden = doc.hidden === true;

        let dref: Opt<HTMLDivElement>;
        const docXf = () => this.getTransform(dref);
        // const scalable = pair.layout.onClick || pair.layout.onDragStart;
        return hidden ? null : (
            <div
                className={preview ? 'preview' : `collectionLinearView-docBtn`}
                key={doc[Id]}
                ref={r => (dref = r || undefined)}
                style={{
                    pointerEvents: 'all',
                    width: NumCast(doc._width),
                    height: NumCast(doc._height),
                    marginLeft: 2,
                    marginRight: 2,
                    // width: NumCast(pair.layout._width),
                    // height: NumCast(pair.layout._height),
                }}>
                <DocumentView
                    Document={doc}
                    isContentActive={this.props.isContentActive}
                    isDocumentActive={returnTrue}
                    addDocument={this.props.addDocument}
                    moveDocument={this.props.moveDocument}
                    addDocTab={this.props.addDocTab}
                    pinToPres={emptyFunction}
                    dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
                    rootSelected={this.props.isSelected}
                    removeDocument={this.props.removeDocument}
                    ScreenToLocalTransform={docXf}
                    PanelWidth={doc[WidthSym]}
                    PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
                    renderDepth={this.props.renderDepth + 1}
                    dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
                    focus={emptyFunction}
                    styleProvider={this.props.styleProvider}
                    docViewPath={returnEmptyDoclist}
                    whenChildContentsActiveChanged={emptyFunction}
                    bringToFront={emptyFunction}
                    docFilters={this.props.docFilters}
                    docRangeFilters={this.props.docRangeFilters}
                    searchFilterDocs={this.props.searchFilterDocs}
                    ContainingCollectionView={undefined}
                    ContainingCollectionDoc={undefined}
                    hideResizeHandles={true}
                />
            </div>
        );
    };

    render() {
        const flexDir = StrCast(this.Document.flexDirection); // Specify direction of linear view content
        const flexGap = NumCast(this.Document.flexGap); // Specify the gap between linear view content
        const isExpanded = BoolCast(this.layoutDoc.linearViewIsExpanded);

        const menuOpener = (
            <label
                className={`collectionlinearView-label${isExpanded ? '-expanded' : ''}`}
                htmlFor={this.Document[Id] + '-input'}
                style={{ boxShadow: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BoxShadow) }}
                onPointerDown={StopEvent}>
                <div className="collectionLinearView-menuOpener">{Cast(this.props.Document.icon, 'string', null) ?? <FontAwesomeIcon icon={isExpanded ? 'minus' : 'plus'} />}</div>
            </label>
        );

        return (
            <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: this.layoutDoc.linearViewIsExpanded ? undefined : 'transparent' }}>
                <div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu} style={{ minHeight: this.dimension() }}>
                    {!this.props.Document.linearViewExpandable ? null : (
                        <Tooltip title={<div className="dash-tooltip">{isExpanded ? 'Close' : 'Open'}</div>} placement="top">
                            {menuOpener}
                        </Tooltip>
                    )}
                    <input
                        id={this.Document[Id] + '-input'}
                        type="checkbox"
                        checked={isExpanded}
                        ref={this.addMenuToggle}
                        onChange={action(e => {
                            ScriptCast(this.Document.onClick)?.script.run({
                                this: this.layoutDoc,
                                self: this.rootDoc,
                                _readOnly_: false,
                                scriptContext: this.props.scriptContext,
                                thisContainer: this.props.ContainingCollectionDoc,
                                documentView: this.props.docViewPath().lastElement(),
                            });
                            this.layoutDoc.linearViewIsExpanded = this.addMenuToggle.current!.checked;
                        })}
                    />

                    <div
                        className="collectionLinearView-content"
                        style={{
                            height: this.dimension(),
                            flexDirection: flexDir as any,
                            gap: flexGap,
                        }}>
                        {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
                    </div>
                </div>
            </div>
        );
    }
}
