import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { returnAll, returnFalse, setupMoveUpEvents } from '../../../../ClientUtils';
import { Doc, NumListCast, StrListCast } from '../../../../fields/Doc';
import { DocCast, ImageCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { emptyFunction } from '../../../../Utils';
import { SnappingManager } from '../../../util/SnappingManager';
import { UndoManager, undoable } from '../../../util/UndoManager';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { DocumentView } from '../DocumentView';
import { DataVizBox } from './DataVizBox';
import './DocCreatorMenu.scss';
import { Id } from '../../../../fields/FieldSymbols';
import { Colors, IconButton, Size } from 'browndash-components';
import { MakeTemplate } from '../../../util/DropConverter';
import { DragManager } from '../../../util/DragManager';
import { GPTCallType, gptAPICall } from '../../../apis/gpt/GPT';
import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
import { Docs } from '../../../documents/Documents';

export enum LayoutType {
    Stacked = 'stacked', 
    Grid = 'grid', 
    Row = 'row', 
    Column = 'column', 
    Custom = 'custom'
}

@observer
export class DocCreatorMenu extends ObservableReactComponent<{}> {

    static Instance: DocCreatorMenu;

    private _ref: HTMLDivElement | null = null;

    @observable _templateDocs: Doc[] = [];
    @observable _selectedTemplate: Doc | undefined = undefined;

    @observable _layout: {type: LayoutType, yMargin: number, xMargin: number, columns?: number, repeat: number} = {type: LayoutType.Grid, yMargin: 0, xMargin: 0, repeat: 0};
    @observable _layoutPreview: boolean = true;
    @observable _layoutPreviewScale: number = 1;
    @observable _savedLayouts: DataVizTemplateLayout[] = [];

    @observable _GPTTemplates: Doc[] = [];
    @observable _GPTOpt: boolean = false;
    @observable _userPrompt: string = '';

    @observable _pageX: number = 0;
    @observable _pageY: number = 0;
    @observable _indicatorX: number | undefined = undefined;
    @observable _indicatorY: number | undefined = undefined;
    @observable _display: boolean = false;

    @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' | 'options' | 'saved' = 'templates';
    @observable _dragging: boolean = false;
    @observable _draggingIndicator: boolean = false;
    @observable _dataViz?: DataVizBox;
    @observable _interactionLock: any;
    @observable _snapPt: any;
    @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: 300, height: 365};

    constructor(props: any) {
        super(props);
        makeObservable(this);
        DocCreatorMenu.Instance = this;
    }

    @action setDataViz = (dataViz: DataVizBox) => { this._dataViz = dataViz };
    @action setTemplateDocs = (docs: Doc[]) => {this._templateDocs = docs.map(doc => doc.annotationOn ? DocCast(doc.annotationOn):doc)};
    @action setGPTTemplates = (docs: Doc[]) => {this._GPTTemplates = docs};

    @computed get docsToRender() {
        return this._selectedTemplate ? NumListCast(this._dataViz?.layoutDoc.dataViz_selectedRows) : [];
    }

    @computed get rowsCount(){
        switch (this._layout.type) {
            case LayoutType.Row: case LayoutType.Stacked: 
                return 1;
            case LayoutType.Column:
                return this.docsToRender.length;
            case LayoutType.Grid:
                return Math.ceil(this.docsToRender.length / (this._layout.columns ?? 1)) ?? 0;
            default: 
                return 0;
        }
    }

    @computed get columnsCount(){
        switch (this._layout.type) {
            case LayoutType.Row:
                return this.docsToRender.length;
            case LayoutType.Column: case LayoutType.Stacked: 
                return 1;
            case LayoutType.Grid:
                return this._layout.columns ?? 0;
            default:
                return 0;
        }
    }

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

    @computed get canMakeDocs(){
        return this._selectedTemplate !== undefined && this._layout !== undefined;
    }

    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: any, func: Function) => {
        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;
        }
    };

    _disposer: IReactionDisposer | undefined;
    _GPTDisposer: IReactionDisposer | undefined;
    componentDidMount() {
        document.addEventListener('pointerdown', this.onPointerDown, true);
        document.addEventListener('pointerup', this.onPointerUp);
        this._disposer = reaction(() => this._templateDocs.slice(), (docs) => docs.map(this.getIcon));
        this._GPTDisposer = reaction(() => this._GPTTemplates.slice(), (docs) => docs.map(this.getIcon));
    }

    componentWillUnmount() {
        this._disposer?.();
        this._GPTDisposer?.();
        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 = () => {
        const wasOpen = this._display;
        this._display = false;
        this._shouldDisplay = false;
        return wasOpen;
    };

    @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 any).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: any): 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<any>(res => { setTimeout(() => { res(this._interactionLock = undefined)})});
        }); // prettier-ignore
        return true;
    }

    @action
    onDrag = (e: any): 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}) => {
        const refCent = [refPt[0], refPt[1]] // fixed reference point for resize (ie, a point that doesn't move)
        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 getIcon(doc: Doc) {
        const docView = DocumentView.getDocumentView(doc);
        if (docView) {
            docView.ComponentView?.updateIcon?.();
            const f = new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 500));
            console.log(f)
            return f;
        }
        return undefined;
    }

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

    @action updateSelectedSavedLayout = (layout: DataVizTemplateLayout) => {
        this._layout.xMargin = layout.layout.xMargin;
        this._layout.yMargin = layout.layout.yMargin;
        this._layout.type = layout.layout.type;
        this._layout.columns = layout.columns;
    }

    isSelectedLayout = (layout: DataVizTemplateLayout) => {
        return this._layout.xMargin === layout.layout.xMargin
        && this._layout.yMargin === layout.layout.yMargin 
        && this._layout.type === layout.layout.type 
        && this._layout.columns === layout.columns;
    }

    @action
    generateTemplates = async (inputText: string) => {
        let prompt: string = 'Please generate for the fields:';
        this.selectedFields?.forEach(field => prompt += ` ${field},`)
        prompt += ` Additional prompt: ${inputText}`;
        console.log(prompt)

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

            if (res) {
                const templates: {template_type: string, fieldVals: {title: string, tlx: string, tly: string, brx: string, bry: string}[]}[] = JSON.parse(res);
                this.createGeneratedTemplates(templates, 500, 500);
            }
        } catch (err) {
            console.error(err);
        }

    }

    @action
    createGeneratedTemplates = (layouts: {template_type: string, fieldVals: {title: string, tlx: string, tly: string, brx: string, bry: string}[]}[], tempWidth: number, tempHeight: number) => { 
        const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView;
        const GPTTemplates: Doc[] = [];

        layouts.forEach(layout => {
            const fields: Doc[] = layout.fieldVals.map(field => {
                const left: number = Number(field.tlx) * tempWidth / 2;    const top: number = Number(field.tly) * tempHeight / 2; //prettier-ignore
                const right: number = Number(field.brx) * tempWidth / 2;   const bottom: number = Number(field.bry) * tempHeight / 2; //prettier-ignore
                const height = bottom - top; 
                const width = right - left; 
                const doc = !field.title.includes('$$') ? Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, x: left, y: top, _text_fontSize: `${height/2}` }) : Docs.Create.ImageDocument('', { _height: height, _width: width, title: field.title.replace(/$$/g, ''), x: left, y: top });
                return doc;
            });

            const template = Docs.Create.FreeformDocument(fields, { _height: tempHeight, _width: tempWidth, title: layout.template_type, x: 40000, y: 40000 });
            mainCollection.addDocument(template);
            
            GPTTemplates.push(template);
        });

        setTimeout(() => this.setGPTTemplates(GPTTemplates), 500);

        this.forceUpdate();
    }

    editTemplate = () => {

    }

    copyGPTTemplate = () => {

    }

    get templatesPreviewContents(){
        const renderedTemplates: Doc[] = [];

        const GPTOptions = 
            <div></div>

        return (
            <div className='docCreatorMenu-templates-view'>
                <div className='docCreatorMenu-section' style={{height: this._GPTOpt ? 200 : 165}}>
                    <div className='docCreatorMenu-section-topbar'>
                        <div className='docCreatorMenu-section-title'>Suggested Templates</div>
                        <button className='docCreatorMenu-menu-button section-reveal-options' onPointerDown={e => this.setUpButtonClick(e, () => this._GPTOpt = !this._GPTOpt)}>
                            <FontAwesomeIcon icon='gear'/>
                        </button>
                    </div>
                    <div className='docCreatorMenu-GPT-templates-preview'>
                        {this._GPTTemplates?.map(doc => 

                        //<div className='docCreatorMenu-preview-window empty'/>
                        ({icon: ImageCast(doc.icon), doc})).filter(info => info.icon && info.doc).map(info =>
                            <div 
                            className='docCreatorMenu-preview-window'
                            style={{
                                border: this._selectedTemplate === info.doc ? `solid 3px ${Colors.MEDIUM_BLUE}` : '',
                                boxShadow: this._selectedTemplate === info.doc ? `0 0 15px rgba(68, 118, 247, .8)` : ''
                            }}
                            onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(info.doc)))}>
                                <button className='option-button left' onPointerDown={e => this.setUpButtonClick(e, () => {this.editTemplate()})}>
                                    <FontAwesomeIcon icon='pencil' color='black'/>
                                </button>
                                <button className='option-button right' onPointerDown={e => this.setUpButtonClick(e, () => {this.copyGPTTemplate()})}>
                                    <FontAwesomeIcon icon='plus' color='black'/>
                                </button>
                                <img className='docCreatorMenu-preview-image' src={info.icon!.url.href.replace(".png", "_o.png")} />
                            </div>
                        )}
                    </div>
                    {this._GPTOpt ? (<div className='docCreatorMenu-GPT-options'>
                        <div className='docCreatorMenu-GPT-options-container'>
                            <button className='docCreatorMenu-menu-button' onPointerDown={e => this.setUpButtonClick(e, () => this.generateTemplates(this._userPrompt))}>
                                <FontAwesomeIcon icon='arrows-rotate'/>
                            </button>
                            <input className='docCreatorMenu-GPT-prompt-input' placeholder='Additional prompt:' onChange={e => this._userPrompt = e.target.value}/>
                        </div>
                        {this._GPTOpt ? GPTOptions : null}
                    </div>) : null}
                </div>
                <hr className='docCreatorMenu-option-divider full no-margin'/>
                <div className='docCreatorMenu-section'>
                    <div className='docCreatorMenu-section-topbar'>
                        <div className='docCreatorMenu-section-title'>Your Templates</div>
                        <button className='docCreatorMenu-menu-button section-reveal-options' onPointerDown={e => this.setUpButtonClick(e, () => this._GPTOpt = !this._GPTOpt)}>
                            <FontAwesomeIcon icon='gear'/>
                        </button>
                    </div>
                    <div className='docCreatorMenu-GPT-templates-preview'>
                        <div className='docCreatorMenu-preview-window empty'
                        onPointerDown={e => this.setUpButtonClick(e, this.basicTemplateTest)}
                        >
                            <FontAwesomeIcon icon='plus' color='rgb(160, 160, 160)'/>
                        </div>
                        {this._templateDocs.map(doc => ({icon: ImageCast(doc.icon), doc})).filter(info => info.icon && info.doc).map(info => {
                            if (renderedTemplates.includes(info.doc)) return undefined;
                            renderedTemplates.push(info.doc);
                            return (<div 
                            className='docCreatorMenu-preview-window'
                            style={{
                                border: this._selectedTemplate === info.doc ? `solid 3px ${Colors.MEDIUM_BLUE}` : '',
                                boxShadow: this._selectedTemplate === info.doc ? `0 0 15px rgba(68, 118, 247, .8)` : ''
                            }}
                            onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(info.doc)))}>
                                <img className='docCreatorMenu-preview-image' src={info.icon!.url.href.replace(".png", "_o.png")} />
                            </div>
                        )})}
                    </div>
                </div>
            </div>
        );
    }

    get savedLayoutsPreviewContents(){
        return (
            <div className='docCreatorMenu-preview-container'>
               {this._savedLayouts.map((layout, index) => 
                <div 
                    className='docCreatorMenu-preview-window'
                    style={{
                        border: this.isSelectedLayout(layout) ? `solid 3px ${Colors.MEDIUM_BLUE}` : '',
                        boxShadow: this.isSelectedLayout(layout) ? `0 0 15px rgba(68, 118, 247, .8)` : ''
                    }}
                    onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedSavedLayout(layout)))}
                    >
                    {this.layoutPreviewContents(87, layout, true, index)}
                </div>
               )}
            </div>
        );
    }

    @action updateXMargin = (input: string) => { this._layout.xMargin = Number(input) };
    @action updateYMargin = (input: string) => { this._layout.yMargin = Number(input) };
    @action updateColumns = (input: string) => { this._layout.columns = Number(input) };

    get layoutConfigOptions() {
        const optionInput = (icon: string, func: Function, def?: number, key?: string, noMargin?: boolean) => {
            return (
                <div className='docCreatorMenu-option-container small no-margin' key={key} style={{marginTop: noMargin ? '0px' : ''}}
                >
                    <div className='docCreatorMenu-option-title config layout-config'>
                        <FontAwesomeIcon icon={icon as any}/>
                    </div>
                    <input defaultValue={def} onInput={(e) => func(e.currentTarget.value)} className='docCreatorMenu-input config layout-config'/>
                </div>
            );
        }

        switch (this._layout.type) {
            case LayoutType.Row:
                return (
                    <div className='docCreatorMenu-configuration-bar'>
                        {optionInput('arrows-left-right', this.updateXMargin, this._layout.xMargin, '0')}
                    </div>
                    );
            case LayoutType.Column:
                return (
                    <div className='docCreatorMenu-configuration-bar'>
                        {optionInput('arrows-up-down', this.updateYMargin, this._layout.yMargin, '1')}
                    </div>
                    );
            case LayoutType.Grid:
                return (
                    <div className='docCreatorMenu-configuration-bar'>
                        {optionInput('arrows-up-down', this.updateYMargin, this._layout.xMargin, '2')}
                        {optionInput('arrows-left-right', this.updateXMargin, this._layout.xMargin, '3')}
                        {optionInput('table-columns', this.updateColumns, this._layout.columns, '4', true)}
                    </div>
                );
            case LayoutType.Stacked:
                return null;
            default:
                break;
        }
    }

    layoutPreviewContents = (outerSpan: number, altLayout?: DataVizTemplateLayout, small: boolean = false, id?: number) => {
        const doc: Doc | undefined = altLayout ? altLayout.template : this._selectedTemplate;
        if (!doc) return;

        const layout = altLayout ? altLayout.layout : this._layout;

        const docWidth: number = Number(doc._width);
        const docHeight: number = Number(doc._height);
        const horizontalSpan: number = (docWidth + layout.xMargin) * (altLayout ? altLayout.columns : this.columnsCount) - layout.xMargin;;
        const verticalSpan: number = (docHeight + layout.yMargin) * (altLayout ? altLayout.rows : this.rowsCount) - layout.yMargin;
        const largerSpan: number = horizontalSpan > verticalSpan ? horizontalSpan : verticalSpan;
        const scaledDown = (input: number) => {return input / (largerSpan / outerSpan * this._layoutPreviewScale)}
        const fontSize = Math.min(scaledDown(docWidth / 3), scaledDown(docHeight / 3));

        return (
            <div className='docCreatorMenu-layout-preview-window-wrapper' id={String(id) ?? undefined}>
                <div className='docCreatorMenu-zoom-button-container'>
                    <button 
                        className='docCreatorMenu-zoom-button'
                        onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this._layoutPreviewScale *= 1.25))}>
                            <FontAwesomeIcon icon={'minus'}/>
                    </button>
                    <button 
                        className='docCreatorMenu-zoom-button zoom-in'
                        onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this._layoutPreviewScale *= .75))}>
                            <FontAwesomeIcon icon={'plus'}/>
                    </button>
                    {altLayout ? <button 
                        className='docCreatorMenu-zoom-button trash'
                        onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this._savedLayouts.splice(this._savedLayouts.indexOf(altLayout), 1)))}>
                            <FontAwesomeIcon icon={'trash'}/>
                    </button> : null}
                </div>
                <div 
                id={String(id) ?? undefined}
                className={`docCreatorMenu-layout-preview-window ${small ? 'small' : ''}`}
                style={{
                    gridTemplateColumns: `repeat(${altLayout ? altLayout.columns : this.columnsCount}, ${scaledDown(docWidth)}px`,
                    gridTemplateRows: `${scaledDown(docHeight)}px`,
                    gridAutoRows: `${scaledDown(docHeight)}px`,
                    rowGap: `${scaledDown(layout.yMargin)}px`,
                    columnGap: `${scaledDown(layout.xMargin)}px` 
                }}>
                    {this._layout.type === LayoutType.Stacked ? 
                    <div 
                    className='docCreatorMenu-layout-preview-item'
                    style={{
                        width: scaledDown(docWidth), 
                        height: scaledDown(docHeight),
                        fontSize: fontSize,
                    }}
                    >
                        All
                    </div> : 
                    this.docsToRender.map(num => 
                        <div 
                        onMouseEnter={() => this._dataViz?.setSpecialHighlightedRow(num)}
                        onMouseLeave={() => this._dataViz?.setSpecialHighlightedRow(undefined)}
                        className='docCreatorMenu-layout-preview-item'
                        style={{
                            width: scaledDown(docWidth), 
                            height: scaledDown(docHeight),
                            fontSize: fontSize,
                        }}
                        >
                            {num}
                        </div>
                    )}

                </div>
            </div>
        );
    }

    get optionsMenuContents(){
        const layoutEquals = (layout: DataVizTemplateLayout) => {

        } //TODO: ADD LATER

        const layoutOption = (option: LayoutType, optStyle?: {}, specialFunc?: Function) => {
            return (
                <div 
                    className="docCreatorMenu-dropdown-option" 
                    style={optStyle}
                    onPointerDown={e => this.setUpButtonClick(e, () => {specialFunc?.(); runInAction(() => this._layout.type = option)})}>
                    {option}
                </div>
            );
        }

        const selectionBox = (width: number, height: number, icon: string, specClass?: string, options?: JSX.Element[], manual?: boolean): JSX.Element => {
            return (<div className='docCreatorMenu-option-container'>
                <div className={`docCreatorMenu-option-title config ${specClass}`} style={{width: width * .4, height: height}}>
                    <FontAwesomeIcon icon={icon as any}/>
                </div>
                {manual ? <input className={`docCreatorMenu-input config ${specClass}`} style={{width: width * .6, height: height}}/> :
                <select className={`docCreatorMenu-input config ${specClass}`} style={{width: width * .6, height: height}}>
                    {options}
                </select>
                }
            </div>);
        }

        const repeatOptions = [0, 1, 2, 3, 4, 5];

        return (
            <div className='docCreatorMenu-menu-container'>
                <div className='docCreatorMenu-option-container layout'>
                    <div className='docCreatorMenu-dropdown-hoverable'>
                        <div className="docCreatorMenu-option-title">{this._layout.type ? this._layout.type.toUpperCase() : 'Choose Layout'}</div>
                        <div className="docCreatorMenu-dropdown-content">
                            {layoutOption(LayoutType.Stacked)}
                            {layoutOption(LayoutType.Grid, undefined, () => {if (!this._layout.columns) this._layout.columns = Math.ceil(Math.sqrt(this.docsToRender.length))})}
                            {layoutOption(LayoutType.Row)}
                            {layoutOption(LayoutType.Column)}
                            {layoutOption(LayoutType.Custom, {borderBottom: `0px`})}
                        </div>
                    </div>
                    <button 
                        className='docCreatorMenu-menu-button preview-toggle'
                        onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this._layoutPreview = !this._layoutPreview))}>
                        <FontAwesomeIcon icon={this._layoutPreview ? 'minus' : 'magnifying-glass'}/>
                    </button>
                </div>
                {this._layout.type ? this.layoutConfigOptions: null}
                {this._layoutPreview ? this.layoutPreviewContents(this._menuDimensions.width * .75) : null}
                {selectionBox(60, 20, 'repeat', undefined, repeatOptions.map(num => <option onPointerDown={e => this._layout.repeat = num}>{`${num}x`}</option>))}
                <hr className='docCreatorMenu-option-divider'/>
                <div className='docCreatorMenu-general-options-container'>
                    <button
                        className='docCreatorMenu-save-layout-button'
                        onPointerDown={e => setupMoveUpEvents( this, e, returnFalse, emptyFunction,
                            undoable(clickEv => {
                                clickEv.stopPropagation();
                                if (!this._selectedTemplate) return;
                                const layout: DataVizTemplateLayout = {template: this._selectedTemplate, layout: {type: this._layout.type, xMargin: this._layout.xMargin, yMargin:this._layout.yMargin, repeat: 0}, columns: this.columnsCount, rows: this.rowsCount, docsNumList: this.docsToRender};
                                if (!this._savedLayouts.includes(layout)) { this._savedLayouts.push(layout) };
                            }, 'make docs') 
                        )
                    }>
                        <FontAwesomeIcon icon='floppy-disk'/>
                    </button>
                    <button 
                        className='docCreatorMenu-create-docs-button'
                        style={{backgroundColor: this.canMakeDocs ? '' : 'rgb(155, 155, 155)', border: this.canMakeDocs ? '' : 'solid 2px rgb(180, 180, 180)'}}
                        onPointerDown={e => setupMoveUpEvents( this, e, returnFalse, emptyFunction,
                                undoable(clickEv => {
                                    clickEv.stopPropagation();
                                    if (!this._selectedTemplate) return;
                                    const templateInfo: DataVizTemplateInfo = {doc: this._selectedTemplate, layout: this._layout, referencePos: {x: this._pageX + 450, y: this._pageY}, columns: this.columnsCount};
                                    this._dataViz?.createDocsFromTemplate(templateInfo);
                                }, 'make docs') 
                            )
                        }>
                         <FontAwesomeIcon icon='plus'/>
                    </button>
                </div>
            </div>
        );
    }

    basicTemplateTest = () => {
        console.log(this._GPTTemplates)
        this.forceUpdate();
        //this._dataViz?.createBasicTemplates(temps);
    }

    get renderSelectedViewType(){
        switch (this._menuContent){
            case 'templates':
                return this.templatesPreviewContents;
            case 'options':
                return this.optionsMenuContents;
            case 'saved':
                return this.savedLayoutsPreviewContents;
            default:
                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' onPointerDown={this.onResizePointerDown} style={{width: width, left: 0, top: -7}}/>, 
            <div className='docCreatorMenu-resizer right' onPointerDown={this.onResizePointerDown} style={{height: height, left: width - 3, top: 0}}/>, 
            <div className='docCreatorMenu-resizer bottom' onPointerDown={this.onResizePointerDown} style={{width: width, left: 0, top: height - 3}}/>, 
            <div className='docCreatorMenu-resizer left' onPointerDown={this.onResizePointerDown} style={{height: height, left: -7, top: 0}}/>,
            <div className='docCreatorMenu-resizer topRight' onPointerDown={this.onResizePointerDown} style={{left: width - 5, top: -10, cursor: 'nesw-resize'}}/>,
            <div className='docCreatorMenu-resizer topLeft' onPointerDown={this.onResizePointerDown} style={{left: -10, top: -10, cursor: 'nwse-resize'}}/>,
            <div className='docCreatorMenu-resizer bottomRight' onPointerDown={this.onResizePointerDown} style={{left: width - 5, top: height - 5, cursor: 'nwse-resize'}}/>,
            <div className='docCreatorMenu-resizer bottomLeft' onPointerDown={this.onResizePointerDown} style={{left: -10, top: height - 5, cursor: 'nesw-resize'}}/>
        ]; //prettier-ignore
    }

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

        const onPreviewSelected = () => {this._menuContent = 'templates'}
        const onSavedSelected = () => {this._menuContent = 'saved'}
        const onOptionsSelected = () => {
            this._menuContent = 'options';
            if (!this._layout.columns) this._layout.columns = Math.ceil(Math.sqrt(this.docsToRender.length));
        }


        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,
                                (e) => {
                                    this._dragging = true;
                                    this._startPos = {x: 0, y: 0};
                                    this._startPos.x = e.pageX - (this._ref?.getBoundingClientRect().left ?? 0);
                                    this._startPos.y = e.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('table-cells', 'templates', onPreviewSelected, 'left')}
                                {topButton('bars', 'options', onOptionsSelected, 'middle')}
                                {topButton('floppy-disk', 'saved', onSavedSelected, 'right')}
                            </div>
                            <button 
                                className='docCreatorMenu-menu-button close-menu'
                                onPointerDown={e => this.setUpButtonClick(e, this.closeMenu)}>
                                <FontAwesomeIcon icon={'minus'}/>
                            </button>
                        </div>
                        {this.renderSelectedViewType}
                </div>
                }
            </div>
            )
        }
}

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;
}