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, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
import { DocUtils } from '../../../documents/Documents';
import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
import { DocumentManager } from "../../../util/DocumentManager";
import { DragManager } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
import { Colors, Shadows } from '../../global/globalEnums';
import { AudioBox } from '../../nodes/AudioBox';
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 { CollectionViewType } from '../CollectionView';
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 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * (this.rootDoc[HeightSym]()) : 10),
            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); // 2 * the padding
    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();
    }


    getDisplayDoc = (doc: Doc, preview: boolean = false) => {
        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: nested ? undefined : NumCast(doc._width),
                height: nested ? undefined : NumCast(doc._height),
                marginLeft: !nested ? 2.5 : 0,
                marginRight: !nested ? 2.5 : 0,
                // 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}
                rootSelected={this.props.isSelected}
                removeDocument={this.props.removeDocument}
                ScreenToLocalTransform={docXf}
                PanelWidth={nested ? doc[WidthSym] : this.dimension}
                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 guid = Utils.GenerateGuid(); // Generate a unique ID to use as the label
        const flexDir: any = StrCast(this.Document.flexDirection); // Specify direction of linear view content
        const flexGap: number = NumCast(this.Document.flexGap); // Specify the gap between linear view content
        const expandable: boolean = BoolCast(this.props.Document.linearViewExpandable); // Specify whether it is expandable or not
        const floating: boolean = BoolCast(this.props.Document.linearViewFloating); // Specify whether it is expandable or not

        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
        const icon: string = StrCast(this.props.Document.icon); // Menu opener toggle
        const menuOpener = <label htmlFor={`${guid}`}
            style={{
                boxShadow: floating ? Shadows.STANDARD_SHADOW : undefined,
                color: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.BLACK,
                backgroundColor: backgroundColor === color ? "black" : BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.LIGHT_GRAY
            }}
            onPointerDown={e => e.stopPropagation()} >
            <div className="collectionLinearView-menuOpener">
                {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={"minus"} /> : icon ? icon : <FontAwesomeIcon icon={"plus"} />}
            </div>
        </label>;

        return <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : "transparent" }}>
            <div className="collectionLinearView" ref={this.createDashEventsTarget}
                onContextMenu={this.myContextMenu} >
                {!expandable ? (null) : <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close" : "Open"}</div></>} placement="top">
                    {menuOpener}
                </Tooltip>}
                <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle}
                    onChange={action(() => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />

                <div className="collectionLinearView-content"
                    style={{
                        height: this.dimension(),
                        flexDirection: flexDir,
                        gap: flexGap
                    }}>
                    {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
                </div>
                {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.DockedBtns ? 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>}
                {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.DockedBtns ? (null) :
                    <span className="bottomPopup-background">
                        <span className="bottomPopup-text">
                            Currently playing:
                            {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) =>
                                <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true, undefined, [])}>
                                    {clip.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")}
                                </span>)}
                        </span>
                    </span>}

            </div>
        </div>;
    }
}