import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Colors } from 'browndash-components';
import { action, computed, makeObservable, observable, reaction, 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, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils';
import { emptyFunction } from '../../../../Utils';
import { Doc, NumListCast, StrListCast, returnEmptyDoclist } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { Cast, DocCast, ImageCast, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { Networking } from '../../../Network';
import { GPTCallType, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { MakeTemplate } from '../../../util/DropConverter';
import { SnappingManager } from '../../../util/SnappingManager';
import { UndoManager, undoable } from '../../../util/UndoManager';
import { LightboxView } from '../../LightboxView';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView';
import { DocumentView, DocumentViewInternal } from '../DocumentView';
import { FieldViewProps } from '../FieldView';
import { OpenWhere } from '../OpenWhere';
import { DataVizBox } from './DataVizBox';
import './DocCreatorMenu.scss';
import { DefaultStyleProvider, returnEmptyDocViewList } from '../../StyleProvider';
import { Transform } from '../../../util/Transform';

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

@observer
export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
    static Instance: DocCreatorMenu;

    private _disposers: { [name: string]: IDisposer } = {};

    private _ref: HTMLDivElement | null = null;

    @observable _templateDocs: Doc[] = [];
    @observable _selectedTemplate: Doc | undefined = undefined;
    @observable _columns: Col[] = [];
    @observable _selectedCols: { title: string; type: string; desc: string }[] | 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 _expandedPreview: { icon: ImageField; doc: Doc } | undefined = undefined;

    @observable _suggestedTemplates: Doc[] = [];
    @observable _GPTOpt: boolean = false;
    @observable _userPrompt: string = '';
    @observable _callCount: number = 0;
    @observable _GPTLoading: boolean = false;

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

    @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' | 'dashboard' = '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: 400, height: 400 };
    @observable _editing: boolean = false;

    constructor(props: any) {
        super(props);
        makeObservable(this);
        DocCreatorMenu.Instance = this;
        //setTimeout(() => this.generateTemplates(''));
    }

    @action setDataViz = (dataViz: DataVizBox) => {
        this._dataViz = dataViz;
    };
    @action setTemplateDocs = (docs: Doc[]) => {
        this._templateDocs = docs.map(doc => (doc.annotationOn ? DocCast(doc.annotationOn) : doc));
    };
    @action setGSuggestedTemplates = (docs: Doc[]) => {
        this._suggestedTemplates = 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 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._columns);
    }

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

    componentDidMount() {
        document.addEventListener('pointerdown', this.onPointerDown, true);
        document.addEventListener('pointerup', this.onPointerUp);
        this._disposers.templates = reaction(
            () => this._templateDocs.slice(),
            docs => this.updateIcons(docs)
        );
        this._disposers.gpt = reaction(
            () => this._suggestedTemplates.slice(),
            docs => this.updateIcons(docs)
        );
        //this._disposers.columns = reaction(() => this._dataViz?.layoutDoc._dataViz_axes, () => {this.generateTemplates('')})
        this._disposers.lightbox = reaction(
            () => LightboxView.LightboxDoc(),
            doc => {
                doc ? this._shouldDisplay && this.closeMenu() : !this._shouldDisplay && this.openMenu();
            }
        );
        //this._disposers.fields = reaction(() => this._dataViz?.axes, cols => this._selectedCols = cols?.map(col => { return {title: col, type: '', desc: ''}}))
    }

    componentWillUnmount() {
        Object.values(this._disposers).forEach(disposer => disposer?.());
        document.removeEventListener('pointerdown', this.onPointerDown, true);
        document.removeEventListener('pointerup', this.onPointerUp);
    }

    updateIcons = (docs: Doc[]) => {
        console.log('called')
        docs.map(this.getIcon);
    };

    @action
    updateSelectedCols = (cols: string[]) => {
        this._selectedCols;
    };

    @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 = () => {
        const allTemplates = this._templateDocs.concat(this._suggestedTemplates);
        this._shouldDisplay = true;
        this.updateIcons(allTemplates);
    };

    @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?.();
            return new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 500));
        }
        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) => {
        ++this._callCount;
        const origCount = this._callCount;

        let prompt: string = `(#${origCount}) Please generate for the fields:`;
        this.selectedFields?.forEach(field => (prompt += ` ${field},`));
        prompt += ` (-----NOT A FIELD-----) Additional prompt: ${inputText}`;

        this._GPTLoading = true;

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

            if (res && this._callCount === origCount) {
                this._suggestedTemplates = [];
                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: 400000, y: 400000 });

            mainCollection.addDocument(template);

            GPTTemplates.push(template);
        });

        setTimeout(() => {
            this.setGSuggestedTemplates(GPTTemplates); /*GPTTemplates.forEach(template => mainCollection.removeDocument(template))*/
        }, 100);

        this.forceUpdate();
    };

    editTemplate = (doc: Doc) => {
        //this.closeMenu();
        DocumentViewInternal.addDocTabFunc(doc, OpenWhere.addRight);
        DocumentView.DeselectAll();
        Doc.UnBrushDoc(doc);
    };

    removeTemplate = (doc: Doc) => {
        this._templateDocs.splice(this._templateDocs.indexOf(doc), 1);
    };

    testTemplate = async () => {
        this.updateIcons(this._suggestedTemplates.slice());
        this.forceUpdate();

        // try {
        //     const res = await gptImageCall('Image of panda eating a cookie');

        //     if (res) {
        //         const result = await Networking.PostToServer('/uploadRemoteImage', { sources: res });

        //         console.log(result);
        //     }
        // } catch (e) {
        //     console.log(e);
        // }
    };

    @action addField = () => {
        const newFields: Col[] = this._columns.concat([{ title: '', type: TemplateFieldType.UNSET, desc: '', sizes: [] }]);
        this._columns = 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._columns.filter(f => f === field);
            if (!toRemove) return;

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

            if (this._columns.length === 1) {
                this._columns = [];
            } else {
                this._columns.splice(this._columns.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 (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();
    };

    generateGPTImage = async (prompt: string): Promise<string | undefined> => {
        console.log(prompt);

        try {
            const res = await gptImageCall(prompt);

            if (res) {
                const result = await Networking.PostToServer('/uploadRemoteImage', { sources: res });
                const source = ClientUtils.prepend(result[0].accessPaths.agnostic.client);
                return source;
            }
        } catch (e) {
            console.log(e);
        }
    };

    matchesForTemplate = (template: TemplateDocInfos, cols: Col[]): number[][] => {
        const colMatchesField = (col: Col, field: Field) => {
            return field.sizes?.some(size => col.sizes?.includes(size)) && field.types?.includes(col.type);
        };

        const matches: number[][] = Array(template.fields.length)
            .fill([])
            .map(() => []);

        template.fields.forEach((field, i) => {
            cols.forEach((col, v) => {
                if (colMatchesField(col, field)) {
                    matches[i].push(v);
                }
            });
        });

        return matches;
    };

    maxMatches = (fieldsCt: number, matches: number[][]) => {
        const used: boolean[] = Array(fieldsCt).fill(false);
        const mt: number[] = Array(fieldsCt).fill(-1);

        const augmentingPath = (v: number): boolean => {
            if (used[v]) return false;
            used[v] = true;
            for (const to of matches[v]) {
                if (mt[to] === -1 || augmentingPath(mt[to])) {
                    mt[to] = v;
                    return true;
                }
            }
            return false;
        };

        for (let v = 0; v < fieldsCt; ++v) {
            used.fill(false);
            augmentingPath(v);
        }

        let count: number = 0;

        for (let i = 0; i < fieldsCt; ++i) {
            if (mt[i] !== -1) ++count;
        }

        return count;
    };

    findValidTemplates = (cols: Col[], templates: TemplateDocInfos[]) => {
        let validTemplates: any[] = [];
        templates.forEach(template => {
            const numFields = template.fields.length;
            if (!(numFields === cols.length)) return;
            const matches = this.matchesForTemplate(template, cols);
            if (this.maxMatches(numFields, matches) === numFields) {
                validTemplates.push(template.title);
            }
        });

        validTemplates = validTemplates.map(title => TemplateLayouts.getTemplateByTitle(title));

        return validTemplates;
    };

    // createColumnField = (template: TemplateDocInfos, field: Field, column: Col): Doc => {

    //     if (field.subfields) {
    //         const doc = FieldFuncs.FreeformField({
    //             tl: field.tl,
    //             br: field.br },
    //             template.height,
    //             template.width,
    //             column.title,
    //             '',
    //             field.opts
    //         );

    //         field.subfields[1].forEach(f => {
    //             const fDoc = ()
    //         })

    //     }

    //     return new Doc;
    // }

    /**
     * Populates a preset template framework with content from a datavizbox or any AI-generated content.
     * @param template the preloaded template framework being filled in
     * @param assignments a list of template field numbers (from top to bottom) and their assigned columns from the linked dataviz
     * @returns a doc containing the fully rendered template
     */
    fillPresetTemplate = async (template: TemplateDocInfos, assignments: { [field: string]: Col }): Promise<Doc> => {
        const wordLimit = (size: TemplateFieldSize) => {
            switch (size) {
                case TemplateFieldSize.TINY:
                    return 2;
                case TemplateFieldSize.SMALL:
                    return 5;
                case TemplateFieldSize.MEDIUM:
                    return 20;
                case TemplateFieldSize.LARGE:
                    return 50;
                case TemplateFieldSize.HUGE:
                    return 100;
                default:
                    return 10;
            }
        };

        const renderTextCalls = async (): Promise<Doc[]> => {
            const rendered: Doc[] = [];

            if (GPTTextCalls.length) {
                try {
                    const prompt = fieldContent + GPTTextAssignment;

                    const res = await gptAPICall(prompt, GPTCallType.FILL);

                    if (res) {
                        const assignments: { [title: string]: { number: string; content: string } } = JSON.parse(res);
                        //console.log('assignments', GPTAssignments, 'assignment string', GPTAssignmentString, 'field content', fieldContent, 'response', res, 'assignments', assignments);
                        Object.entries(assignments).forEach(([title, info]) => {
                            const field: Field = template.fields[Number(info.number)];
                            const col = this.getColByTitle(title);

                            const doc = FieldUtils.TextField(
                                {
                                    tl: field.tl,
                                    br: field.br,
                                },
                                template.height,
                                template.width,
                                col.title,
                                info.content ?? '',
                                field.opts
                            );

                            rendered.push(doc);
                        });
                    }
                } catch (err) {
                    console.log(err);
                }
            }

            return rendered;
        };

        const createGeneratedImage = async (fieldNum: string, col: Col, prompt: string) => {
            const url = await this.generateGPTImage(prompt);
            const field: Field = template.fields[Number(fieldNum)];
            const doc = FieldUtils.ImageField(
                {
                    tl: field.tl,
                    br: field.br,
                },
                template.height,
                template.width,
                col.title,
                url ?? '',
                field.opts
            );

            return doc;
        };

        const renderImageCalls = async (): Promise<Doc[]> => {
            const rendered: Doc[] = [];
            const calls = GPTIMGCalls;

            if (calls.length) {
                try {
                    const renderedImages: Doc[] = await Promise.all(
                        calls.map(async ([fieldNum, col]) => {
                            const sysPrompt =
                                'Your job is to create a prompt for an AI image generator to help it generate an image based on existing content in a template and a user prompt. Your prompt should focus heavily on visual elements to help the image generator; avoid unecessary info that might distract it. ONLY INCLUDE THE PROMPT, NO OTHER TEXT OR EXPLANATION. The existing content is as follows: ' +
                                fieldContent +
                                ' **** The user prompt is: ' +
                                col.desc;

                            const prompt = await gptAPICall(sysPrompt, GPTCallType.COMPLETEPROMPT);
                            console.log(sysPrompt, prompt);

                            return createGeneratedImage(fieldNum, col, prompt);
                        })
                    );

                    const renderedTemplates: Doc[] = await Promise.all(renderedImages);
                    renderedTemplates.forEach(doc => rendered.push(doc));
                } catch (e) {
                    console.log(e);
                }
            }

            return rendered;
        };

        const fields: Doc[] = [];

        const GPTAssignments = Object.entries(assignments).filter(([f, col]) => this._columns.includes(col));
        const nonGPTAssignments: [string, Col][] = Object.entries(assignments).filter(a => !GPTAssignments.includes(a));
        const GPTTextCalls = GPTAssignments.filter(([str, col]) => col.type === TemplateFieldType.TEXT);
        const GPTIMGCalls = GPTAssignments.filter(([str, col]) => col.type === TemplateFieldType.VISUAL);

        const stringifyGPTInfo = (calls: [string, Col][]): string => {
            let string: string = '*** COLUMN INFO:';
            calls.forEach(([fieldNum, col]) => {
                string += `--- title: ${col.title}, prompt: ${col.desc}, word limit: ${wordLimit(col.sizes[0])} words, assigned field: ${fieldNum} ---`;
            });
            return (string += ' ***');
        };

        const GPTTextAssignment = stringifyGPTInfo(GPTTextCalls);

        let fieldContent: string = '';

        Object.entries(nonGPTAssignments).forEach(([f, strCol]) => {
            const field: Field = template.fields[Number(f)];
            const col = strCol[1];

            const doc = (col.type === TemplateFieldType.VISUAL ? FieldUtils.ImageField : FieldUtils.TextField)(
                {
                    tl: field.tl,
                    br: field.br,
                },
                template.height,
                template.width,
                col.title,
                col.defaultContent ?? '',
                field.opts
            );

            fieldContent += `--- Field #${f} (title: ${col.title}): ${col.defaultContent ?? ''} ---`;

            fields.push(doc);
        });

        template.decorations.forEach(dec => {
            const doc = FieldUtils.FreeformField(
                {
                    tl: dec.tl,
                    br: dec.br,
                },
                template.height,
                template.width,
                '',
                '',
                dec.opts
            );

            fields.push(doc);
        });

        const createMainDoc = (): Doc => {
            const main = Docs.Create.FreeformDocument(fields, {
                _height: template.height,
                _width: template.width,
                title: template.title,
                backgroundColor: template.opts.backgroundColor,
                _layout_borderRounding: `${template.opts.cornerRounding}px` ?? '0px',
                borderWidth: template.opts.borderWidth,
                borderColor: template.opts.borderColor,
                x: 40000,
                y: 40000,
            });

            const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView;
            mainCollection.addDocument(main);

            return main;
        };

        const textCalls = await renderTextCalls();
        const imageCalls = await renderImageCalls();

        textCalls.forEach(doc => {
            fields.push(doc);
        });
        imageCalls.forEach(doc => {
            fields.push(doc);
        });

        return createMainDoc();
    };

    compileFieldDescriptions = (templates: TemplateDocInfos[]): string => {
        let descriptions: string = '';
        templates.forEach(template => {
            descriptions += `----------  NEW TEMPLATE TO INCLUDE: Description of template ${template.title}'s fields: `;
            template.fields.forEach((field, index) => {
                descriptions += `{Field #${index}: ${field.description}} `;
            });
        });

        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: TemplateDocInfos[], cols: Col[]): Promise<[TemplateDocInfos, { [field: number]: Col }][]> => {
        const fieldDescriptions: string = this.compileFieldDescriptions(templates);
        const colDescriptions: string = this.compileColDescriptions(cols);

        const inputText = fieldDescriptions.concat(colDescriptions);

        ++this._callCount;
        const origCount = this._callCount;

        let prompt: string = `(${origCount}) ${inputText}`;

        this._GPTLoading = true;

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

            if (res && this._callCount === origCount) {
                const assignments: { [templateTitle: string]: { [field: string]: string } } = JSON.parse(res);
                const brokenDownAssignments: [TemplateDocInfos, { [field: number]: Col }][] = [];

                Object.entries(assignments).forEach(([tempTitle, assignment]) => {
                    const template = TemplateLayouts.getTemplateByTitle(tempTitle);
                    if (!template) return;
                    const toObj = Object.entries(assignment).reduce(
                        (a, [fieldNum, colTitle]) => {
                            a[Number(fieldNum)] = this.getColByTitle(colTitle);
                            return a;
                        },
                        {} as { [field: number]: Col }
                    );
                    brokenDownAssignments.push([template, toObj]);
                });
                return brokenDownAssignments;
            }
        } catch (err) {
            console.error(err);
        }

        return [];
    };

    generatePresetTemplates = async () => {
        this._dataViz?.updateColDefaults();

        const cols = this.fieldsInfos;
        const templates = this.findValidTemplates(cols, TemplateLayouts.allTemplates);

        const assignments: [TemplateDocInfos, { [field: number]: Col }][] = await this.assignColsToFields(templates, cols);

        const renderedTemplatePromises: Promise<Doc>[] = assignments.map(([template, assignments]) => this.fillPresetTemplate(template, assignments));

        const renderedTemplates: Doc[] = await Promise.all(renderedTemplatePromises);

        setTimeout(() => {
            this.setGSuggestedTemplates(renderedTemplates);
            this._GPTLoading = false;
        });
    };

    @action setExpandedView = (info: { icon: ImageField; doc: Doc } | undefined) => {
        if (info) {
            const doc = info.doc;
            const wrapper: Doc = Docs.Create.FreeformDocument([info.doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: ''});
            const newInfo = {icon: new ImageField(''), doc: wrapper}
            this._expandedPreview = newInfo;
        } else {
            this._expandedPreview = info;
        }
    };

    get editingWindow(){
        const doc = this._expandedPreview?.doc ?? new Doc();
        const rendered = 
            <div className="docCreatorMenu-expanded-template-preview">
                <CollectionFreeFormView
                    Document={this._expandedPreview!.doc}
                    docViewPath={returnEmptyDocViewList}
                    childLayoutTemplate={() => Cast(doc.childLayoutTemplate, Doc, null)}
                    isContentActive={emptyFunction}
                    isAnyChildContentActive={() => true}
                    select={emptyFunction}
                    isSelected={returnFalse}
                    fieldKey={Doc.LayoutFieldKey(doc)}
                    addDocument={returnFalse}
                    moveDocument={returnFalse}
                    removeDocument={returnFalse}
                    PanelWidth={() => this._menuDimensions.width - 10}
                    PanelHeight={() => this._menuDimensions.height - 60}
                    ScreenToLocalTransform={() => new Transform(-this._pageX,-this._pageY, 1)}
                    renderDepth={5}
                    whenChildContentsActiveChanged={emptyFunction}
                    focus={emptyFunction}
                    styleProvider={DefaultStyleProvider}
                    addDocTab={this._props.addDocTab}
                    // eslint-disable-next-line no-use-before-define
                    pinToPres={() => undefined}
                    childFilters={returnEmptyFilter}
                    childFiltersByRanges={returnEmptyFilter}
                    searchFilterDocs={returnEmptyDoclist}
                    fitContentsToBox={returnTrue}
                    xPadding={0}
                    yPadding={0}
                />
            </div>


        return (
            <div className="docCreatorMenu-expanded-template-preview">
                <div className="top-panel"/>
                {rendered}
                <div className="right-buttons-panel">
                    <button className="docCreatorMenu-menu-button section-reveal-options top-right" onPointerDown={e => this.setUpButtonClick(e, () => {this._expandedPreview && this.updateIcons(this._suggestedTemplates.slice()); this.setExpandedView(undefined)})}>
                        <FontAwesomeIcon icon="minimize" />
                    </button>
                    <button className="docCreatorMenu-menu-button section-reveal-options top-right-lower" onPointerDown={e => this.setUpButtonClick(e, () => this._expandedPreview && this._templateDocs.push(this._expandedPreview.doc))}>
                        <FontAwesomeIcon icon="plus" color="white" />
                    </button>
                </div>
            </div>

        );
    }

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

        const GPTOptions = <div></div>;

        //<img className='docCreatorMenu-preview-image expanded' src={this._expandedPreview.icon!.url.href.replace(".png", "_o.png")} />

        return (
            <div className={`docCreatorMenu-templates-view`}>
                {this._expandedPreview ? (
                    this.editingWindow
                ) : (
                    <div>
                        <div className="docCreatorMenu-section" style={{ height: this._GPTOpt ? 200 : 200 }}>
                            <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, () => runInAction(() => (this._menuContent = 'dashboard')))}>
                                    <FontAwesomeIcon icon="gear" />
                                </button>
                            </div>
                            <div className="docCreatorMenu-templates-preview-window" style={{ justifyContent: this._GPTLoading || this._menuDimensions.width > 400 ? 'center' : '' }}>
                                {this._GPTLoading ? (
                                    <div className="loading-spinner">
                                        <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
                                    </div>
                                ) : (
                                    this._suggestedTemplates
                                        ?.map(doc => ({ 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.setExpandedView(info);
                                                        })
                                                    }>
                                                    <FontAwesomeIcon icon="magnifying-glass" color="white" />
                                                </button>
                                                <button className="option-button right" onPointerDown={e => this.setUpButtonClick(e, () => this._templateDocs.push(info.doc))}>
                                                    <FontAwesomeIcon icon="plus" color="white" />
                                                </button>
                                                <img className="docCreatorMenu-preview-image" src={info.icon!.url.href.replace('.png', '_o.png')} />
                                            </div>
                                        ))
                                )}
                            </div>
                            <div className="docCreatorMenu-GPT-options">
                                <div className="docCreatorMenu-GPT-options-container">
                                    <button className="docCreatorMenu-menu-button" onPointerDown={e => this.setUpButtonClick(e, () => this.generatePresetTemplates())}>
                                        <FontAwesomeIcon icon="arrows-rotate" />
                                    </button>
                                </div>
                                {this._GPTOpt ? GPTOptions : null}
                            </div>
                        </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-templates-preview-window" style={{ justifyContent: this._menuDimensions.width > 400 ? 'center' : '' }}>
                                <div className="docCreatorMenu-preview-window empty" onPointerDown={e => this.testTemplate()}>
                                    <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)))}>
                                                <button
                                                    className="option-button left"
                                                    onPointerDown={e =>
                                                        this.setUpButtonClick(e, () => {
                                                            this.editTemplate(info.doc);
                                                        })
                                                    }>
                                                    <FontAwesomeIcon icon="pencil" color="black" />
                                                </button>
                                                <button
                                                    className="option-button right"
                                                    onPointerDown={e =>
                                                        this.setUpButtonClick(e, () => {
                                                            this.removeTemplate(info.doc);
                                                        })
                                                    }>
                                                    <FontAwesomeIcon icon="trash" color="black" />
                                                </button>
                                                <img className="docCreatorMenu-preview-image" src={info.icon!.url.href.replace('.png', '_o.png')} />
                                            </div>
                                        );
                                    })}
                            </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;
        }
    }

    // doc = () => {
    //     return Docs.Create.FreeformDocument([], { _height: 200, _width: 200, title: 'title'});
    // }

    screenToLocalTransform = () => this._props.ScreenToLocalTransform();

    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='divvv' style={{width: 100, height: 100, border: `1px solid white`}}>
            //     <CollectionFreeFormView
            //         // eslint-disable-next-line react/jsx-props-no-spreading
            //         {...this._props}
            //         Document={new Doc()}
            //         isContentActive={returnFalse}
            //         setContentViewBox={emptyFunction}
            //         NativeWidth={() => 100}
            //         NativeHeight={() => 100}
            //         pointerEvents={SnappingManager.IsDragging ? returnAll : returnNone}
            //         isAnnotationOverlay
            //         isAnnotationOverlayScrollable
            //         childDocumentsActive={returnFalse}
            //         fieldKey={this._props.fieldKey + '_annotations'}
            //         dropAction={dropActionType.move}
            //         select={emptyFunction}
            //         addDocument={returnFalse}
            //         removeDocument={returnFalse}
            //         moveDocument={returnFalse}
            //         renderDepth={this._props.renderDepth + 1}>
            //         {null}
            //     </CollectionFreeFormView>
            // </div>
            <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 *= 0.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 * 0.4, height: height }}>
                        <FontAwesomeIcon icon={icon as any} />
                    </div>
                    {manual ? (
                        <input className={`docCreatorMenu-input config ${specClass}`} style={{ width: width * 0.6, height: height }} />
                    ) : (
                        <select className={`docCreatorMenu-input config ${specClass}`} style={{ width: width * 0.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 * 0.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>
        );
    }

    get dashboardContents() {
        const sizes: string[] = ['tiny', 'small', 'medium', 'large', 'huge'];

        const fieldPanel = (field: Col) => {
            return (
                <div className="field-panel">
                    <div className="top-bar">
                        <span className="field-title">{`${field.title} Field`}</span>
                        <button className="docCreatorMenu-menu-button section-reveal-options no-margin" onPointerDown={e => this.setUpButtonClick(e, () => this.removeField(field))} style={{ position: 'absolute', right: '0px' }}>
                            <FontAwesomeIcon icon="minus" />
                        </button>
                    </div>
                    <div className="opts-bar">
                        <div className="opt-box">
                            <div className="top-bar"> Title </div>
                            <textarea className="content" style={{ width: '100%', height: 'calc(100% - 20px)' }} defaultValue={field.title} placeholder={'Enter title'} onChange={e => this.setColTitle(field, e.target.value)} />
                        </div>
                        <div className="opt-box">
                            <div className="top-bar"> Type </div>
                            <div className="content">
                                <span className="type-display">{field.type === TemplateFieldType.TEXT ? 'Text Field' : field.type === TemplateFieldType.VISUAL ? 'File Field' : ''}</span>
                                <div className="bubbles">
                                    <input
                                        className="bubble"
                                        type="radio"
                                        name="type"
                                        onClick={() => {
                                            this.setColType(field, TemplateFieldType.TEXT);
                                        }}
                                    />
                                    <div className="text">Text</div>
                                    <input
                                        className="bubble"
                                        type="radio"
                                        name="type"
                                        onClick={() => {
                                            this.setColType(field, TemplateFieldType.VISUAL);
                                        }}
                                    />
                                    <div className="text">File</div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div className="sizes-box">
                        <div className="top-bar"> Valid Sizes </div>
                        <div className="content">
                            <div className="bubbles">
                                {sizes.map(size => (
                                    <>
                                        <input
                                            className="bubble"
                                            type="checkbox"
                                            name="type"
                                            checked={field.sizes.includes(size as TemplateFieldSize)}
                                            onChange={e => {
                                                this.modifyColSizes(field, size as TemplateFieldSize, e.target.checked);
                                            }}
                                        />
                                        <div className="text">{size}</div>
                                    </>
                                ))}
                            </div>
                        </div>
                    </div>
                    <div className="desc-box">
                        <div className="top-bar"> Prompt </div>
                        <textarea
                            className="content"
                            onChange={e => this.setColDesc(field, e.target.value)}
                            defaultValue={field.desc === this._dataViz?.GPTSummary?.get(field.title)?.desc ? '' : field.desc}
                            placeholder={this._dataViz?.GPTSummary?.get(field.title)?.desc ?? 'Add a description/prompt to help with template generation.'}
                        />
                    </div>
                </div>
            );
        };

        return (
            <div className="docCreatorMenu-dashboard-view">
                <div className="topbar">
                    <button className="docCreatorMenu-menu-button section-reveal-options" onPointerDown={e => this.setUpButtonClick(e, this.addField)}>
                        <FontAwesomeIcon icon="plus" />
                    </button>
                    <button className="docCreatorMenu-menu-button section-reveal-options float-right" onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => (this._menuContent = 'templates')))}>
                        <FontAwesomeIcon icon="arrow-left" />
                    </button>
                </div>
                <div className="panels-container">{this.fieldsInfos.map(field => fieldPanel(field))}</div>
            </div>
        );
    }

    get renderSelectedViewType() {
        switch (this._menuContent) {
            case 'templates':
                return this.templatesPreviewContents;
            case 'options':
                return this.optionsMenuContents;
            case 'saved':
                return this.savedLayoutsPreviewContents;
            case 'dashboard':
                return this.dashboardContents;
            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 = 'dashboard';
        };
        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;
}

