import React = require("react");
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon, FontAwesomeIconProps } from "@fortawesome/react-fontawesome";
import { Tooltip } from "@material-ui/core";
import { action, computed, Lambda, observable, reaction, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
import { ColorState } from "react-color";
import { Doc, DocListCast, Opt } from "../../../fields/Doc";
import { Document } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { InkTool } from "../../../fields/InkField";
import { List } from "../../../fields/List";
import { ObjectField } from "../../../fields/ObjectField";
import { RichTextField } from "../../../fields/RichTextField";
import { listSpec } from "../../../fields/Schema";
import { ScriptField } from "../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { DragManager } from "../../util/DragManager";
import { Scripting } from "../../util/Scripting";
import { SelectionManager } from "../../util/SelectionManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu";
import { EditableView } from "../EditableView";
import { GestureOverlay } from "../GestureOverlay";
import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke";
import { LightboxView } from "../LightboxView";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { DocumentView } from "../nodes/DocumentView";
import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
import { PresBox } from "../nodes/trails/PresBox";
import { DefaultStyleProvider } from "../StyleProvider";
import { CollectionDockingView } from "./CollectionDockingView";
import { CollectionLinearView } from "./collectionLinear";
import "./CollectionMenu.scss";
import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { TabDocView } from "./TabDocView";
import { Colors } from "../global/globalEnums";

interface CollectionMenuProps {
    panelHeight: () => number;
    panelWidth: () => number;
}

@observer
export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
    @observable static Instance: CollectionMenu;

    @observable SelectedCollection: DocumentView | undefined;
    @observable FieldKey: string;

    private _docBtnRef = React.createRef<HTMLDivElement>();

    constructor(props: any) {
        super(props);
        this.FieldKey = "";
        runInAction(() => CollectionMenu.Instance = this);
        this._canFade = false; // don't let the inking menu fade away
        runInAction(() => this.Pinned = Cast(Doc.UserDoc()["menuCollections-pinned"], "boolean", true));
        this.jumpTo(300, 300);
    }

    componentDidMount() {
        reaction(() => SelectionManager.Views().length && SelectionManager.Views()[0],
            view => view && this.SetSelection(view));
    }

    @action
    SetSelection(view: DocumentView) {
        this.SelectedCollection = view;
    }

    @action
    toggleMenuPin = (e: React.MouseEvent) => {
        Doc.UserDoc()["menuCollections-pinned"] = this.Pinned = !this.Pinned;
        if (!this.Pinned && this._left < 0) {
            this.jumpTo(300, 300);
        }
    }

    @action
    toggleProperties = () => {
        if (CurrentUserUtils.propertiesWidth > 0) {
            CurrentUserUtils.propertiesWidth = 0;
        } else {
            CurrentUserUtils.propertiesWidth = 250;
        }
    }

    buttonBarXf = () => {
        if (!this._docBtnRef.current) return Transform.Identity();
        const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
        return new Transform(-translateX, -translateY, 1 / scale);
    }

    @computed get contMenuButtons() {
        const selDoc = Doc.UserDoc().contextMenuBtns;
        return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }} >
            <CollectionLinearView
                Document={selDoc}
                DataDoc={undefined}
                fieldKey={"data"}
                dropAction={"alias"}
                setHeight={returnFalse}
                styleProvider={DefaultStyleProvider}
                layerProvider={undefined}
                rootSelected={returnTrue}
                bringToFront={emptyFunction}
                select={emptyFunction}
                isContentActive={returnTrue}
                isAnyChildContentActive={returnFalse}
                isSelected={returnFalse}
                docViewPath={returnEmptyDoclist}
                moveDocument={returnFalse}
                CollectionView={undefined}
                addDocument={returnFalse}
                addDocTab={returnFalse}
                pinToPres={emptyFunction}
                removeDocument={returnFalse}
                ScreenToLocalTransform={this.buttonBarXf}
                PanelWidth={this.props.panelWidth}
                PanelHeight={this.props.panelHeight}
                renderDepth={0}
                focus={emptyFunction}
                whenChildContentsActiveChanged={emptyFunction}
                docFilters={returnEmptyFilter}
                docRangeFilters={returnEmptyFilter}
                searchFilterDocs={returnEmptyDoclist}
                ContainingCollectionView={undefined}
                ContainingCollectionDoc={undefined} />
        </div>;
    }

    render() {

        const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left";
        const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Properties Panel" : "Open Properties Panel";

        const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="properties" placement="bottom">
            <div className="collectionMenu-hardCodedButton"
                style={{ backgroundColor: CurrentUserUtils.propertiesWidth > 0 ? Colors.MEDIUM_BLUE : undefined }}
                key="properties"
                onPointerDown={this.toggleProperties}>
                <FontAwesomeIcon icon={propIcon} size="lg" />
            </div>
        </Tooltip>;

        // NEW BUTTONS
        //dash col linear view buttons
        const contMenuButtons =
            <div className="collectionMenu-container">
                {this.contMenuButtons}
                {prop}
            </div>;

        return contMenuButtons;

        // const button = <Tooltip title={<div className="dash-tooltip">Pin Menu</div>} key="pin menu" placement="bottom">
        //     <button className="antimodeMenu-button" onClick={this.toggleMenuPin} style={{ backgroundColor: "#121721" }}>
        //         <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
        //     </button>
        // </Tooltip>;

        // OLD BUTTONS
        // return this.getElement(!this.SelectedCollection ? [/*button*/] :
        //     [<CollectionViewBaseChrome key="chrome"
        //         docView={this.SelectedCollection}
        //         fieldKey={this.SelectedCollection.LayoutFieldKey}
        //         type={StrCast(this.SelectedCollection?.props.Document._viewType, CollectionViewType.Invalid) as CollectionViewType} />,
        //         prop,
        //         /*button*/]);
    }
}

interface CollectionViewMenuProps {
    type: CollectionViewType;
    fieldKey: string;
    docView: DocumentView;
}

const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();

@observer
export class CollectionViewBaseChrome extends React.Component<CollectionViewMenuProps> {
    //(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)

