import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { returnAll, returnFalse, setupMoveUpEvents } from '../../../../ClientUtils';
import { Doc, NumListCast } from '../../../../fields/Doc';
import { DocCast, ImageCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { emptyFunction } from '../../../../Utils';
import { SnappingManager } from '../../../util/SnappingManager';
import { undoable } from '../../../util/UndoManager';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { DocumentView } from '../DocumentView';
import { DataVizBox } from './DataVizBox';
import './DocCreatorMenu.scss';
import { Id } from '../../../../fields/FieldSymbols';
import { Colors, IconButton, Size } from 'browndash-components';
import { MakeTemplate } from '../../../util/DropConverter';
import { threadId } from 'worker_threads';
import { ideahub } from 'googleapis/build/src/apis/ideahub';

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

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

    static Instance: DocCreatorMenu;

    private _ref: HTMLDivElement | null = null;

    @observable _templateDocs: Doc[] = [];
    @observable _templateRefToDoc?: ObservableMap<HTMLDivElement, Doc>;
    @observable _selectedTemplate: Doc | undefined = undefined;

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

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

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

    @observable _menuContent: 'templates' | 'options' | 'saved' = 'templates';
    @observable _dragging: boolean = false;
    @observable _draggingIndicator: boolean = false;
    @observable _dataViz?: DataVizBox;

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

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

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

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

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

    @action
    onPointerUp = (e: PointerEvent) => {
        if (e.button !== 2 && !e.ctrlKey) return;
        const curX = e.clientX;
        const curY = e.clientY;
        if (Math.abs(this._mouseX - curX) > 1 || Math.abs(this._mouseY - curY) > 1) {
            this._shouldDisplay = false;
        }
    };

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

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

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

    @action
    closeMenu = () => {
        const wasOpen = this._display;
        this._display = false;
        this._shouldDisplay = false;
        return wasOpen;
    };

    @action
    onPointerMove = (e: any) => {
        // if (this._draggingIndicator) {
        //     this._indicatorX = e.pageX - (this._startPos?.x ?? 0) + this._pageX;
        //     this._indicatorY = e.pageY - (this._startPos?.y ?? 0) + this._pageY;
        // } else 
        if (this._dragging){
            this._pageX = e.pageX - (this._startPos?.x ?? 0);
            this._pageY = e.pageY - (this._startPos?.y ?? 0);
        }
    }

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

    get templatesPreviewContents(){
        const renderedTemplates: Doc[] = [];
        return (
            <div className='docCreatorMenu-preview-container'>
                {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={{
                        background: this._selectedTemplate === info.doc ? Colors.MEDIUM_BLUE : '',
                        boxShadow: this._selectedTemplate === info.doc ? `0 0 15px rgba(68, 118, 247, .8)` : ''
                    }}
                    onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(info.doc)))}>
                        <img className='docCreatorMenu-preview-image' src={info.icon!.url.href.replace(".png", "_o.png")} />
                    </div>
                )})}
            </div>
        );
    }

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



    get layoutPreviewContents() {
        const docWidth = Number(this._selectedTemplate?._width);
        const docHeight = Number(this._selectedTemplate?._height);
        const horizontalSpan: number = (docWidth + this._layout.xMargin) * this.columnsCount - this._layout.xMargin;
        const verticalSpan: number = (docHeight + this._layout.yMargin) * this.rowsCount - this._layout.yMargin;
        const largerSpan: number = horizontalSpan > verticalSpan ? horizontalSpan : verticalSpan;
        const scaledDown = (input: number) => {return input / (largerSpan / 225 * this._layoutPreviewScale)}
        const fontSize = Math.min(scaledDown(docWidth / 3), scaledDown(docHeight / 3));

        return (
            <div className='docCreatorMenu-layout-preview-window-wrapper'>
                <button 
                    className='docCreatorMenu-zoom-button'
                    onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this._layoutPreviewScale *= 1.25))}>
                        <FontAwesomeIcon icon={'minus'}/>
                </button>
                <button 
                    className='docCreatorMenu-zoom-button zoom-in'
                    onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this._layoutPreviewScale *= .75))}>
                        <FontAwesomeIcon icon={'plus'}/>
                </button>
                <div 
                className='docCreatorMenu-layout-preview-window'
                style={{
                    gridTemplateColumns: `repeat(${this.columnsCount}, ${scaledDown(docWidth)}px`,
                    gridTemplateRows: `${scaledDown(docHeight)}px`,
                    gridAutoRows: `${scaledDown(docHeight)}px`,
                    rowGap: `${scaledDown(this._layout.yMargin)}px`,
                    columnGap: `${scaledDown(this._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 layoutOption = (option: LayoutType, optStyle?: {}, specialFunc?: Function) => {
            return (
                <div 
                    className="docCreatorMenu-dropdown-option" 
                    style={optStyle}
                    onPointerDown={e => this.setUpButtonClick(e, () => {specialFunc?.(); runInAction(() => this._layout.type = option)})}>
                    {option}
                </div>
            );
        }

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

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

        return (
            <div className='docCreatorMenu-menu-container'>
                <div className='docCreatorMenu-option-container layout'>
                    <div className='docCreatorMenu-dropdown-hoverable'>
                        <div className="docCreatorMenu-option-title">{this._layout.type ? this._layout.type.toUpperCase() : 'Choose Layout'}</div>
                        <div className="docCreatorMenu-dropdown-content">
                            {layoutOption(LayoutType.Stacked)}
                            {layoutOption(LayoutType.Grid, undefined, () => {if (!this._layout.columns) this._layout.columns = Math.ceil(Math.sqrt(this.docsToRender.length))})}
                            {layoutOption(LayoutType.Row)}
                            {layoutOption(LayoutType.Column)}
                            {layoutOption(LayoutType.Custom, {borderBottom: `0px`})}
                        </div>
                    </div>
                    <button 
                        className='docCreatorMenu-menu-button'
                        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 : null}
                {selectionBox(60, 20, 'repeat', undefined, repeatOptions.map(num => <option onPointerDown={e => this._layout.repeat = num}>{`${num}x`}</option>))}
                <button 
                    className='docCreatorMenu-create-docs-button'
                    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')
                        )
                    }>
                    Create
                </button>
            </div>
        );
    }

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

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


        return (
            <div className='docCreatorMenu'>
                {!this._shouldDisplay ? undefined : 
                <>
                {/* <div className='docCreatorMenu-placement-indicator' 
                style={{
                    display: '',
                    left: this._indicatorX ?? this._pageX + 300,
                    top: this._indicatorY ?? this._pageY,
                }}
                onPointerMove={e => this.onPointerMove(e)}
                onPointerDown={e =>
                    setupMoveUpEvents(
                        this,
                        e,
                        (e) => {
                            this._draggingIndicator = 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);
                            return true;
                        },
                        emptyFunction,
                        undoable(clickEv => {
                            clickEv.stopPropagation();
                        }, 'drag menu')
                    )
                }/> */}
                <div
                    className="docCreatorMenu-cont"
                    ref={r => this._ref = r}
                    style={{
                        display: '',
                        left: this._pageX,
                        top: this._pageY,
                        width: 300,
                        height: 400,
                        background: SnappingManager.userBackgroundColor,
                        color: SnappingManager.userColor,
                    }}>
                        <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);
                                    return true;
                                },
                                emptyFunction,
                                undoable(clickEv => {
                                    clickEv.stopPropagation();
                                }, 'drag menu')
                            )
                        }
                        onPointerMove={e => this.onPointerMove(e)}
                        onPointerUp={() => this._dragging = false}
                        >
                            <div className='docCreatorMenu-top-buttons-container'>
                                {topButton('table-cells', onPreviewSelected, 'left')}
                                {topButton('bars', onOptionsSelected, 'middle')}
                                {topButton('floppy-disk', onSavedSelected, 'right')}
                            </div>
                            <button 
                                className='docCreatorMenu-menu-button close-menu'
                                onPointerDown={e => this.setUpButtonClick(e, this.closeMenu)}>
                                <FontAwesomeIcon icon={'minus'}/>
                            </button>
                        </div>
                        {this._menuContent === 'templates' ? this.templatesPreviewContents : this.optionsMenuContents}
                </div>
                </>
                }
            </div>
            )
        }
}

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