export enum TemplateFieldType {
    TEXT = 'text',
    VISUAL = 'visual',
    UNSET = 'unset',
}

export enum TemplateFieldSize {
    TINY = 'tiny',
    SMALL = 'small',
    MEDIUM = 'medium',
    LARGE = 'large',
    HUGE = 'huge',
}

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

type Field = {
    tl: [number, number];
    br: [number, number];
    opts: FieldOpts;
    subfields?: Field[];
    types?: TemplateFieldType[];
    sizes?: TemplateFieldSize[];
    isDecoration?: boolean;
    description?: string;
};

// class ContentField implements Field {
//     tl: [number, number];
//     br: [number, number];
//     opts: FieldOpts;
//     subfields?: Field[];
//     types?: TemplateFieldType[];
//     sizes?: TemplateFieldSize[];
//     description?: string;

//     constructor( tl: [number, number], br: [number, number],
//                         opts: FieldOpts, subfields?: Field[],
//                         types?: TemplateFieldType[],
//                         sizes?: TemplateFieldSize[],
//                         description?: string) {
//         this.tl = tl;
//         this.br = br;
//         this.opts = opts;
//         this.subfields = subfields;
//         this.types = types;
//         this.sizes = sizes;
//         this.description = description;
//     }

//     render = (content: any): Doc => {
//         return new Doc;
//     }
// }