    get document() { return this.props.docView?.props.Document; }
    get target() { return this.document; }
    _templateCommand = {
        params: ["target", "source"], title: "item view",
        script: "self.target.childLayoutTemplate = getDocTemplate(self.source?.[0])",
        immediate: undoBatch((source: Doc[]) => {
            let formatStr = source.length && Cast(source[0].text, RichTextField, null)?.Text;
            try { formatStr && JSON.parse(formatStr); } catch (e) { formatStr = ""; }
            if (source.length === 1 && formatStr) {
                Doc.SetInPlace(this.target, "childLayoutString", formatStr, false);
            } else if (source.length) {
                this.target.childLayoutTemplate = Doc.getDocTemplate(source?.[0]);
            } else {
                Doc.SetInPlace(this.target, "childLayoutString", undefined, true);
                Doc.SetInPlace(this.target, "childLayoutTemplate", undefined, true);
            }
        }),
        initialize: emptyFunction,
    };
    _narrativeCommand = {
        params: ["target", "source"], title: "child click view",
        script: "self.target.childClickedOpenTemplateView = getDocTemplate(self.source?.[0])",
        immediate: undoBatch((source: Doc[]) => source.length && (this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]))),
        initialize: emptyFunction,
    };
    _contentCommand = {
        params: ["target", "source"], title: "set content",
        script: "getProto(self.target).data = copyField(self.source);",
        immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List<Doc>(source)),
        initialize: emptyFunction,
    };
    _onClickCommand = {
        params: ["target", "proxy"], title: "copy onClick",
        script: `{ if (self.proxy?.[0]) {
             getProto(self.proxy[0]).onClick = copyField(self.target.onClick); 
             getProto(self.proxy[0]).target = self.target.target;
             getProto(self.proxy[0]).source = copyField(self.target.source); 
            }}`,
        immediate: undoBatch((source: Doc[]) => { }),
        initialize: emptyFunction,
    };
    _openLinkInCommand = {
        params: ["target", "container"], title: "link follow target",
        script: `{ if (self.container?.length) {
            getProto(self.target).linkContainer = self.container[0];
            getProto(self.target).isLinkButton = true;
            getProto(self.target).onClick = makeScript("getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])");
            }}`,
        immediate: undoBatch((container: Doc[]) => {
            if (container.length) {
                Doc.GetProto(this.target).linkContainer = container[0];
                Doc.GetProto(this.target).isLinkButton = true;
                Doc.GetProto(this.target).onClick = ScriptField.MakeScript("getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])");
            }
        }),
        initialize: emptyFunction,
    };
    _viewCommand = {
        params: ["target"], title: "bookmark view",
        script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale']; gotoFrame(self.target, self['target-currentFrame']);",
        immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target._viewScale = 1; this.target._currentFrame = (this.target._currentFrame === undefined ? undefined : 0); }),
        initialize: (button: Doc) => { button['target-panX'] = this.target._panX; button['target-panY'] = this.target._panY; button['target-viewScale'] = this.target._viewScale; button['target-currentFrame'] = this.target._currentFrame; },
    };
    _clusterCommand = {
        params: ["target"], title: "fit content",
        script: "self.target._fitToBox = !self.target._fitToBox;",
        immediate: undoBatch((source: Doc[]) => this.target._fitToBox = !this.target._fitToBox),
        initialize: emptyFunction
    };
    _fitContentCommand = {
        params: ["target"], title: "toggle clusters",
        script: "self.target._useClusters = !self.target._useClusters;",
        immediate: undoBatch((source: Doc[]) => this.target._useClusters = !this.target._useClusters),
        initialize: emptyFunction
    };
    _saveFilterCommand = {
        params: ["target"], title: "save filter",
        script: `self.target._docFilters = compareLists(self['target-docFilters'],self.target._docFilters) ? undefined : copyField(self['target-docFilters']); 
                 self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
        immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
        initialize: (button: Doc) => {
            button['target-docFilters'] = (Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters || Cast(Doc.UserDoc().activeDashboard, Doc, null)._docFilters) instanceof ObjectField ?
                ObjectField.MakeCopy((Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters || Cast(Doc.UserDoc().activeDashboard, Doc, null)._docFilters) as any as ObjectField) : undefined;
            button['target-searchFilterDocs'] = CurrentUserUtils.ActiveDashboard._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(CurrentUserUtils.ActiveDashboard._searchFilterDocs as any as ObjectField) : undefined;
        },
    };

    @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
    @computed get _stacking_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
    @computed get _notetaking_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
    @computed get _masonry_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
    @computed get _schema_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; }
    @computed get _doc_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; }
    @computed get _tree_commands() { return undefined; }
    private get _buttonizableCommands() {
        switch (this.props.type) {
            default: return this._doc_commands;
            case CollectionViewType.Freeform: return this._freeform_commands;
            case CollectionViewType.Tree: return this._tree_commands;
            case CollectionViewType.Schema: return this._schema_commands;
            case CollectionViewType.Stacking: return this._stacking_commands;
            case CollectionViewType.NoteTaking: return this._notetaking_commands;
            case CollectionViewType.Masonry: return this._stacking_commands;
            case CollectionViewType.Time: return this._freeform_commands;
            case CollectionViewType.Carousel: return this._freeform_commands;
            case CollectionViewType.Carousel3D: return this._freeform_commands;
        }
    }
    private _commandRef = React.createRef<HTMLInputElement>();
    private _viewRef = React.createRef<HTMLInputElement>();
    @observable private _currentKey: string = "";

    componentDidMount = action(() => {
        this._currentKey = this._currentKey || (this._buttonizableCommands?.length ? this._buttonizableCommands[0]?.title : "");
    });

    @undoBatch
    viewChanged = (e: React.ChangeEvent) => {
        const target = this.document !== Doc.UserDoc().sidebar ? this.document : this.document.proto as Doc;
        //@ts-ignore
        target._viewType = e.target.selectedOptions[0].value;
    }

    commandChanged = (e: React.ChangeEvent) => {
        //@ts-ignore
        runInAction(() => this._currentKey = e.target.selectedOptions[0].value);
    }


    @action closeViewSpecs = () => {
        this.document._facetWidth = 0;
    }

    @computed get subChrome() {
        switch (this.props.docView.props.LayoutTemplateString ? CollectionViewType.Freeform : this.props.type) { // bcz: ARgh!  hack to get menu for tree view outline items
            default: return this.otherSubChrome;
            case CollectionViewType.Invalid:
            case CollectionViewType.Freeform: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} />);
            case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" {...this.props} />);
            case CollectionViewType.NoteTaking: return (<CollectionNoteTakingViewChrome key="collchrome" {...this.props} />);
            case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" {...this.props} />);
            case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" {...this.props} />);
            case CollectionViewType.Masonry: return (<CollectionStackingViewChrome key="collchrome" {...this.props} />);
            case CollectionViewType.Carousel:
            case CollectionViewType.Carousel3D: return (<Collection3DCarouselViewChrome key="collchrome" {...this.props} />);
            case CollectionViewType.Grid: return (<CollectionGridViewChrome key="collchrome" {...this.props} />);
            case CollectionViewType.Docking: return (<CollectionDockingChrome key="collchrome" {...this.props} />);
        }
    }

    @computed get otherSubChrome() {
        const docType = this.props.docView.Document.type;
        switch (docType) {
            default: return (null);
            case DocumentType.IMG: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
            case DocumentType.PDF: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
            case DocumentType.INK: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
            case DocumentType.WEB: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
            case DocumentType.VID: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
            case DocumentType.RTF: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} isDoc={true} />);
            case DocumentType.MAP: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
        }
    }


    private dropDisposer?: DragManager.DragDropDisposer;
    protected createDropTarget = (ele: HTMLDivElement) => {
        this.dropDisposer?.();
        if (ele) {
            this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.document);
        }
    }

    @undoBatch
    @action
    protected drop(e: Event, de: DragManager.DropEvent): boolean {
        const docDragData = de.complete.docDragData;
        if (docDragData?.draggedDocuments.length) {
            this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => c.immediate(docDragData.draggedDocuments || []));
            e.stopPropagation();
        }
        return true;
    }

    dragViewDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(this, e, (e, down, delta) => {
            const vtype = this.props.type;
            const c = {
                params: ["target"], title: vtype,
                script: `this.target._viewType = '${StrCast(this.props.type)}'`,
                immediate: (source: Doc[]) => this.document._viewType = Doc.getDocTemplate(source?.[0]),
                initialize: emptyFunction,
            };
            DragManager.StartButtonDrag([this._viewRef.current!], c.script, StrCast(c.title),
                { target: this.document }, c.params, c.initialize, e.clientX, e.clientY);
            return true;
        }, emptyFunction, emptyFunction);
    }
    dragCommandDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(this, e, (e, down, delta) => {
            this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c =>
                DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title,
                    { target: this.document }, c.params, c.initialize, e.clientX, e.clientY));
            return true;
        }, emptyFunction, () => {
            this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => c.immediate([]));
        });
    }

    @computed get templateChrome() {
        return <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} >
            <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
                <div className="commandEntry-outerDiv" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
                    <button className={"antimodeMenu-button"} >
                        <FontAwesomeIcon icon="bullseye" size="lg" />
                    </button>
                    <select
                        className="collectionViewBaseChrome-cmdPicker" onPointerDown={stopPropagation} onChange={this.commandChanged} value={this._currentKey}>
                        <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={"empty"} value={""} />
                        {this._buttonizableCommands?.map(cmd =>
                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>{cmd.title}</option>
                        )}
                    </select>
                </div>
            </Tooltip>
        </div>;
    }

    @computed get viewModes() {
        const excludedViewTypes = [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Linear];
        const isPres: boolean = (this.document && this.document.type === DocumentType.PRES);
        return isPres ? (null) : (<div className="collectionViewBaseChrome-viewModes" >
            <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
                <div className="commandEntry-outerDiv" ref={this._viewRef} onPointerDown={this.dragViewDown}>
                    <button className={"antimodeMenu-button"}>
                        <FontAwesomeIcon icon="bullseye" size="lg" />
                    </button>
                    <select
                        className="collectionViewBaseChrome-viewPicker"
                        onPointerDown={stopPropagation}
                        onChange={this.viewChanged}
                        value={StrCast(this.props.type)}>
                        {Object.values(CollectionViewType).filter(type => !excludedViewTypes.includes(type)).map(type => (
                            <option
                                key={Utils.GenerateGuid()}
                                className="collectionViewBaseChrome-viewOption"
                                onPointerDown={stopPropagation}
                                value={type}>
                                {type[0].toUpperCase() + type.substring(1)}
                            </option>
                        ))}
                    </select>
                </div>
            </Tooltip>
        </div>);
    }

    @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); }
    @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); }
    @computed get notACollection() {
        if (this.selectedDoc) {
            const layoutField = Doc.LayoutField(this.selectedDoc);
            return this.props.type === CollectionViewType.Docking ||
                typeof (layoutField) === "string" && !layoutField?.includes("CollectionView");
        }
        else return false;
    }
    @computed
    get pinButton() {
        const targetDoc = this.selectedDoc;
        const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
        return !targetDoc ? (null) : <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div>} placement="top">
            <button className="antimodeMenu-button" style={{ backgroundColor: isPinned ? "121212" : undefined, borderLeft: "1px solid gray" }}
                onClick={e => TabDocView.PinDoc(targetDoc, { unpin: isPinned })}>
                <FontAwesomeIcon className="colMenu-icon" size="lg" icon="map-pin" />
            </button>
        </Tooltip>;
    }

    @undoBatch
    @action
    startRecording = () => {
        const doc = Docs.Create.ScreenshotDocument("screen recording", { _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" });
        //Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
        CollectionDockingView.AddSplit(doc, "right");
    }

    @computed
    get recordButton() {
        const targetDoc = this.selectedDoc;
        return <Tooltip key="record" title={<div className="dash-tooltip">{"Capture screen"}</div>} placement="top">
            <button className="antimodeMenu-button"
                onClick={e => this.startRecording()}>
                <div className="recordButtonOutline" style={{}}>
                    <div className="recordButtonInner" style={{}}>
                    </div>
                </div>
            </button>
        </Tooltip>;
    }

    @undoBatch
    @action
    pinWithView = (targetDoc: Opt<Doc>) => {
        if (targetDoc) {
            TabDocView.PinDoc(targetDoc);
            const presArray: Doc[] = PresBox.Instance?.sortArray();
            const size: number = PresBox.Instance?._selectedArray.size;
            const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
            const activeDoc = presSelected ? PresBox.Instance?.childDocs[PresBox.Instance?.childDocs.indexOf(presSelected) + 1] : PresBox.Instance?.childDocs[PresBox.Instance?.childDocs.length - 1];
            if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking || targetDoc._viewType === CollectionViewType.NoteTaking) {
                const scroll = targetDoc._scrollTop;
                activeDoc.presPinView = true;
                activeDoc.presPinViewScroll = scroll;
            } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.MAP) {
                const x = targetDoc._panX;
                const y = targetDoc._panY;
                const scale = targetDoc._viewScale;
                activeDoc.presPinView = true;
                activeDoc.presPinViewX = x;
                activeDoc.presPinViewY = y;
                activeDoc.presPinViewScale = scale;
            } else if (targetDoc.type === DocumentType.VID) {
                activeDoc.presPinTimecode = targetDoc._currentTimecode;
                activeDoc.presPinView = true;
            } else if (targetDoc.type === DocumentType.COMPARISON) {
                const width = targetDoc._clipWidth;
                activeDoc.presPinClipWidth = width;
                activeDoc.presPinView = true;
            }
        }
    }

    @computed
    get pinWithViewButton() {
        const presPinWithViewIcon = <img src={`/assets/pinWithView.png`} style={{ margin: "auto", width: 19 }} />;
        return !this.selectedDoc ? (null) :
            <Tooltip title={<div className="dash-tooltip">{"Pin with current view"}</div>} placement="top">
                <button className="antimodeMenu-button" style={{ justifyContent: 'center' }}
                    onClick={() => this.pinWithView(this.selectedDoc)}>
                    {presPinWithViewIcon}
                </button>
            </Tooltip>;
    }


    @undoBatch
    onAlias = () => {
        if (this.selectedDoc && this.selectedDocumentView) {
            // const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true);
            // copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
            // copy.y = NumCast(this.selectedDoc.y) + 30;
            // this.selectedDocumentView.props.addDocument?.(copy);
            const alias = Doc.MakeAlias(this.selectedDoc);
            alias.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
            alias.y = NumCast(this.selectedDoc.y) + 30;
            this.selectedDocumentView.props.addDocument?.(alias);
        }
    }
    onAliasButtonDown = (e: React.PointerEvent): void => {
        setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
    }

    @undoBatch
    onAliasButtonMoved = (e: PointerEvent) => {
        const contentDiv = this.selectedDocumentView?.ContentDiv;
        if (contentDiv && this.selectedDoc) {
            const dragData = new DragManager.DocumentDragData([this.selectedDoc]);
            const offset = [e.clientX - contentDiv.getBoundingClientRect().x, e.clientY - contentDiv.getBoundingClientRect().y];
            dragData.defaultDropAction = "alias";
            dragData.canEmbed = true;
            DragManager.StartDocumentDrag([contentDiv], dragData, e.clientX, e.clientY, {
                offsetX: offset[0],
                offsetY: offset[1],
                hideSource: false
            });
            return true;
        }
        return false;
    }

    @computed
    get aliasButton() {
        const targetDoc = this.selectedDoc;
        return !targetDoc || targetDoc.type === DocumentType.PRES ? (null) : <Tooltip title={<div className="dash-tooltip">{"Tap or Drag to create an alias"}</div>} placement="top">
            <button className="antimodeMenu-button" onPointerDown={this.onAliasButtonDown} onClick={this.onAlias} style={{ cursor: "drag" }}>
                <FontAwesomeIcon className="colMenu-icon" icon="copy" size="lg" />
            </button>
        </Tooltip>;
    }

    @computed get lightboxButton() {
        const targetDoc = this.selectedDoc;
        return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"View in Lightbox"}</div>} placement="top">
            <button className="antimodeMenu-button" onPointerDown={() => {
                const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
                LightboxView.SetLightboxDoc(targetDoc, undefined, docs);
            }}>
                <FontAwesomeIcon className="colMenu-icon" icon="desktop" size="lg" />
            </button>
        </Tooltip>;
    }

    @computed get toggleOverlayButton() {
        return <>
            <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom">
                <button className={"antimodeMenu-button"} key="float"
                    style={{
                        backgroundColor: this.props.docView.layoutDoc.z ? "121212" : undefined,
                        pointerEvents: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "none" : undefined,
                        color: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "dimgrey" : undefined
                    }}
                    onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}>
                    <FontAwesomeIcon icon={["fab", "buffer"]} size={"lg"} />
                </button>
            </Tooltip>
        </>;
    }

    render() {
        return (
            <div className="collectionMenu-cont" >
                <div className="collectionMenu">
                    <div className="collectionViewBaseChrome">
                        {this.notACollection || this.props.type === CollectionViewType.Invalid ? (null) : this.viewModes}
                        <div className="collectionMenu-divider" key="divider1"></div>
                        {this.aliasButton}
                        {/* {this.pinButton} */}
                        {this.toggleOverlayButton}
                        {this.pinWithViewButton}
                        <div className="collectionMenu-divider" key="divider2"></div>
                        {this.subChrome}
                        <div className="collectionMenu-divider" key="divider3"></div>
                        {this.lightboxButton}
                        {this.recordButton}
                        {!this._buttonizableCommands ? (null) : this.templateChrome}
                    </div>
                </div>
            </div>
        );
    }
}

@observer
export class CollectionDockingChrome extends React.Component<CollectionViewMenuProps> {
    render() {
        return (null);
    }
}

@observer
export class CollectionFreeFormViewChrome extends React.Component<CollectionViewMenuProps & { isOverlay: boolean, isDoc?: boolean }> {
    public static Instance: CollectionFreeFormViewChrome;
    constructor(props: any) {
        super(props);
        CollectionFreeFormViewChrome.Instance = this;
    }
    get document() { return this.props.docView.props.Document; }
    @computed get dataField() {
        return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? "-annotations" : "")];
    }
    @computed get childDocs() { return DocListCast(this.dataField); }
    @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); }
    @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); }
    @computed get isText() { return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false; }

    @undoBatch
    @action
    nextKeyframe = (): void => {
        const currentFrame = Cast(this.document._currentFrame, "number", null);
        if (currentFrame === undefined) {
            this.document._currentFrame = 0;
            CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
        }
        CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, currentFrame || 0);
        this.document._currentFrame = Math.max(0, (currentFrame || 0) + 1);
        this.document.lastFrame = Math.max(NumCast(this.document._currentFrame), NumCast(this.document.lastFrame));
    }
    @undoBatch
    @action
    prevKeyframe = (): void => {
        const currentFrame = Cast(this.document._currentFrame, "number", null);
        if (currentFrame === undefined) {
            this.document._currentFrame = 0;
            CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
        }
        CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice());
        this.document._currentFrame = Math.max(0, (currentFrame || 0) - 1);
    }

    private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", ""];
    private _width = ["1", "5", "10", "100"];
    private _dotsize = [10, 20, 30, 40];
    private _draw = ["∿", "=", "⎯", "→", "↔︎", "ロ", "O"];
    private _head = ["", "", "", "", "arrow", "", ""];
    private _end = ["", "", "", "arrow", "arrow", "", ""];
    private _shapePrims = ["", "", "line", "line", "line", "rectangle", "circle"];
    private _title = ["pen", "highlighter", "line", "line with arrow", "line with double arrows", "square", "circle"];
    private _faName = ["pen-fancy", "highlighter", "minus", "long-arrow-alt-right", "arrows-alt-h", "square", "circle"];
    @observable _selectedPrimitive = this._shapePrims.length;
    @observable _keepPrimitiveMode = false; // for whether primitive selection enters a one-shot or persistent mode
    @observable _colorBtn = false;
    @observable _widthBtn = false;
    @observable _fillBtn = false;

    @action clearKeepPrimitiveMode() { this._selectedPrimitive = this._shapePrims.length; }
    @action primCreated() {
        if (!this._keepPrimitiveMode) { //get out of ink mode after each stroke=
            if (CurrentUserUtils.SelectedTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
            CurrentUserUtils.SelectedTool = InkTool.None;
            this._selectedPrimitive = this._shapePrims.length;
            SetActiveArrowStart("none");
            SetActiveArrowEnd("none");
        }
    }

    @action
    changeColor = (color: string, type: string) => {
        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: "",
        };
        if (type === "color") {
            SetActiveInkColor(Utils.colorString(col));
        } else if (type === "fill") {
            SetActiveFillColor(Utils.colorString(col));
        }
    }

    @action
    editProperties = (value: any, field: string) => {
        SelectionManager.Views().forEach(action((element: DocumentView) => {
            const doc = Document(element.rootDoc);
            if (doc.type === DocumentType.INK) {
                switch (field) {
                    case "width": doc.strokeWidth = Number(value); break;
                    case "color": doc.color = String(value); break;
                    case "fill": doc.fillColor = String(value); break;
                    case "dash": doc.strokeDash = value;
                }
            }
        }));
    }

    @computed get drawButtons() {
        const func = action((e: React.MouseEvent | React.PointerEvent, i: number, keep: boolean) => {
            this._keepPrimitiveMode = keep;
            if (this._selectedPrimitive !== i) {
                this._selectedPrimitive = i;
                if (this._title[i] === "highlighter") {
                    CurrentUserUtils.SelectedTool = InkTool.Highlighter;
                    GestureOverlay.Instance.SavedColor = ActiveInkColor();
                    SetActiveInkColor("rgba(245, 230, 95, 0.75)");
                } else {
                    CurrentUserUtils.SelectedTool = InkTool.Pen;
                }
                SetActiveArrowStart(this._head[i]);
                SetActiveArrowEnd(this._end[i]);
                SetActiveBezierApprox("300");

                GestureOverlay.Instance.InkShape = this._shapePrims[i];
            } else {
                this._selectedPrimitive = this._shapePrims.length;
                CurrentUserUtils.SelectedTool = InkTool.None;
                SetActiveArrowStart("");
                SetActiveArrowEnd("");
                GestureOverlay.Instance.InkShape = "";
                SetActiveBezierApprox("0");
            }
            e.stopPropagation();
        });
        return <div className="btn-draw" key="draw">
            {this._draw.map((icon, i) =>
                <Tooltip key={icon} title={<div className="dash-tooltip">{this._title[i]}</div>} placement="bottom">
                    <button className="antimodeMenu-button"
                        onPointerDown={e => func(e, i, false)}
                        onDoubleClick={e => func(e, i, true)}
                        style={{ backgroundColor: i === this._selectedPrimitive ? "525252" : "", fontSize: "20" }}>
                        <FontAwesomeIcon icon={this._faName[i] as IconProp} size="sm" />
                    </button>
                </Tooltip>)}
        </div>;
    }

    toggleButton = (key: string, value: boolean, setter: () => {}, icon: FontAwesomeIconProps["icon"], ele: JSX.Element | null) => {
        return <Tooltip title={<div className="dash-tooltip">{key}</div>} placement="bottom">
            <button className="antimodeMenu-button" key={key}
                onPointerDown={action(e => setter())}
                style={{ backgroundColor: value ? "121212" : "" }}>
                <FontAwesomeIcon icon={icon} size="lg" />
                {ele}
            </button>
        </Tooltip>;
    }

    @computed get widthPicker() {
        const widthPicker = this.toggleButton("stroke width", this._widthBtn, () => this._widthBtn = !this._widthBtn, "bars", null);
        return !this._widthBtn ? widthPicker :
            <div className="btn2-group" key="width">
                {widthPicker}
                {this._width.map((wid, i) =>
                    <Tooltip title={<div className="dash-tooltip">change width</div>} placement="bottom">
                        <button className="antimodeMenu-button" key={wid}
                            onPointerDown={action(() => { SetActiveInkWidth(wid); this._widthBtn = false; this.editProperties(wid, "width"); })}
                            style={{ backgroundColor: this._widthBtn ? "121212" : "", zIndex: 1001, fontSize: this._dotsize[i], padding: 0, textAlign: "center" }}>
                            •
                        </button>
                    </Tooltip>)}
            </div>;
    }

    @computed get colorPicker() {
        const colorPicker = this.toggleButton("stroke color", this._colorBtn, () => this._colorBtn = !this._colorBtn, "pen-nib",
            <div className="color-previewI" style={{ backgroundColor: ActiveInkColor() ?? "121212" }} />);
        return !this._colorBtn ? colorPicker :
            <div className="btn-group" key="color">
                {colorPicker}
                {this._palette.map(color =>
                    <button className="antimodeMenu-button" key={color}
                        onPointerDown={action(() => { this.changeColor(color, "color"); this._colorBtn = false; this.editProperties(color, "color"); })}
                        style={{ backgroundColor: this._colorBtn ? "121212" : "", zIndex: 1001 }}>
                        {/* <FontAwesomeIcon icon="pen-nib" size="lg" /> */}
                        <div className="color-previewII" style={{ backgroundColor: color }}>
                            {color === "" ? <p style={{ fontSize: 40, color: "red", marginTop: -10, marginLeft: -5, position: "fixed" }}>☒</p> : ""}
                        </div>
                    </button >)}
            </div >;
    }
    @computed get fillPicker() {
        const fillPicker = this.toggleButton("shape fill color", this._fillBtn, () => this._fillBtn = !this._fillBtn, "fill-drip",
            <div className="color-previewI" style={{ backgroundColor: ActiveFillColor() ?? "121212" }} />);
        return !this._fillBtn ? fillPicker :
            <div className="btn-group" key="fill" >
                {fillPicker}
                {this._palette.map(color =>
                    <button className="antimodeMenu-button" key={color}
                        onPointerDown={action(() => { this.changeColor(color, "fill"); this._fillBtn = false; this.editProperties(color, "fill"); })}
                        style={{ backgroundColor: this._fillBtn ? "121212" : "", zIndex: 1001 }}>
                        <div className="color-previewII" style={{ backgroundColor: color }}>
                            {color === "" ? <p style={{ fontSize: 40, color: "red", marginTop: -10, marginLeft: -5, position: "fixed" }}>☒</p> : ""}
                        </div>
                    </button>)}

            </div>;
    }

    render() {
        return !this.props.docView.layoutDoc ? (null) :
            <div className="collectionFreeFormMenu-cont">
                <RichTextMenu key="rich" />
                {!this.isText ?
                    <>
                        {this.drawButtons}
                        {this.widthPicker}
                        {this.colorPicker}
                        {this.fillPicker}
                        {Doc.UserDoc().noviceMode || this.props.isDoc ? (null) :
                            <>
                                <Tooltip key="back" title={<div className="dash-tooltip">Back Frame</div>} placement="bottom">
                                    <div className="backKeyframe" onClick={this.prevKeyframe}>
                                        <FontAwesomeIcon icon={"caret-left"} size={"lg"} />
                                    </div>
                                </Tooltip>
                                <Tooltip key="num" title={<div className="dash-tooltip">Toggle View All</div>} placement="bottom">
                                    <div className="numKeyframe" style={{ color: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? "white" : "black", backgroundColor: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? "#5B9FDD" : "#AEDDF8" }}
                                        onClick={action(() => this.props.docView.ComponentView?.setKeyFrameEditing?.(!this.props.docView.ComponentView?.getKeyFrameEditing?.()))} >
                                        {NumCast(this.document._currentFrame)}
                                    </div>
                                </Tooltip>
                                <Tooltip key="fwd" title={<div className="dash-tooltip">Forward Frame</div>} placement="bottom">
                                    <div className="fwdKeyframe" onClick={this.nextKeyframe}>
                                        <FontAwesomeIcon icon={"caret-right"} size={"lg"} />
                                    </div>
                                </Tooltip>
                            </>}
                    </> : (null)
                }
                {!this.selectedDocumentView?.ComponentView?.menuControls ? (null) : this.selectedDocumentView?.ComponentView?.menuControls?.()}
            </div>;
    }
}
@observer
export class CollectionStackingViewChrome extends React.Component<CollectionViewMenuProps> {
    @observable private _currentKey: string = "";
    @observable private suggestions: string[] = [];

    get document() { return this.props.docView.props.Document; }

    @computed private get descending() { return StrCast(this.document._columnsSort) === "descending"; }
    @computed get pivotField() { return StrCast(this.document._pivotField); }

    getKeySuggestions = async (value: string): Promise<string[]> => {
        const val = value.toLowerCase();
        const docs = DocListCast(this.document[this.props.fieldKey]);

        if (Doc.UserDoc().noviceMode) {
            if (docs instanceof Doc) {
                const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
                    key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
                    (key[0].toUpperCase() === key[0] && key[0] !== "_"));
                return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
            } else {
                const keys = new Set<string>();
                docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
                const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
                    key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
                    (key[0]?.toUpperCase() === key[0] && key[0] !== "_"));
                return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
            }
        }

        if (docs instanceof Doc) {
            return Object.keys(docs).filter(key => key.toLowerCase().indexOf(val) > -1);
        } else {
            const keys = new Set<string>();
            docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
            return Array.from(keys).filter(key => key.toLowerCase().indexOf(val) > -1);
        }
    }

    @action
    onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
        this._currentKey = newValue;
    }

    getSuggestionValue = (suggestion: string) => suggestion;

    renderSuggestion = (suggestion: string) => {
        return <p>{suggestion}</p>;
    }

    onSuggestionFetch = async ({ value }: { value: string }) => {
        const sugg = await this.getKeySuggestions(value);
        runInAction(() => {
            this.suggestions = sugg;
        });
    }

    @action
    onSuggestionClear = () => {
        this.suggestions = [];
    }

    @action
    setValue = (value: string) => {
        this.document._pivotField = value;
        return true;
    }

    @action toggleSort = () => {
        this.document._columnsSort =
            this.document._columnsSort === "descending" ? "ascending" :
                this.document._columnsSort === "ascending" ? undefined : "descending";
    }
    @action resetValue = () => { this._currentKey = this.pivotField; };

    render() {
        const doctype = this.props.docView.Document.type;
        const isPres: boolean = (doctype === DocumentType.PRES);
        return (
            isPres ? (null) : <div className="collectionStackingViewChrome-cont">
                <div className="collectionStackingViewChrome-pivotField-cont">
                    <div className="collectionStackingViewChrome-pivotField-label">
                        GROUP BY:
                    </div>
                    <div className="collectionStackingViewChrome-sortIcon" onClick={this.toggleSort} style={{ transform: `rotate(${this.descending ? "180" : "0"}deg)` }}>
                        <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
                    </div>
                    <div className="collectionStackingViewChrome-pivotField">
                        <EditableView
                            GetValue={() => this.pivotField}
                            autosuggestProps={
                                {
                                    resetValue: this.resetValue,
                                    value: this._currentKey,
                                    onChange: this.onKeyChange,
                                    autosuggestProps: {
                                        inputProps:
                                        {
                                            value: this._currentKey,
                                            onChange: this.onKeyChange
                                        },
                                        getSuggestionValue: this.getSuggestionValue,
                                        suggestions: this.suggestions,
                                        alwaysRenderSuggestions: true,
                                        renderSuggestion: this.renderSuggestion,
                                        onSuggestionsFetchRequested: this.onSuggestionFetch,
                                        onSuggestionsClearRequested: this.onSuggestionClear
                                    }
                                }}
                            oneLine
                            SetValue={this.setValue}
                            contents={this.pivotField ? this.pivotField : "N/A"}
                        />
                    </div>
                </div>
            </div>
        );
    }
}

@observer
export class CollectionNoteTakingViewChrome extends React.Component<CollectionViewMenuProps> {
    @observable private _currentKey: string = "";
    @observable private suggestions: string[] = [];

    get document() { return this.props.docView.props.Document; }

    @computed private get descending() { return StrCast(this.document._columnsSort) === "descending"; }
    @computed get pivotField() { return StrCast(this.document._pivotField); }

    getKeySuggestions = async (value: string): Promise<string[]> => {
        const val = value.toLowerCase();
        const docs = DocListCast(this.document[this.props.fieldKey]);

        if (Doc.UserDoc().noviceMode) {
            if (docs instanceof Doc) {
                const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
                    key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
                    (key[0].toUpperCase() === key[0] && key[0] !== "_"));
                return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
            } else {
                const keys = new Set<string>();
                docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
                const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
                    key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
                    (key[0]?.toUpperCase() === key[0] && key[0] !== "_"));
                return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
            }
        }

        if (docs instanceof Doc) {
            return Object.keys(docs).filter(key => key.toLowerCase().indexOf(val) > -1);
        } else {
            const keys = new Set<string>();
            docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
            return Array.from(keys).filter(key => key.toLowerCase().indexOf(val) > -1);
        }
    }

    @action
    onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
        this._currentKey = newValue;
    }

    getSuggestionValue = (suggestion: string) => suggestion;

    renderSuggestion = (suggestion: string) => {
        return <p>{suggestion}</p>;
    }

    onSuggestionFetch = async ({ value }: { value: string }) => {
        const sugg = await this.getKeySuggestions(value);
        runInAction(() => {
            this.suggestions = sugg;
        });
    }

    @action
    onSuggestionClear = () => {
        this.suggestions = [];
    }

    @action
    setValue = (value: string) => {
        this.document._pivotField = value;
        return true;
    }

    @action toggleSort = () => {
        this.document._columnsSort =
            this.document._columnsSort === "descending" ? "ascending" :
                this.document._columnsSort === "ascending" ? undefined : "descending";
    }
    @action resetValue = () => { this._currentKey = this.pivotField; };

    render() {
        const doctype = this.props.docView.Document.type;
        const isPres: boolean = (doctype === DocumentType.PRES);
        return (
            isPres ? (null) : <div className="collectionStackingViewChrome-cont">
                <div className="collectionStackingViewChrome-pivotField-cont">
                    <div className="collectionStackingViewChrome-pivotField-label">
                        GROUP BY:
                    </div>
                    <div className="collectionStackingViewChrome-sortIcon" onClick={this.toggleSort} style={{ transform: `rotate(${this.descending ? "180" : "0"}deg)` }}>
                        <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
                    </div>
                    <div className="collectionStackingViewChrome-pivotField">
                        <EditableView
                            GetValue={() => this.pivotField}
                            autosuggestProps={
                                {
                                    resetValue: this.resetValue,
                                    value: this._currentKey,
                                    onChange: this.onKeyChange,
                                    autosuggestProps: {
                                        inputProps:
                                        {
                                            value: this._currentKey,
                                            onChange: this.onKeyChange
                                        },
                                        getSuggestionValue: this.getSuggestionValue,
                                        suggestions: this.suggestions,
                                        alwaysRenderSuggestions: true,
                                        renderSuggestion: this.renderSuggestion,
                                        onSuggestionsFetchRequested: this.onSuggestionFetch,
                                        onSuggestionsClearRequested: this.onSuggestionClear
                                    }
                                }}
                            oneLine
                            SetValue={this.setValue}
                            contents={this.pivotField ? this.pivotField : "N/A"}
                        />
                    </div>
                </div>
            </div>
        );
    }
}


@observer
export class CollectionSchemaViewChrome extends React.Component<CollectionViewMenuProps> {
    // private _textwrapAllRows: boolean = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
    get document() { return this.props.docView.props.Document; }

    @undoBatch
    togglePreview = () => {
        const dividerWidth = 4;
        const borderWidth = Number(COLLECTION_BORDER_WIDTH);
        const panelWidth = this.props.docView.props.PanelWidth();
        const previewWidth = NumCast(this.document.schemaPreviewWidth);
        const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
        this.document.schemaPreviewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0;
    }

    @undoBatch
    @action
    toggleTextwrap = async () => {
        const textwrappedRows = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []);
        if (textwrappedRows.length) {
            this.document.textwrappedSchemaRows = new List<string>([]);
        } else {
            const docs = DocListCast(this.document[this.props.fieldKey]);
            const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
            this.document.textwrappedSchemaRows = new List<string>(allRows);
        }
    }


    render() {
        const previewWidth = NumCast(this.document.schemaPreviewWidth);
        const textWrapped = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0;

        return (
            <div className="collectionSchemaViewChrome-cont">
                <div className="collectionSchemaViewChrome-toggle">
                    <div className="collectionSchemaViewChrome-label">Show Preview: </div>
                    <div className="collectionSchemaViewChrome-toggler" onClick={this.togglePreview}>
                        <div className={"collectionSchemaViewChrome-togglerButton" + (previewWidth !== 0 ? " on" : " off")}>
                            {previewWidth !== 0 ? "on" : "off"}
                        </div>
                    </div>
                </div>
            </div >
        );
    }
}

@observer
export class CollectionTreeViewChrome extends React.Component<CollectionViewMenuProps> {

    get document() { return this.props.docView.props.Document; }
    get sortAscending() {
        return this.document[this.props.fieldKey + "-sortAscending"];
    }
    set sortAscending(value) {
        this.document[this.props.fieldKey + "-sortAscending"] = value;
    }
    @computed private get ascending() {
        return Cast(this.sortAscending, "boolean", null);
    }

    @action toggleSort = () => {
        if (this.sortAscending) this.sortAscending = undefined;
        else if (this.sortAscending === undefined) this.sortAscending = false;
        else this.sortAscending = true;
    }

    render() {
        return (
            <div className="collectionTreeViewChrome-cont">
                <button className="collectionTreeViewChrome-sort" onClick={this.toggleSort}>
                    <div className="collectionTreeViewChrome-sortLabel">
                        Sort
                    </div>
                    <div className="collectionTreeViewChrome-sortIcon" style={{ transform: `rotate(${this.ascending === undefined ? "90" : this.ascending ? "180" : "0"}deg)` }}>
                        <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
                    </div>
                </button>
            </div>
        );
    }
}

// Enter scroll speed for 3D Carousel 
@observer
export class Collection3DCarouselViewChrome extends React.Component<CollectionViewMenuProps> {
    get document() { return this.props.docView.props.Document; }
    @computed get scrollSpeed() {
        return this.document._autoScrollSpeed;
    }

    @action
    setValue = (value: string) => {
        const numValue = Number(StrCast(value));
        if (numValue > 0) {
            this.document._autoScrollSpeed = numValue;
            return true;
        }
        return false;
    }

    render() {
        return (
            <div className="collection3DCarouselViewChrome-cont">
                <div className="collection3DCarouselViewChrome-scrollSpeed-cont">
                    {FormattedTextBox.Focused ? <RichTextMenu /> : (null)}
                    <div className="collectionStackingViewChrome-scrollSpeed-label">
                        AUTOSCROLL SPEED:
                    </div>
                    <div className="collection3DCarouselViewChrome-scrollSpeed">
                        <EditableView
                            GetValue={() => StrCast(this.scrollSpeed)}
                            oneLine
                            SetValue={this.setValue}
                            contents={this.scrollSpeed ? this.scrollSpeed : 1000} />
                    </div>
                </div>
            </div>
        );
    }
}

/**
 * Chrome for grid view.
 */
@observer
export class CollectionGridViewChrome extends React.Component<CollectionViewMenuProps> {

    private clicked: boolean = false;
    private entered: boolean = false;
    private decrementLimitReached: boolean = false;
    @observable private resize = false;
    private resizeListenerDisposer: Opt<Lambda>;
    get document() { return this.props.docView.props.Document; }

    componentDidMount() {

        runInAction(() => this.resize = this.props.docView.props.PanelWidth() < 700);

        // listener to reduce text on chrome resize (panel resize)
        this.resizeListenerDisposer = computed(() => this.props.docView.props.PanelWidth()).observe(({ newValue }) => {
            runInAction(() => this.resize = newValue < 700);
        });
    }

    componentWillUnmount() {
        this.resizeListenerDisposer?.();
    }

    get numCols() { return NumCast(this.document.gridNumCols, 10); }

    /**
    * Sets the value of `numCols` on the grid's Document to the value entered.
    */
    onNumColsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        if (e.currentTarget.valueAsNumber > 0) undoBatch(() => this.document.gridNumCols = e.currentTarget.valueAsNumber)();
    }

    /**
     * Sets the value of `rowHeight` on the grid's Document to the value entered.
     */
    // @undoBatch
    // onRowHeightEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
    //     if (e.key === "Enter" || e.key === "Tab") {
    //         if (e.currentTarget.valueAsNumber > 0 && this.document.rowHeight as number !== e.currentTarget.valueAsNumber) {
    //             this.document.rowHeight = e.currentTarget.valueAsNumber;
    //         }
    //     }
    // }

    /**
     * Sets whether the grid is flexible or not on the grid's Document.
     */
    @undoBatch
    toggleFlex = () => {
        this.document.gridFlex = !BoolCast(this.document.gridFlex, true);
    }

    /**
     * Increments the value of numCols on button click
     */
    onIncrementButtonClick = () => {
        this.clicked = true;
        this.entered && (this.document.gridNumCols as number)--;
        undoBatch(() => this.document.gridNumCols = this.numCols + 1)();
        this.entered = false;
    }

    /**
     * Decrements the value of numCols on button click
     */
    onDecrementButtonClick = () => {
        this.clicked = true;
        if (this.numCols > 1 && !this.decrementLimitReached) {
            this.entered && (this.document.gridNumCols as number)++;
            undoBatch(() => this.document.gridNumCols = this.numCols - 1)();
            if (this.numCols === 1) this.decrementLimitReached = true;
        }
        this.entered = false;
    }

    /**
     * Increments the value of numCols on button hover
     */
    incrementValue = () => {
        this.entered = true;
        if (!this.clicked && !this.decrementLimitReached) {
            this.document.gridNumCols = this.numCols + 1;
        }
        this.decrementLimitReached = false;
        this.clicked = false;
    }

    /**
     * Decrements the value of numCols on button hover
     */
    decrementValue = () => {
        this.entered = true;
        if (!this.clicked) {
            if (this.numCols > 1) {
                this.document.gridNumCols = this.numCols - 1;
            }
            else {
                this.decrementLimitReached = true;
            }
        }

        this.clicked = false;
    }

    /**
     * Toggles the value of preventCollision
     */
    toggleCollisions = () => {
        this.document.gridPreventCollision = !this.document.gridPreventCollision;
    }

    /**
     * Changes the value of the compactType
     */
    changeCompactType = (e: React.ChangeEvent<HTMLSelectElement>) => {
        // need to change startCompaction so that this operation will be undoable.
        this.document.gridStartCompaction = e.target.selectedOptions[0].value;
    }

    render() {
        return (
            <div className="collectionGridViewChrome-cont" >
                <span className="grid-control" style={{ width: this.resize ? "25%" : "30%" }}>
                    <span className="grid-icon">
                        <FontAwesomeIcon icon="columns" size="1x" />
                    </span>
                    <input className="collectionGridViewChrome-entryBox" type="number" value={this.numCols} onChange={this.onNumColsChange} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
                    <input className="collectionGridViewChrome-columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" />
                    <input className="collectionGridViewChrome-columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" />
                </span>
                {/* <span className="grid-control">
                    <span className="grid-icon">
                        <FontAwesomeIcon icon="text-height" size="1x" />
                    </span>
                    <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.document.rowHeight as string} onKeyDown={this.onRowHeightEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
                </span> */}
                <span className="grid-control" style={{ width: this.resize ? "12%" : "20%" }}>
                    <input type="checkbox" style={{ marginRight: 5 }} onChange={this.toggleCollisions} checked={!this.document.gridPreventCollision} />
                    <label className="flexLabel">{this.resize ? "Coll" : "Collisions"}</label>
                </span>

                <select className="collectionGridViewChrome-viewPicker"
                    style={{ marginRight: 5 }}
                    onPointerDown={stopPropagation}
                    onChange={this.changeCompactType}
                    value={StrCast(this.document.gridStartCompaction, StrCast(this.document.gridCompaction))}>
                    {["vertical", "horizontal", "none"].map(type =>
                        <option className="collectionGridViewChrome-viewOption"
                            onPointerDown={stopPropagation}
                            value={type}>
                            {this.resize ? type[0].toUpperCase() + type.substring(1) : "Compact: " + type}
                        </option>
                    )}
                </select>

                <span className="grid-control" style={{ width: this.resize ? "12%" : "20%" }}>
                    <input style={{ marginRight: 5 }} type="checkbox" onChange={this.toggleFlex}
                        checked={BoolCast(this.document.gridFlex, true)} />
                    <label className="flexLabel">{this.resize ? "Flex" : "Flexible"}</label>
                </span>

                <button onClick={() => this.document.gridResetLayout = true}>
                    {!this.resize ? "Reset" :
                        <FontAwesomeIcon icon="redo-alt" size="1x" />}
                </button>

            </div>
        );
    }
}
Scripting.addGlobal(function gotoFrame(doc: any, newFrame: any) {
    const dataField = doc[Doc.LayoutFieldKey(doc)];
    const childDocs = DocListCast(dataField);
    const currentFrame = Cast(doc._currentFrame, "number", null);
    if (currentFrame === undefined) {
        doc._currentFrame = 0;
        CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
    }
    CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0);
    doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame);
});

