import { Colors } from '@dash/components';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { IDisposer } from 'mobx-utils';
import * as React from 'react';
import ReactLoading from 'react-loading';
import { ClientUtils, returnEmptyFilter, returnFalse, setupMoveUpEvents } from '../../../../../ClientUtils';
import { emptyFunction } from '../../../../../Utils';
import { Doc, NumListCast, StrListCast, returnEmptyDoclist } from '../../../../../fields/Doc';
import { Id } from '../../../../../fields/FieldSymbols';
import { ImageCast, StrCast } from '../../../../../fields/Types';
import { ImageField } from '../../../../../fields/URLField';
import { Upload } from '../../../../../server/SharedMediaTypes';
import { Networking } from '../../../../Network';
import { GPTCallType, gptAPICall, gptImageCall } from '../../../../apis/gpt/GPT';
import { Docs, DocumentOptions } from '../../../../documents/Documents';
import { DragManager } from '../../../../util/DragManager';
import { SnappingManager } from '../../../../util/SnappingManager';
import { Transform } from '../../../../util/Transform';
import { UndoManager, undoable } from '../../../../util/UndoManager';
import { ObservableReactComponent } from '../../../ObservableReactComponent';
import { DefaultStyleProvider } from '../../../StyleProvider';
import { CollectionFreeFormView } from '../../../collections/collectionFreeForm/CollectionFreeFormView';
import { DocumentView, DocumentViewInternal } from '../../DocumentView';
import { OpenWhere } from '../../OpenWhere';
import { DataVizBox } from '../DataVizBox';
import './DocCreatorMenu.scss';
import { TemplateField, ViewType } from './TemplateFieldTypes/TemplateField';
import { Template } from './Template';
import { TemplateFieldSize, TemplateFieldType, TemplateLayouts } from './TemplateBackend';
import { Conditional, TemplateManager } from './Backend/TemplateManager';
import { DrawingFillHandler } from '../../../smartdraw/DrawingFillHandler';
import { CgPathIntersect } from 'react-icons/cg';
import { StaticContentField } from './TemplateFieldTypes/StaticContentField';
import { TemplateMenuAIUtils } from './Backend/TemplateMenuAIUtils'
import { TemplatePreviewGrid } from './Menu/TemplatePreviewGrid';
import { FireflyStructureOptions, TemplateEditingWindow } from './Menu/TemplateEditingWindow';
import { DocCreatorMenuButton } from './Menu/DocCreatorMenuButton';
import { ConditionalsTextArea } from './Menu/ConditionalsTextarea';
import { TemplatesRenderPreviewWindow } from './Menu/TemplateRenderPreviewWindow';
import { TemplateMenuFieldOptions } from './Menu/TemplateMenuFieldOptions';

export enum LayoutType {
    FREEFORM = 'Freeform',
    CAROUSEL = 'Carousel',
    CAROUSEL3D = '3D Carousel',
    MASONRY = 'Masonry',
    CARD = 'Card View',
}

export interface DataVizTemplateInfo {
    doc: Doc;
    layout: { type: LayoutType; xMargin: number; yMargin: number; repeat: number };
    columns: number;
    referencePos: { x: number; y: number };
}

export interface DataVizTemplateLayout {
    template: Doc;
    docsNumList: number[];
    layout: { type: LayoutType; xMargin: number; yMargin: number; repeat: number };
    columns: number;
    rows: number;
}

export type Col = {
    sizes: TemplateFieldSize[];
    desc: string;
    title: string;
    type: TemplateFieldType;
    defaultContent?: string;
    AIGenerated?: boolean;
};

interface DocCreateMenuProps {
    addDocTab: (doc: Doc | Doc[], where: OpenWhere) => boolean;
}