type DecorationField = Field;

type InkDecoration = {};

type TemplateDecorations = Field | InkDecoration;

export interface TemplateDocInfos {
    title: string;
    height: number;
    width: number;
    opts: TemplateOpts;
    fields: Field[];
    decorations: Field[];
}

export interface FieldOpts {
    backgroundColor?: string;
    color?: string;
    cornerRounding?: number;
    borderWidth?: string;
    borderColor?: string;
    contentXCentering?: 'h-left' | 'h-center' | 'h-right';
    contentYCentering?: 'top' | 'center' | 'bottom';
    opacity?: number;
    rotation?: number;
    //animation?: boolean;
    fontBold?: boolean;
    fontTransform?: 'uppercase' | 'lowercase';
    fieldViewType?: 'freeform' | 'stacked';
}

interface TemplateOpts extends FieldOpts {}

export class FieldUtils {
    public static contentFields = (fields: Field[]) => {
        let toRet: Field[] = [];
        fields.forEach(field => {
            if (!field.isDecoration) {
                toRet.push(field);
            }
            toRet = toRet.concat(FieldUtils.contentFields(field.subfields ?? []));
        });

        return toRet;
    };

    public static calculateFontSize = (contWidth: number, contHeight: number, text: string, uppercase: boolean): number => {
        const words: string[] = text.split(/\s+/).filter(Boolean);

        let currFontSize = 1;
        let rowsCount = 1;
        let currTextHeight = currFontSize * rowsCount * 2;

        while (currTextHeight <= contHeight) {
            let wordIndex = 0;
            let currentRowWidth = 0;
            let wordsInCurrRow = 0;
            rowsCount = 1;

            while (wordIndex < words.length) {
                const word = words[wordIndex];
                const wordWidth = word.length * currFontSize * 0.5;
                //console.log(wordWidth)

                if (currentRowWidth + wordWidth <= contWidth) {
                    currentRowWidth += wordWidth;
                    ++wordsInCurrRow;
                } else {
                    if (words.length !== 1 && words.length > wordsInCurrRow) {
                        rowsCount++;
                        currentRowWidth = wordWidth;
                        wordsInCurrRow = 1;
                    } else {
                        break;
                    }
                }

                wordIndex++;
            }

            currTextHeight = rowsCount * currFontSize * 2;
            //console.log(rowsCount, currFontSize, currTextHeight)

            currFontSize += 1;
        }

        return currFontSize - 1;
    };

