import { faLaptopHouse } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Slider, Switch } from '@mui/material';
import { Button, IconButton } from 'browndash-components';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { AiOutlineSend } from 'react-icons/ai';
import ReactLoading from 'react-loading';
import { returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero } from '../../../ClientUtils';
import { ActiveInkWidth, Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { InkData, InkField } from '../../../fields/InkField';
import { BoolCast, DocCast, ImageCast } from '../../../fields/Types';
import { emptyFunction, unimplementedFunction } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { makeUserTemplateButton, makeUserTemplateImage } from '../../util/DropConverter';
import { SettingsManager } from '../../util/SettingsManager';
import { Transform } from '../../util/Transform';
import { undoable, undoBatch } from '../../util/UndoManager';
import { CollectionFreeFormView, MarqueeOptionsMenu, MarqueeView } from '../collections/collectionFreeForm';
import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveIsInkMask, DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
import { FieldView } from '../nodes/FieldView';
import { ObservableReactComponent } from '../ObservableReactComponent';
import { DefaultStyleProvider } from '../StyleProvider';
import './AnnotationPalette.scss';
import { DrawingOptions, SmartDrawHandler } from './SmartDrawHandler';
import { DocumentType } from '../../documents/DocumentTypes';
import { ImageField } from '../../../fields/URLField';
import { CollectionCarousel3DView } from '../collections/CollectionCarousel3DView';
import { Copy } from '../../../fields/FieldSymbols';

interface AnnotationPaletteProps {
    Document: Doc;
}

@observer
export class AnnotationPalette extends ObservableReactComponent<AnnotationPaletteProps> {
    @observable private _paletteMode: 'create' | 'view' = 'view';
    @observable private _userInput: string = '';
    @observable private _isLoading: boolean = false;
    @observable private _canInteract: boolean = true;
    @observable private _showRegenerate: boolean = false;
    @observable private _docView: DocumentView | null = null;
    @observable private _docCarouselView: DocumentView | null = null;
    @observable private _opts: DrawingOptions = { text: '', complexity: 5, size: 200, autoColor: true, x: 0, y: 0 };
    private _gptRes: string[] = [];

    constructor(props: any) {
        super(props);
        makeObservable(this);
    }

    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(AnnotationPalette, fieldKey);
    }

    Contains = (view: DocumentView) => {
        return (this._docView && (view.containerViewPath?.() ?? []).concat(view).includes(this._docView)) || (this._docCarouselView && (view.containerViewPath?.() ?? []).concat(view).includes(this._docCarouselView));
    };

    return170 = () => 170;

    @action
    handleKeyPress = async (event: React.KeyboardEvent) => {
        if (event.key === 'Enter') {
            await this.generateDrawings();
        }
    };

    @action
    setPaletteMode = (mode: 'create' | 'view') => {
        this._paletteMode = mode;
    };

    @action
    setUserInput = (input: string) => {
        if (!this._isLoading) this._userInput = input;
    };

    @action
    setDetail = (detail: number) => {
        if (this._canInteract) this._opts.complexity = detail;
    };

    @action
    setColor = (autoColor: boolean) => {
        if (this._canInteract) this._opts.autoColor = autoColor;
    };

    @action
    setSize = (size: number) => {
        if (this._canInteract) this._opts.size = size;
    };

    @action
    resetPalette = (changePaletteMode: boolean) => {
        if (changePaletteMode) this.setPaletteMode('view');
        this.setUserInput('');
        this.setDetail(5);
        this.setColor(true);
        this.setSize(200);
        this._showRegenerate = false;
        this._canInteract = true;
        this._opts = { text: '', complexity: 5, size: 200, autoColor: true, x: 0, y: 0 };
        this._gptRes = [];
        this._props.Document[DocData].data = undefined;
    };

    /**
     * Adds a doc to the annotation palette. Gets a snapshot of the document to use as a preview in the palette. When this
     * preview is dragged onto a parent document, a copy of that document is added as an annotation.
     */
    public static addToPalette = async (doc: Doc) => {
        if (!doc.savedAsAnno) {
            const clone = await Doc.MakeClone(doc);
            clone.clone.title = doc.title;
            const image = (await AnnotationPalette.getIcon(doc))?.[Copy]();
            if (image) {
                const imageTemplate = makeUserTemplateImage(clone.clone, image);
                Doc.AddDocToList(Doc.MyAnnos, 'data', imageTemplate);
                doc.savedAsAnno = true;
            }
        }
    };

    public static getIcon(group: Doc) {
        const docView = DocumentView.getDocumentView(group);
        if (docView) {
            docView.ComponentView?.updateIcon?.(true);
            return new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000));
        }
        return undefined;
    }

    /**
     * Calls the draw with GPT functions in SmartDrawHandler to allow users to generate drawings straight from
     * the annotation palette.
     */
    @undoBatch
    generateDrawings = action(async () => {
        this._isLoading = true;
        this._props.Document[DocData].data = undefined;
        for (var i = 0; i < 3; i++) {
            try {
                SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
                this._canInteract = false;
                if (this._showRegenerate) {
                    await SmartDrawHandler.Instance.regenerate(this._opts, this._gptRes[i], this._userInput);
                } else {
                    await SmartDrawHandler.Instance.drawWithGPT({ X: 0, Y: 0 }, this._userInput, this._opts.complexity, this._opts.size, this._opts.autoColor);
                }
            } catch (e) {
                console.log('Error generating drawing');
            }
        }
        this._opts.text !== '' ? (this._opts.text = `${this._opts.text} ~~~ ${this._userInput}`) : (this._opts.text = this._userInput);
        this._userInput = '';
        this._isLoading = false;
        this._showRegenerate = true;
    });

    @action
    addDrawing = (drawing: Doc, opts: DrawingOptions, gptRes: string) => {
        this._gptRes.push(gptRes);
        drawing[DocData].freeform_fitContentsToBox = true;
        Doc.AddDocToList(this._props.Document, 'data', drawing);
    };

    /**
     * Saves the currently showing, newly generated drawing to the annotation palette and sets the metadata.
     * AddToPalette() is generically used to add any document to the palette, while this defines the behavior for when a user
     * presses the "save drawing" button.
     */
    saveDrawing = async () => {
        const cIndex: number = this._props.Document.carousel_index as number;
        const focusedDrawing = DocListCast(this._props.Document.data)[cIndex];
        const docData = focusedDrawing[DocData];
        docData.title = this._opts.text.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text;
        docData.drawingInput = this._opts.text;
        docData.drawingComplexity = this._opts.complexity;
        docData.drawingColored = this._opts.autoColor;
        docData.drawingSize = this._opts.size;
        docData.drawingData = this._gptRes[cIndex];
        docData.width = this._opts.size;
        await AnnotationPalette.addToPalette(focusedDrawing);
        this.resetPalette(true);
    };

    async getIcon(group: Doc) {
        const docView = DocumentView.getDocumentView(group);
        if (docView) {
            docView.ComponentView?.updateIcon?.(true);
            return new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000));
        }
        return undefined;
    }

    render() {
        return (
            <div className="annotation-palette" style={{ zIndex: 1000 }} onClick={e => e.stopPropagation()}>
                {this._paletteMode === 'view' && (
                    <>
                        <DocumentView
                            ref={r => (this._docView = r)}
                            Document={Doc.MyAnnos}
                            addDocument={undefined}
                            addDocTab={DocumentViewInternal.addDocTabFunc}
                            pinToPres={DocumentView.PinDoc}
                            containerViewPath={returnEmptyDoclist}
                            styleProvider={DefaultStyleProvider}
                            removeDocument={returnFalse}
                            ScreenToLocalTransform={Transform.Identity}
                            PanelWidth={this.return170}
                            PanelHeight={this.return170}
                            renderDepth={0}
                            isContentActive={returnTrue}
                            focus={emptyFunction}
                            whenChildContentsActiveChanged={emptyFunction}
                            childFilters={returnEmptyFilter}
                            childFiltersByRanges={returnEmptyFilter}
                            searchFilterDocs={returnEmptyDoclist}
                        />
                        <Button text="Add" icon={<FontAwesomeIcon icon="square-plus" />} color={SettingsManager.userColor} onClick={() => this.setPaletteMode('create')} />
                    </>
                )}
                {this._paletteMode === 'create' && (
                    <>
                        <div style={{ display: 'flex', flexDirection: 'row', width: '170px' }}>
                            <input
                                aria-label="label-input"
                                id="new-label"
                                type="text"
                                style={{ color: 'black', width: '170px' }}
                                value={this._userInput}
                                onChange={e => {
                                    this.setUserInput(e.target.value);
                                }}
                                placeholder={this._showRegenerate ? '(Optional) Enter edits' : 'Enter item to draw'}
                                onKeyDown={this.handleKeyPress}
                            />
                            <Button
                                style={{ alignSelf: 'flex-end' }}
                                tooltip={this._showRegenerate ? 'Regenerate' : 'Send'}
                                icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : this._showRegenerate ? <FontAwesomeIcon icon={'rotate'} /> : <AiOutlineSend />}
                                iconPlacement="right"
                                color={SettingsManager.userColor}
                                onClick={this.generateDrawings}
                            />
                        </div>
                        <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', width: '170px', marginTop: '5px' }}>
                            <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '40px' }}>
                                Color
                                <Switch
                                    sx={{
                                        '& .MuiSwitch-switchBase.Mui-checked': {
                                            color: SettingsManager.userColor,
                                        },
                                        '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': {
                                            backgroundColor: SettingsManager.userVariantColor,
                                        },
                                    }}
                                    defaultChecked={true}
                                    value={this._opts.autoColor}
                                    size="small"
                                    onChange={() => this.setColor(!this._opts.autoColor)}
                                />
                            </div>
                            <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '60px' }}>
                                Detail
                                <Slider
                                    sx={{
                                        '& .MuiSlider-thumb': {
                                            color: SettingsManager.userColor,
                                            '&.Mui-focusVisible, &:hover, &.Mui-active': {
                                                boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10`,
                                            },
                                        },
                                        '& .MuiSlider-track': {
                                            color: SettingsManager.userVariantColor,
                                        },
                                        '& .MuiSlider-rail': {
                                            color: SettingsManager.userColor,
                                        },
                                    }}
                                    style={{ width: '80%' }}
                                    min={1}
                                    max={10}
                                    step={1}
                                    size="small"
                                    value={this._opts.complexity}
                                    onChange={(e, val) => {
                                        this.setDetail(val as number);
                                    }}
                                    valueLabelDisplay="auto"
                                />
                            </div>
                            <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '60px' }}>
                                Size
                                <Slider
                                    sx={{
                                        '& .MuiSlider-thumb': {
                                            color: SettingsManager.userColor,
                                            '&.Mui-focusVisible, &:hover, &.Mui-active': {
                                                boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}20`,
                                            },
                                        },
                                        '& .MuiSlider-track': {
                                            color: SettingsManager.userVariantColor,
                                        },
                                        '& .MuiSlider-rail': {
                                            color: SettingsManager.userColor,
                                        },
                                    }}
                                    style={{ width: '80%' }}
                                    min={50}
                                    max={500}
                                    step={10}
                                    size="small"
                                    value={this._opts.size}
                                    onChange={(e, val) => {
                                        this.setSize(val as number);
                                    }}
                                    valueLabelDisplay="auto"
                                />
                            </div>
                        </div>
                        <DocumentView
                            ref={r => (this._docCarouselView = r)}
                            Document={this._props.Document}
                            addDocument={undefined}
                            addDocTab={DocumentViewInternal.addDocTabFunc}
                            pinToPres={DocumentView.PinDoc}
                            containerViewPath={returnEmptyDoclist}
                            styleProvider={DefaultStyleProvider}
                            removeDocument={returnFalse}
                            ScreenToLocalTransform={Transform.Identity}
                            PanelWidth={this.return170}
                            PanelHeight={this.return170}
                            renderDepth={1}
                            isContentActive={returnTrue}
                            focus={emptyFunction}
                            whenChildContentsActiveChanged={emptyFunction}
                            childFilters={returnEmptyFilter}
                            childFiltersByRanges={returnEmptyFilter}
                            searchFilterDocs={returnEmptyDoclist}
                        />
                        <div style={{ width: '100%', display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
                            <Button text="Back" tooltip="Back to All Annotations" icon={<FontAwesomeIcon icon="reply" />} color={SettingsManager.userColor} onClick={() => this.resetPalette(true)} />
                            <div style={{ display: 'flex', flexDirection: 'row' }}>
                                <Button tooltip="Save" icon={<FontAwesomeIcon icon="file-arrow-down" />} color={SettingsManager.userColor} onClick={this.saveDrawing} />
                                <Button tooltip="Reset" icon={<FontAwesomeIcon icon="rotate-left" />} color={SettingsManager.userColor} onClick={() => this.resetPalette(false)} />
                            </div>
                        </div>
                    </>
                )}
            </div>
        );
    }
}

Docs.Prototypes.TemplateMap.set(DocumentType.ANNOPALETTE, {
    layout: { view: AnnotationPalette, dataField: 'data' },
    options: { acl: '' },
});