@observer
export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> {
    // eslint-disable-next-line no-use-before-define
    static Instance: DocCreatorMenu;

    DEBUG_MODE: boolean = false;
    private _ref: HTMLDivElement | null = null;
    private templateManager: TemplateManager;

    @observable _docsRendering: boolean = false; // dictates loading symbol

    @observable _userTemplates: Template[] = [];
    @observable _selectedTemplate: Template | undefined = undefined;
    @observable _currEditingTemplate: Template | undefined = undefined;
    @observable _editedTemplateTrail: Template[] = [];

    @observable _userCreatedFields: Col[] = [];

    @observable _suggestedTemplates: Template[] = [];
    @observable _GPTLoading: boolean = false;

    @observable _pageX: number = 0;
    @observable _pageY: number = 0;

    @observable _hoveredLayoutPreview: number | undefined = undefined;
    @observable _mouseX: number = -1;
    @observable _mouseY: number = -1;
    @observable _startPos?: { x: number; y: number };
    @observable _shouldDisplay: boolean = false;

    @observable _menuContent: 'templates' | 'renderPreview' | 'saved' | 'dashboard' | 'templateEditing' = 'templates';
    @observable _dragging: boolean = false;
    @observable _dataViz?: DataVizBox;
    @observable _interactionLock: boolean | undefined;
    @observable _snapPt: { x: number; y: number } = { x: 0, y: 0 };
    @observable _resizeHdlId: string = '';
    @observable _resizing: boolean = false;
    @observable _offset: { x: number; y: number } = { x: 0, y: 0 };
    @observable _resizeUndo: UndoManager.Batch | undefined = undefined;
    @observable _initDimensions: { width: number; height: number; x?: number; y?: number } = { width: 300, height: 400, x: undefined, y: undefined };
    @observable _menuDimensions: { width: number; height: number } = { width: 400, height: 400 };

    @observable _variations: Template[] = [];

    constructor(props: DocCreateMenuProps) {
        super(props);
        makeObservable(this);
        DocCreatorMenu.Instance = this;
        this.templateManager = new TemplateManager(TemplateLayouts.allTemplates);
    }

    @action setDataViz = (dataViz: DataVizBox) => {
        this._dataViz = dataViz;
        this._selectedTemplate = undefined;
        this._suggestedTemplates = [];
        this._userCreatedFields = [];
    };
    @action setSuggestedTemplates = (templates: Template[]) => {
        this._suggestedTemplates = templates; //prettier-ignore
    };

    @computed get selectedFields() {
        return StrListCast(this._dataViz?.layoutDoc._dataViz_axes);
    }

    @computed get fieldsInfos(): Col[] {
        const colInfo = this._dataViz?.colsInfo;
        return this.selectedFields
            .map(field => {
                const fieldInfo = colInfo?.get(field);

                const col: Col = {
                    title: field,
                    type: fieldInfo?.type ?? TemplateFieldType.UNSET,
                    desc: fieldInfo?.desc ?? '',
                    sizes: fieldInfo?.sizes ?? [TemplateFieldSize.MEDIUM],
                };

                if (fieldInfo?.defaultContent !== undefined) {
                    col.defaultContent = fieldInfo.defaultContent;
                }

                return col;
            })
            .concat(this._userCreatedFields);
    }

    get bounds(): { t: number; b: number; l: number; r: number } {
        const rect = this._ref?.getBoundingClientRect();
        const bounds = { t: rect?.top ?? 0, b: rect?.bottom ?? 0, l: rect?.left ?? 0, r: rect?.right ?? 0 };
        return bounds;
    }

    setUpButtonClick = (e: React.PointerEvent, func: (...args: any) => void) => {
        setupMoveUpEvents(
            this,
            e,
            returnFalse,
            emptyFunction,
            undoable(clickEv => {
                clickEv.stopPropagation();
                clickEv.preventDefault();
                func();
            }, 'create docs')
        );
    };

    @action
    onPointerDown = (e: PointerEvent) => {
        this._mouseX = e.clientX;
        this._mouseY = e.clientY;
    };

    @action
    onPointerUp = (e: PointerEvent) => {
        if (this._resizing) {
            this._initDimensions.width = this._menuDimensions.width;
            this._initDimensions.height = this._menuDimensions.height;
            this._initDimensions.x = this._pageX;
            this._initDimensions.y = this._pageY;
            document.removeEventListener('pointermove', this.onResize);
            SnappingManager.SetIsResizing(undefined);
            this._resizing = false;
        }
        if (this._dragging) {
            document.removeEventListener('pointermove', this.onDrag);
            this._dragging = false;
        }
        if (e.button !== 2 && !e.ctrlKey) return;
        const curX = e.clientX;
        const curY = e.clientY;
        if (Math.abs(this._mouseX - curX) > 1 || Math.abs(this._mouseY - curY) > 1) {
            this._shouldDisplay = false;
        }
    };

    componentDidMount() {
        document.addEventListener('pointerdown', this.onPointerDown, true);
        document.addEventListener('pointerup', this.onPointerUp);
    }

    componentWillUnmount() {
        document.removeEventListener('pointerdown', this.onPointerDown, true);
        document.removeEventListener('pointerup', this.onPointerUp);
    }

    @action
    toggleDisplay = (x: number, y: number) => {
        if (this._shouldDisplay) {
            this._shouldDisplay = false;
        } else {
            this._pageX = x;
            this._pageY = y;
            this._shouldDisplay = true;
        }
    };

    @action
    closeMenu = () => {
        this._shouldDisplay = false;
    };

    @action
    openMenu = () => {
        this._shouldDisplay = true;
    };

    @action
    onResizePointerDown = (e: React.PointerEvent): void => {
        this._resizing = true;
        document.addEventListener('pointermove', this.onResize);
        SnappingManager.SetIsResizing(DocumentView.Selected().lastElement()?.Document[Id]); // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them
        e.stopPropagation();
        const id = (this._resizeHdlId = e.currentTarget.className);
        const pad = id.includes('Left') || id.includes('Right') ? Number(getComputedStyle(e.target as HTMLElement).width.replace('px', '')) / 2 : 0;
        const bounds = e.currentTarget.getBoundingClientRect();
        this._offset = {
            x: id.toLowerCase().includes('left') ? bounds.right - e.clientX - pad : bounds.left - e.clientX + pad, //
            y: id.toLowerCase().includes('top') ? bounds.bottom - e.clientY - pad : bounds.top - e.clientY + pad,
        };
        this._resizeUndo = UndoManager.StartBatch('drag resizing');
        this._snapPt = { x: e.pageX, y: e.pageY };
    };

    @action
    onResize = (e: PointerEvent): boolean => {
        const dragHdl = this._resizeHdlId.split(' ')[1];
        const thisPt = DragManager.snapDrag(e, -this._offset.x, -this._offset.y, this._offset.x, this._offset.y);

        const { scale, refPt, transl } = this.getResizeVals(thisPt, dragHdl);
        !this._interactionLock && runInAction(async () => {   // resize selected docs if we're not in the middle of a resize (ie, throttle input events to frame rate)
            this._interactionLock = true;
            const scaleAspect = {x: scale.x, y: scale.y};
            this.resizeView(refPt, scaleAspect, transl); // prettier-ignore
            await new Promise<boolean | undefined>(res => { setTimeout(() => { res(this._interactionLock = undefined)})});
        }); // prettier-ignore
        return true;
    };

    @action
    onDrag = (e: PointerEvent): boolean => {
        this._pageX = e.pageX - (this._startPos?.x ?? 0);
        this._pageY = e.pageY - (this._startPos?.y ?? 0);
        this._initDimensions.x = this._pageX;
        this._initDimensions.y = this._pageY;
        return true;
    };

    getResizeVals = (thisPt: { x: number; y: number }, dragHdl: string) => {
        const [w, h] = [this._initDimensions.width, this._initDimensions.height];
        const [moveX, moveY] = [thisPt.x - this._snapPt!.x, thisPt.y - this._snapPt!.y];
        let vals: { scale: { x: number; y: number }; refPt: [number, number]; transl: { x: number; y: number } };
        switch (dragHdl) {
            case 'topLeft':    vals = { scale: { x: 1 - moveX / w, y: 1 -moveY / h },  refPt: [this.bounds.r, this.bounds.b], transl: {x: moveX, y: moveY } }; break;
            case 'topRight':   vals = { scale: { x: 1 + moveX / w, y: 1 -moveY / h },  refPt: [this.bounds.l, this.bounds.b], transl: {x: 0, y: moveY } }; break;
            case 'top':        vals = { scale: { x: 1,             y: 1 -moveY / h },  refPt: [this.bounds.l, this.bounds.b], transl: {x: 0, y: moveY } }; break;
            case 'left':       vals = { scale: { x: 1 - moveX / w, y: 1 },             refPt: [this.bounds.r, this.bounds.t], transl: {x: moveX, y: 0 } }; break;
            case 'bottomLeft': vals = { scale: { x: 1 - moveX / w, y: 1 + moveY / h }, refPt: [this.bounds.r, this.bounds.t], transl: {x: moveX, y: 0 } }; break;
            case 'right':      vals = { scale: { x: 1 + moveX / w, y: 1 },             refPt: [this.bounds.l, this.bounds.t], transl: {x: 0, y: 0 } }; break;
            case 'bottomRight':vals = { scale: { x: 1 + moveX / w, y: 1 + moveY / h }, refPt: [this.bounds.l, this.bounds.t], transl: {x: 0, y: 0 } }; break;
            case 'bottom':     vals = { scale: { x: 1,             y: 1 + moveY / h }, refPt: [this.bounds.l, this.bounds.t], transl: {x: 0, y: 0 } }; break;
            default:           vals = { scale: { x: 1,             y: 1 },             refPt: [this.bounds.l, this.bounds.t], transl: {x: 0, y: 0 } }; break;
        } // prettier-ignore
        return vals;
    };

    resizeView = (refPt: number[], scale: { x: number; y: number }, translation: { x: number; y: number }) => {
        if (this._initDimensions.x === undefined) this._initDimensions.x = this._pageX;
        if (this._initDimensions.y === undefined) this._initDimensions.y = this._pageY;
        const { height, width, x, y } = this._initDimensions;

        this._menuDimensions.width = Math.max(300, scale.x * width);
        this._menuDimensions.height = Math.max(200, scale.y * height);
        this._pageX = x + translation.x;
        this._pageY = y + translation.y;
    };
    
    async createDocsForPreview(): Promise<Doc[]> { return this._dataViz && this._selectedTemplate ? ((await this.templateManager.createDocsFromTemplate(this._dataViz, this._selectedTemplate, this.fieldsInfos, this.DEBUG_MODE)).filter(doc => doc).map(doc => doc!) ?? []) as unknown as Doc[] : []; }

    @action updateSelectedTemplate = async (template: Template) => {
        if (this._selectedTemplate === template) {
            this._selectedTemplate = undefined;
            return;
        } else {
            this._selectedTemplate = template;
        }
    };

    // testTemplate = async () => {
    //     this._suggestedTemplates = this.templateManager.templates; //prettier-ignore
    // };

    @action addField = () => {
        const newFields: Col[] = this._userCreatedFields.concat([{ title: '', type: TemplateFieldType.UNSET, desc: '', sizes: [], AIGenerated: true }]);
        this._userCreatedFields = newFields;
    };

    @action removeField = (field: { title: string; type: string; desc: string }) => {
        if (this._dataViz?.axes.includes(field.title)) {
            this._dataViz.selectAxes(this._dataViz.axes.filter(col => col !== field.title));
        } else {
            const toRemove = this._userCreatedFields.filter(f => f === field);
            if (!toRemove) return;

            if (toRemove.length > 1) {
                while (toRemove.length > 1) {
                    toRemove.pop();
                }
            }

            if (this._userCreatedFields.length === 1) {
                this._userCreatedFields = [];
            } else {
                this._userCreatedFields.splice(this._userCreatedFields.indexOf(toRemove[0]), 1);
            }
        }
    };

    @action setColTitle = (column: Col, title: string) => {
        if (this.selectedFields.includes(column.title)) {
            this._dataViz?.setColumnTitle(column.title, title);
        } else {
            column.title = title;
        }
        this.forceUpdate();
    };

    @action setColType = (column: Col, type: TemplateFieldType) => {
        if (type === TemplateFieldType.DATA) {
            this.templateManager.addDataField(column.title);
        } else if (column.type === TemplateFieldType.DATA) {
            this.templateManager.removeDataField(column.title);
        }

        if (this.selectedFields.includes(column.title)) {
            this._dataViz?.setColumnType(column.title, type);
        } else {
            column.type = type;
        }

        this.forceUpdate();
    };

    modifyColSizes = (column: Col, size: TemplateFieldSize, valid: boolean) => {
        if (this.selectedFields.includes(column.title)) {
            this._dataViz?.modifyColumnSizes(column.title, size, valid);
        } else {
            if (!valid && column.sizes.includes(size)) {
                column.sizes.splice(column.sizes.indexOf(size), 1);
            } else if (valid && !column.sizes.includes(size)) {
                column.sizes.push(size);
            }
        }
        this.forceUpdate();
    };

    setColDesc = (column: Col, desc: string) => {
        if (this.selectedFields.includes(column.title)) {
            this._dataViz?.setColumnDesc(column.title, desc);
        } else {
            column.desc = desc;
        }
        this.forceUpdate();
    };

    compileFieldDescriptions = (templates: Template[]): string => {
        let descriptions: string = '';
        templates.forEach(template => {
            descriptions += `----------  NEW TEMPLATE TO INCLUDE: The title is: ${template.title}. Its fields are: `;
            descriptions += template.descriptionSummary;
        });

        return descriptions;
    };

    compileColDescriptions = (cols: Col[]): string => {
        let descriptions: string = ' ------------- COL DESCRIPTIONS START HERE:';
        cols.forEach(col => (descriptions += `{title: ${col.title}, sizes: ${String(col.sizes)}, type: ${col.type}, descreiption: ${col.desc}} `));

        return descriptions;
    };

    getColByTitle = (title: string) => {
        return this.fieldsInfos.filter(col => col.title === title)[0];
    };

    @action
    assignColsToFields = async (templates: Template[], cols: Col[]): Promise<[Template, { [field: number]: Col }][]> => {
        const fieldDescriptions: string = this.compileFieldDescriptions(templates);
        const colDescriptions: string = this.compileColDescriptions(cols);

        const inputText = fieldDescriptions.concat(colDescriptions);

        const prompt: string = `(${Math.random() * 100000}) ${inputText}`;

        this._GPTLoading = true;

        try {
            const res = await gptAPICall(prompt, GPTCallType.TEMPLATE);

            if (res) {
                const assignments: { [templateTitle: string]: { [fieldID: string]: string } } = JSON.parse(res);
                const brokenDownAssignments: [Template, { [fieldID: number]: Col }][] = [];

                Object.entries(assignments).forEach(([tempTitle, assignment]) => {
                    const template = templates.filter(temp => temp.title === tempTitle)[0];
                    if (!template) return;
                    const toObj = Object.entries(assignment).reduce(
                        (a, [fieldID, colTitle]) => {
                            const col = this.getColByTitle(colTitle);
                            if (!col.AIGenerated) {
                                var field = template.getFieldByID(Number(fieldID));
                                field.setContent(col.defaultContent ?? '', col.type === TemplateFieldType.VISUAL ? ViewType.IMG : ViewType.TEXT);
                                field = template.getFieldByID(Number(fieldID));
                                field.setTitle(col.title);
                            } else {
                                a[Number(fieldID)] = this.getColByTitle(colTitle);
                            }
                            return a;
                        },
                        {} as { [field: number]: Col }
                    );
                    brokenDownAssignments.push([template, toObj]);
                });

                return brokenDownAssignments;
            }
        } catch (err) {
            console.error(err);
        }

        return [];
    };

    generatePresetTemplates = async () => {
        const templates: Template[] = [];

        if (this.DEBUG_MODE) {
            templates.push(...this.templateManager.templates);
        } else {
            this._dataViz?.updateColDefaults();

            const contentFields = this.fieldsInfos.filter(field => field.type !== TemplateFieldType.DATA);

            templates.push(...this.templateManager.getValidTemplates(contentFields));

            const assignments = await this.assignColsToFields(templates, contentFields);

            const renderedTemplatePromises = assignments.map(([template, assgns]) => TemplateMenuAIUtils.applyGPTContentToTemplate(template, assgns));

            await Promise.all(renderedTemplatePromises);
        }

        setTimeout(
            action(() => {
                this.setSuggestedTemplates(templates);
                this._GPTLoading = false;
            })
        );
    };

    generateVariations = async (onDoc: Doc, prompt: string, options: FireflyStructureOptions): Promise<string[]> => {
        const { numVariations, temperature, useStyleRef } = options;
        this.variations = [];
        const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView;

        const clone: Doc = (await Doc.MakeClone(onDoc)).clone;
        mainCollection.addDocument(clone);
        clone.x = 10000;
        clone.y = 10000;

        await DrawingFillHandler.drawingToImage(clone, 100 - temperature, prompt, useStyleRef ? clone : undefined, this, numVariations)

        return this.variations;
    }

    variations: string[] = []

    @action addVariation = (url: string) => {
        this.variations.push(url);
    }

    addRenderedCollectionToMainview = (collection: Doc) => {
        if (collection) {
            const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView;
            collection.x = this._pageX - this._menuDimensions.width;
            collection.y = this._pageY - this._menuDimensions.height;
            mainCollection?.addDocument(collection);
            this.closeMenu();
        }
    };

    @action editLastTemplate = () => { if (this._editedTemplateTrail.length) this._currEditingTemplate = this._editedTemplateTrail.pop()}

    @action setExpandedView = (template: Template | undefined) => {

        if (template) {
            this._menuContent = 'templateEditing';
            this._currEditingTemplate && this._editedTemplateTrail.push(this._currEditingTemplate);
        } else {
            this._menuContent = 'templates';
        }

        this._currEditingTemplate = template;

        //Docs.Create.FreeformDocument([doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: ''});
    };

    @computed 
    get templatesView() { return (
        <div className='docCreatorMenu-templates-view'>
            <div className="docCreatorMenu-templates-displays">
                <TemplatePreviewGrid 
                    title={'Suggested Templates'} 
                    menu={this} 
                    loading={this._GPTLoading}
                    optionsButtonOpts={this.optionsButtonOpts}
                    templates={this._suggestedTemplates}
                />
                <div className="docCreatorMenu-GPT-options">
                    <div className="docCreatorMenu-GPT-options-container">
                        <DocCreatorMenuButton icon={'arrows-rotate'} styles={'border'} function={this.generatePresetTemplates}/>
                    </div>
                </div>
            </div>
        </div>
    )};
    
    private optionsButtonOpts: [IconProp, () => any] = ['gear', () => (this._menuContent = 'dashboard')];

    get renderSelectedViewType() {
        switch (this._menuContent) {
            case 'templates':       return this.templatesView;
            case 'templateEditing': return <TemplateEditingWindow template={this._currEditingTemplate as Template} menu={this} />;
            case 'renderPreview':   return <TemplatesRenderPreviewWindow menu={this}/>;
            case 'dashboard':       return <TemplateMenuFieldOptions menu={this} templateManager={this.templateManager}/>;
        } // prettier-ignore
        return undefined;
    }

    get resizePanes() {
        const ref = this._ref?.getBoundingClientRect();
        const height: number = ref?.height ?? 0;
        const width: number = ref?.width ?? 0;

        return [
            <div className='docCreatorMenu-resizer top' key='0' onPointerDown={this.onResizePointerDown} style={{width: width, left: 0, top: -7}}/>, 
            <div className='docCreatorMenu-resizer left' key='1' onPointerDown={this.onResizePointerDown} style={{height: height, left: -7, top: 0}}/>,
            <div className='docCreatorMenu-resizer right' key='2' onPointerDown={this.onResizePointerDown} style={{height: height, left: width - 3, top: 0}}/>, 
            <div className='docCreatorMenu-resizer bottom' key='3' onPointerDown={this.onResizePointerDown} style={{width: width, left: 0, top: height - 3}}/>,
            <div className='docCreatorMenu-resizer topLeft' key='4' onPointerDown={this.onResizePointerDown} style={{left: -10, top: -10, cursor: 'nwse-resize'}}/>,
            <div className='docCreatorMenu-resizer topRight' key='5' onPointerDown={this.onResizePointerDown} style={{left: width - 5, top: -10, cursor: 'nesw-resize'}}/>,
            <div className='docCreatorMenu-resizer bottomLeft' key='6' onPointerDown={this.onResizePointerDown} style={{left: -10, top: height - 5, cursor: 'nesw-resize'}}/>,
            <div className='docCreatorMenu-resizer bottomRight' key='7' onPointerDown={this.onResizePointerDown} style={{left: width - 5, top: height - 5, cursor: 'nwse-resize'}}/>,
        ]; //prettier-ignore
    }

    render() {
        const topButton = (icon: string, opt: string, func: () => void, tag: string) => (
            <div className={`top-button-container ${tag} ${opt === this._menuContent ? 'selected' : ''}`}>
                <div className="top-button-content" onPointerDown={e => this.setUpButtonClick(e, action(func))}>
                    <FontAwesomeIcon icon={icon as IconProp} />
                </div>
            </div>
        );

        const onPreviewSelected = () => (this._menuContent = 'templates');
        const onSavedSelected = () => (this._menuContent = 'dashboard');
        const onOptionsSelected = () => (this._menuContent = 'renderPreview');

        return (
            <div className="docCreatorMenu">
                {!this._shouldDisplay ? undefined : (
                    <div
                        className="docCreatorMenu-cont"
                        ref={r => (this._ref = r)}
                        style={{
                            display: '',
                            left: this._pageX,
                            top: this._pageY,
                            width: this._menuDimensions.width,
                            height: this._menuDimensions.height,
                            background: SnappingManager.userBackgroundColor,
                            color: SnappingManager.userColor,
                        }}>
                        {this.resizePanes}
                        <div
                            className="docCreatorMenu-menu"
                            onPointerDown={e =>
                                setupMoveUpEvents(
                                    this,
                                    e,
                                    event => {
                                        this._dragging = true;
                                        this._startPos = { x: 0, y: 0 };
                                        this._startPos.x = event.pageX - (this._ref?.getBoundingClientRect().left ?? 0);
                                        this._startPos.y = event.pageY - (this._ref?.getBoundingClientRect().top ?? 0);
                                        document.addEventListener('pointermove', this.onDrag);
                                        return true;
                                    },
                                    emptyFunction,
                                    undoable(clickEv => clickEv.stopPropagation(), 'drag menu')
                                )
                            }>
                            <div className="docCreatorMenu-top-buttons-container">
                                {topButton('lightbulb', 'templates', onPreviewSelected, 'left')}
                                {topButton('magnifying-glass', 'options', onOptionsSelected, 'middle')}
                                {topButton('bars', 'saved', onSavedSelected, 'right')}
                            </div>
                            <DocCreatorMenuButton icon={'minus'} styles={'float-right'} function={this.closeMenu}/>
                        </div>
                        {this.renderSelectedViewType}
                    </div>
                )}
            </div>
        );
    }
}