    private static getDimensions = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number): { width: number; height: number; coord: { x: number; y: number } } => {
        const l = (coords.tl[0] * parentHeight) / 2;
        const t = coords.tl[1] * parentWidth / 2; //prettier-ignore
        const r = (coords.br[0] * parentHeight) / 2;
        const b = coords.br[1] * parentWidth / 2; //prettier-ignore
        const width = r - l;
        const height = b - t;
        const coord = { x: l, y: t };
        //console.log(coords, parentWidth, parentHeight, height);
        return { width, height, coord };
    };

    public static FreeformField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, content: string, opts: FieldOpts) => {
        const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);

        const docWithBasicOpts = Docs.Create.FreeformDocument([], {
            isDefaultTemplateDoc: true,
            _height: height,
            _width: width,
            title: title,
            x: coord.x,
            y: coord.y,
            backgroundColor: opts.backgroundColor ?? '',
            _layout_borderRounding: `${opts.cornerRounding}px` ?? '0px',
            borderColor: opts.borderColor,
            borderWidth: opts.borderWidth,
            opacity: opts.opacity,
            hCentering: opts.contentXCentering,
            _rotation: opts.rotation,
        });

        return docWithBasicOpts;
    };

    public static TextField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, content: string, opts: FieldOpts) => {
        const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);

        const bool = true;

        const docWithBasicOpts = Docs.Create.TextDocument(content, {
            isDefaultTemplateDoc: true,
            _height: height,
            _width: width,
            title: title,
            x: coord.x,
            y: coord.y,
            _text_fontSize: `${FieldUtils.calculateFontSize(width, height, content, true)}`,
            backgroundColor: opts.backgroundColor ?? '',
            text_fontColor: opts.color,
            contentBold: opts.fontBold,
            textTransform: opts.fontTransform,
            color: opts.color,
            _layout_borderRounding: `${opts.cornerRounding}px` ?? '0px',
            borderColor: opts.borderColor,
            borderWidth: opts.borderWidth,
            opacity: opts.opacity,
            hCentering: opts.contentXCentering,
            _rotation: opts.rotation,
        });

        docWithBasicOpts._layout_hideScroll = true;

        return docWithBasicOpts;
    };

    public static ImageField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, content: string, opts: FieldOpts) => {
        const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);

        const doc = Docs.Create.ImageDocument(content, {
            isDefaultTemplateDoc: true,
            _height: height,
            _width: width,
            title: title,
            x: coord.x,
            y: coord.y,
            _layout_fitWidth: false,
            backgroundColor: opts.backgroundColor ?? '',
            _layout_borderRounding: `${opts.cornerRounding}px` ?? '0px',
            borderColor: opts.borderColor,
            borderWidth: opts.borderWidth,
            opacity: opts.opacity,
            _rotation: opts.rotation,
        });

        //setTimeout(() => {doc._height = height; doc._width = width}, 10);

        return doc;
    };

    public static CarouselField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, fields: Doc[]) => {
        const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);

        const doc = Docs.Create.Carousel3DDocument(fields, { _height: height, _width: width, title: title, x: coord.x, y: coord.y, _text_fontSize: `${height / 2}` });

        return doc;
    };
}

export class TemplateLayouts {
    public static get allTemplates(): TemplateDocInfos[] {
        return Object.values(TemplateLayouts).filter(value => typeof value === 'object' && value !== null && 'title' in value) as TemplateDocInfos[];
    }

    public static getTemplateByTitle = (title: string): TemplateDocInfos | undefined => {
        switch (title) {
            case 'fourfield1':
                return TemplateLayouts.FourField001;
            case 'fourfield2':
                return TemplateLayouts.FourField002;
            // case 'fourfield3':
            //     return TemplateLayouts.FourField003;
            case 'fourfield4':
                return TemplateLayouts.FourField004;
            case 'threefield1':
                return TemplateLayouts.ThreeField001;
            case 'threefield2':
                return TemplateLayouts.ThreeField002;
            default:
                break;
        }

        return undefined;
    };

    public static FourField001: TemplateDocInfos = {
        title: 'fourfield1',
        width: 416,
        height: 700,
        opts: {
            backgroundColor: '#C0B887',
            cornerRounding: 20,
            borderColor: '#6B461F',
            borderWidth: '12',
        },
        fields: [
            {
                tl: [-0.95, -1],
                br: [0.95, -0.85],
                types: [TemplateFieldType.TEXT],
                sizes: [TemplateFieldSize.TINY],
                description: 'A title field for very short text that contextualizes the content.',
                opts: {
                    backgroundColor: 'transparent',
                    color: '#F1F0E9',
                    contentXCentering: 'h-center',
                    fontBold: true,
                },
            },
            {
                tl: [-0.87, -0.83],
                br: [0.87, 0.2],
                types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL],
                sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
                description: 'The main focus of the template; could be an image, long text, etc.',
                opts: {
                    cornerRounding: 20,
                    borderColor: '#8F5B25',
                    borderWidth: '6',
                    backgroundColor: '#CECAB9',
                },
            },
            {
                tl: [-0.8, 0.2],
                br: [0.8, 0.3],
                types: [TemplateFieldType.TEXT],
                sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL],
                description: 'A caption for field #2, very short to short text that contextualizes the content of field #2',
                opts: {
                    backgroundColor: 'transparent',
                    contentXCentering: 'h-center',
                    color: '#F1F0E9',
                },
            },
            {
                tl: [-0.87, 0.37],
                br: [0.87, 0.96],
                types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL],
                sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
                description: 'A medium-sized field for medium/long text.',
                opts: {
                    cornerRounding: 15,
                    borderColor: '#8F5B25',
                    borderWidth: '6',
                    backgroundColor: '#CECAB9',
                },
            },
        ],
        decorations: [],
    };

    public static FourField002: TemplateDocInfos = {
        title: 'fourfield2',
        width: 425,
        height: 778,
        opts: {
            backgroundColor: '#242425',
        },
        fields: [
            {
                tl: [-0.83, -0.95],
                br: [0.83, -0.2],
                types: [TemplateFieldType.VISUAL, TemplateFieldType.TEXT],
                sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE],
                description: 'A medium to large-sized field suitable for an image or longer text that should be the main focus.',
                opts: {
                    borderWidth: '8',
                    borderColor: '#F8E71C',
                },
            },
            {
                tl: [-0.65, -0.2],
                br: [0.65, -0.02],
                types: [TemplateFieldType.TEXT],
                sizes: [TemplateFieldSize.TINY],
                description: 'A tiny field for just a word or two of plain text.',
                opts: {
                    backgroundColor: 'transparent',
                    color: 'white',
                    contentXCentering: 'h-center',
                    fontTransform: 'uppercase',
                },
            },
            {
                tl: [-0.65, 0],
                br: [0.65, 0.18],
                types: [TemplateFieldType.TEXT],
                sizes: [TemplateFieldSize.TINY],
                description: 'A tiny field for just a word or two of plain text.',
                opts: {
                    backgroundColor: 'transparent',
                    color: 'white',
                    contentXCentering: 'h-center',
                    fontTransform: 'uppercase',
                },
            },
            {
                tl: [-0.83, 0.2],
                br: [0.83, 0.95],
                types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL],
                sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
                description: 'A medium to large-sized field suitable for an image or longer text that should be the main focus, or share focus with field 1.',
                opts: {
                    borderWidth: '8',
                    borderColor: '#F8E71C',
                    color: 'white',
                    backgroundColor: '#242425',
                },
            },
        ],
        decorations: [
            {
                tl: [-0.8, -0.075],
                br: [-0.525, 0.075],
                opts: {
                    backgroundColor: '#F8E71C',
                    rotation: 45,
                },
            },
            {
                tl: [-0.3075, -0.0245],
                br: [-0.2175, 0.0245],
                opts: {
                    backgroundColor: '#F8E71C',
                    rotation: 45,
                },
            },
            {
                tl: [-0.045, -0.0245],
                br: [0.045, 0.0245],
                opts: {
                    backgroundColor: '#F8E71C',
                    rotation: 45,
                },
            },
            {
                tl: [0.2175, -0.0245],
                br: [0.3075, 0.0245],
                opts: {
                    backgroundColor: '#F8E71C',
                    rotation: 45,
                },
            },
            {
                tl: [0.525, -0.075],
                br: [0.8, 0.075],
                opts: {
                    backgroundColor: '#F8E71C',
                    rotation: 45,
                },
            },
        ],
    };

    // public static FourField003: TemplateDocInfos = {
    //     title: 'fourfield3',
    //     width: 477,
    //     height: 662,
    //     opts: {
    //         backgroundColor: '#9E9C95'
    //     },
    //     fields: [{
    //         tl: [-.875, -.9],
    //         br: [.875, .7],
    //         types: [TemplateFieldType.VISUAL],
    //         sizes: [TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
    //         description: '',
    //         opts: {
    //             borderWidth: '15',
    //             borderColor: '#E0E0DA',
    //         }
    //     }, {
    //         tl: [-.95, .8],
    //         br: [-.1, .95],
    //         types: [TemplateFieldType.TEXT],
    //         sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL],
    //         description: '',
    //         opts: {
    //             backgroundColor: 'transparent',
    //             color: 'white',
    //             contentXCentering: 'h-right',
    //         }
    //     }, {
    //         tl: [.1, .8],
    //         br: [.95, .95],
    //         types: [TemplateFieldType.TEXT],
    //         sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL],
    //         description: '',
    //         opts: {
    //             backgroundColor: 'transparent',
    //             color: 'red',
    //             fontTransform: 'uppercase',
    //             contentXCentering: 'h-left'
    //         }
    //     }, {
    //         tl: [0, -.9],
    //         br: [.85, -.66],
    //         types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL],
    //         sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
    //         description: '',
    //         opts: {
    //             backgroundColor: 'transparent',
    //             contentXCentering: 'h-right'
    //         }
    //     }],
    //     decorations: [{
    //         tl: [-.025, .8],
    //         br: [.025, .95],
    //         opts: {
    //             backgroundColor: '#E0E0DA',
    //         }
    //     }]
    // };

    public static FourField004: TemplateDocInfos = {
        title: 'fourfield4',
        width: 414,
        height: 583,
        opts: {
            backgroundColor: '#6CCAF0',
            borderColor: '#1088C3',
            borderWidth: '10',
        },
        fields: [
            {
                tl: [-0.86, -0.92],
                br: [-0.075, -0.77],
                types: [TemplateFieldType.TEXT],
                sizes: [TemplateFieldSize.TINY],
                description: 'A tiny field for just a word or two of plain text.',
                opts: {
                    backgroundColor: '#E2B4F5',
                    borderWidth: '9',
                    borderColor: '#9222F1',
                    contentXCentering: 'h-center',
                },
            },
            {
                tl: [0.075, -0.92],
                br: [0.86, -0.77],
                types: [TemplateFieldType.TEXT],
                sizes: [TemplateFieldSize.TINY],
                description: 'A tiny field for just a word or two of plain text.',
                opts: {
                    backgroundColor: '#F5B4DD',
                    borderWidth: '9',
                    borderColor: '#E260F3',
                    contentXCentering: 'h-center',
                },
            },
            {
                tl: [-0.81, -0.64],
                br: [0.81, 0.48],
                types: [TemplateFieldType.VISUAL],
                sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
                description: 'A large to huge field for visual content that is the main content of the template.',
                opts: {
                    borderWidth: '16',
                    borderColor: '#A2BD77',
                },
            },
            {
                tl: [-0.86, 0.6],
                br: [0.86, 0.92],
                types: [TemplateFieldType.TEXT],
                sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE],
                description: 'A medium to large field for text that describes the visual content above',
                opts: {
                    borderWidth: '9',
                    borderColor: '#F0D601',
                    backgroundColor: '#F3F57D',
                },
            },
        ],
        decorations: [
            {
                tl: [-0.852, -0.67],
                br: [0.852, 0.51],
                opts: {
                    backgroundColor: 'transparent',
                    borderColor: '#007C0C',
                    borderWidth: '10',
                },
            },
        ],
    };

    public static ThreeField001: TemplateDocInfos = {
        title: 'threefield1',
        width: 575,
        height: 770,
        opts: {
            backgroundColor: '#DDD3A9',
        },
        fields: [
            {
                tl: [-0.66, -0.747],
                br: [0.66, 0.247],
                types: [TemplateFieldType.VISUAL],
                sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
                description: 'A medium to large field for visual content that is the central focus.',
                opts: {
                    borderColor: 'yellow',
                    borderWidth: '8',
                    rotation: 45,
                },
            },
            {
                tl: [-0.7, 0.2],
                br: [0.7, 0.46],
                types: [TemplateFieldType.TEXT],
                sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL],
                description: 'A very small text field for one to a few words. A good caption for the image.',
                opts: {
                    backgroundColor: 'transparent',
                    contentXCentering: 'h-center',
                },
            },
            {
                tl: [-0.95, 0.5],
                br: [0.95, 0.95],
                types: [TemplateFieldType.TEXT],
                sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE],
                description: 'A medium to large text field for a thorough description of the image. ',
                opts: {
                    backgroundColor: 'transparent',
                    color: 'white',
                },
            },
        ],
        decorations: [
            {
                tl: [0.2, -1.32],
                br: [1.8, -0.66],
                opts: {
                    backgroundColor: '#CEB155',
                    rotation: 45,
                },
            },
            {
                tl: [-1.8, -1.32],
                br: [-0.2, -0.66],
                opts: {
                    backgroundColor: '#CEB155',
                    rotation: 135,
                },
            },
            {
                tl: [0.33, 0.75],
                br: [1.66, 1.25],
                opts: {
                    backgroundColor: '#CEB155',
                    rotation: 135,
                },
            },
            {
                tl: [-1.66, 0.75],
                br: [-0.33, 1.25],
                opts: {
                    backgroundColor: '#CEB155',
                    rotation: 45,
                },
            },
        ],
    };

    public static ThreeField002: TemplateDocInfos = {
        title: 'threefield2',
        width: 477,
        height: 662,
        opts: {
            backgroundColor: '#9E9C95',
        },
        fields: [
            {
                tl: [-0.875, -0.9],
                br: [0.875, 0.7],
                types: [TemplateFieldType.VISUAL],
                sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
                description: 'A medium to large visual field for the main content of the template',
                opts: {
                    borderWidth: '15',
                    borderColor: '#E0E0DA',
                },
            },
            {
                tl: [0.1, 0.775],
                br: [0.95, 0.975],
                types: [TemplateFieldType.TEXT],
                sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL],
                description: 'A very small text field for one to a few words. The content should represent a general categorization of the image.',
                opts: {
                    backgroundColor: 'transparent',
                    color: '#AF0D0D',
                    fontTransform: 'uppercase',
                    fontBold: true,
                    contentXCentering: 'h-left',
                },
            },
            {
                tl: [-0.95, 0.775],
                br: [-0.1, 0.975],
                types: [TemplateFieldType.TEXT],
                sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL],
                description: 'A very small text field for one to a few words. The content should contextualize field 2.',
                opts: {
                    backgroundColor: 'transparent',
                    color: 'black',
                    contentXCentering: 'h-right',
                },
            },
        ],
        decorations: [
            {
                tl: [-0.025, 0.8],
                br: [0.025, 0.95],
                opts: {
                    backgroundColor: '#E0E0DA',
                },
            },
        ],
    };

    //     public static FourField002: TemplateDocInfos = {
    //         width: 450,
    //         height: 600,
    //         fields: [{
    //             tl: [-.6, -.9],
    //             br: [.6, -.8],
    //             types: [FieldType.TEXT],
    //             sizes: [FieldSize.TINY]
    //         }, {
    //             tl: [-.9, -.7],
    //             br: [.9, .2],
    //             types: [FieldType.TEXT, FieldType.VISUAL],
    //             sizes: [FieldSize.MEDIUM, FieldSize.LARGE, FieldSize.HUGE]
    //         }, {
    //             tl: [-.9, .3],
    //             br: [-.05, .9],
    //             types: [FieldType.TEXT],
    //             sizes: [FieldSize.TINY]
    //         }, {
    //             tl: [.05, .3],
    //             br: [.9, .9],
    //             types: [FieldType.TEXT, FieldType.VISUAL],
    //             sizes: [FieldSize.MEDIUM, FieldSize.LARGE, FieldSize.HUGE]
    //         }]
    //     };

    //     public static TwoFieldPlusCarousel: TemplateDocInfos = {
    //         width: 500,
    //         height: 600,
    //         fields: [{
    //             tl: [-.9, -.99],
    //             br: [.9, -.7],
    //             types: [FieldType.TEXT],
    //             sizes: [FieldSize.TINY]
    //         }, {
    //             tl: [-.9, -.65],
    //             br: [.9, .35],
    //             types: [],
    //             sizes: []
    //         }, {
    //             tl: [-.9, .4],
    //             br: [.9, .95],
    //             types: [FieldType.TEXT],
    //             sizes: [FieldSize.TINY]
    //         }]
    //     };
}

// export class ContentField extends Field {
    
// }
