/* eslint-disable react/jsx-props-no-spreading */
import { IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { returnEmptyString } from '../../../ClientUtils';
import { Doc, DocListCast } from '../../../fields/Doc';
import { ObjectField } from '../../../fields/ObjectField';
import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
import { DocUtils } from '../../documents/DocUtils';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
import { ImageUtils } from '../../util/Import & Export/ImageUtils';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent } from '../DocComponent';
import { FieldView } from '../nodes/FieldView';
import { OpenWhere } from '../nodes/OpenWhere';
import { CollectionCalendarView } from './CollectionCalendarView';
import { CollectionCarousel3DView } from './CollectionCarousel3DView';
import { CollectionCarouselView } from './CollectionCarouselView';
import { CollectionDockingView } from './CollectionDockingView';
import { CollectionNoteTakingView } from './CollectionNoteTakingView';
import { CollectionPileView } from './CollectionPileView';
import { CollectionStackingView } from './CollectionStackingView';
import { CollectionViewProps, SubCollectionViewProps } from './CollectionSubView';
import { CollectionTimeView } from './CollectionTimeView';
import { CollectionTreeView } from './CollectionTreeView';
import './CollectionView.scss';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import { CollectionGridView } from './collectionGrid/CollectionGridView';
import { CollectionLinearView } from './collectionLinear';
import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
import { CollectionSchemaView } from './collectionSchema/CollectionSchemaView';
import { CollectionCardView } from './CollectionCardDeckView';

@observer
export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewProps>() {
    public static LayoutString(fieldStr: string) {
        return FieldView.LayoutString(CollectionView, fieldStr);
    }

    @observable private static _safeMode = false;
    public static SetSafeMode(safeMode: boolean) {
        this._safeMode = safeMode;
    }
    private reactionDisposer: IReactionDisposer | undefined;
    @observable _isContentActive: boolean | undefined = undefined;

    constructor(props: any) {
        super(props);
        makeObservable(this);
        this._annotationKeySuffix = returnEmptyString;
    }

    componentDidMount() {
        // we use a reaction/observable instead of a computed value to reduce invalidations.
        // There are many variables that aggregate into this boolean output - a change in any of them
        // will cause downstream invalidations even if the computed value doesn't change.  By making
        // this a reaction, downstream invalidations only occur when the reaction value actually changes.
        this.reactionDisposer = reaction(
            () => (this.isAnyChildContentActive() ? true : this._props.isContentActive()),
            active => {
                this._isContentActive = active;
            },
            { fireImmediately: true }
        );
    }
    componentWillUnmount() {
        this.reactionDisposer?.();
    }

    get collectionViewType(): CollectionViewType | undefined {
        const viewField = StrCast(this.layoutDoc._type_collection) as any as CollectionViewType;
        if (CollectionView._safeMode) {
            switch (viewField) { 
                case CollectionViewType.Freeform:
                case CollectionViewType.Schema:  return CollectionViewType.Tree;
                case CollectionViewType.Invalid:  return CollectionViewType.Freeform;
                default:
            } // prettier-ignore
        }
        return viewField;
    }

    screenToLocalTransform = () => (this._props.renderDepth ? this.ScreenToLocalBoxXf() : this.ScreenToLocalBoxXf().scale(this._props.PanelWidth() / this.bodyPanelWidth()));
    // prettier-ignore
    private renderSubView = (type: CollectionViewType | undefined, props: SubCollectionViewProps) => {
        TraceMobx();
        if (type === undefined) return null;
        switch (type) {
            case CollectionViewType.Schema:       return <CollectionSchemaView      key="collview" {...props} />;
            case CollectionViewType.Calendar:     return <CollectionCalendarView    key="collview" {...props} />;
            case CollectionViewType.Docking:      return <CollectionDockingView     key="collview" {...props} />;
            case CollectionViewType.Tree:         return <CollectionTreeView        key="collview" {...props} />;
            case CollectionViewType.Multicolumn:  return <CollectionMulticolumnView key="collview" {...props} />;
            case CollectionViewType.Multirow:     return <CollectionMultirowView    key="collview" {...props} />;
            case CollectionViewType.Linear:       return <CollectionLinearView      key="collview" {...props} />;
            case CollectionViewType.Pile:         return <CollectionPileView        key="collview" {...props} />;
            case CollectionViewType.Carousel:     return <CollectionCarouselView    key="collview" {...props} />;
            case CollectionViewType.Carousel3D:   return <CollectionCarousel3DView  key="collview" {...props} />;
            case CollectionViewType.Stacking:     return <CollectionStackingView    key="collview" {...props} />;
            case CollectionViewType.NoteTaking:   return <CollectionNoteTakingView  key="collview" {...props} />;
            case CollectionViewType.Masonry:      return <CollectionStackingView    key="collview" {...props} />;
            case CollectionViewType.Time:         return <CollectionTimeView        key="collview" {...props} />;
            case CollectionViewType.Grid:         return <CollectionGridView        key="collview" {...props} />;
            case CollectionViewType.Card:         return <CollectionCardView        key="collview" {...props} />;
            case CollectionViewType.Freeform:     
            default:                              return <CollectionFreeFormView    key="collview" {...props} />;
        }
    };

    setupViewTypes(category: string, func: (type_collection: CollectionViewType) => Doc) {
        if (!Doc.IsSystem(this.Document) && this.Document._type_collection !== CollectionViewType.Docking && !this.dataDoc.isGroup && !this.Document.annotationOn) {
            // prettier-ignore
            const subItems: ContextMenuProps[] = [
                { description: 'Freeform',    event: () => func(CollectionViewType.Freeform), icon: 'signature' },
                { description: 'Schema',      event: () => func(CollectionViewType.Schema), icon: 'th-list' },
                { description: 'Tree',        event: () => func(CollectionViewType.Tree), icon: 'tree' },
                { description: 'Stacking',    event: () => {func(CollectionViewType.Stacking)._layout_autoHeight = true}, icon: 'ellipsis-v' },
                { description: 'Calendar',    event: () => func(CollectionViewType.Calendar), icon: 'columns'},
                { description: 'Notetaking',  event: () => {func(CollectionViewType.NoteTaking)._layout_autoHeight = true}, icon: 'ellipsis-v' },
                { description: 'Multicolumn', event: () => func(CollectionViewType.Multicolumn), icon: 'columns' },
                { description: 'Multirow',    event: () => func(CollectionViewType.Multirow), icon: 'columns' },
                { description: 'Masonry',     event: () => func(CollectionViewType.Masonry), icon: 'columns' },
                { description: 'Carousel',    event: () => func(CollectionViewType.Carousel), icon: 'columns' },
                { description: '3D Carousel', event: () => func(CollectionViewType.Carousel3D), icon: 'columns' },
                { description: 'Pivot/Time',  event: () => func(CollectionViewType.Time), icon: 'columns' },
                { description: 'Map',         event: () => func(CollectionViewType.Map), icon: 'globe-americas' },
                { description: 'Grid',        event: () => func(CollectionViewType.Grid), icon: 'th-list' },
            ];

            const existingVm = ContextMenu.Instance.findByDescription(category);
            const catItems = existingVm && 'subitems' in existingVm ? existingVm.subitems : [];
            catItems.push({ description: 'Add a Perspective...', addDivider: true, noexpand: true, subitems: subItems, icon: 'eye' });
            !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: catItems, icon: 'eye' });
        }
    }

    onContextMenu = (e: React.MouseEvent): void => {
        const cm = ContextMenu.Instance;
        if (cm && !e.isPropagationStopped()) {
            // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
            !Doc.noviceMode &&
                this.setupViewTypes('Appearance...', vtype => {
                    const newRendition = Doc.MakeEmbedding(this.Document);
                    newRendition._type_collection = vtype;
                    this._props.addDocTab(newRendition, OpenWhere.addRight);
                    return newRendition;
                });

            const options = cm.findByDescription('Options...');
            const optionItems = options && 'subitems' in options ? options.subitems : [];
            !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.Document.forceActive ? 'Select' : 'Force'} Contents Active`, event: () => {this.Document.forceActive = !this.Document.forceActive}, icon: 'project-diagram' }) : null; // prettier-ignore
            if (this.Document.childLayout instanceof Doc) {
                optionItems.push({ description: 'View Child Layout', event: () => this._props.addDocTab(this.Document.childLayout as Doc, OpenWhere.addRight), icon: 'project-diagram' });
            }
            if (this.Document.childClickedOpenTemplateView instanceof Doc) {
                optionItems.push({ description: 'View Child Detailed Layout', event: () => this._props.addDocTab(this.Document.childClickedOpenTemplateView as Doc, OpenWhere.addRight), icon: 'project-diagram' });
            }
            !Doc.noviceMode && optionItems.push({ description: `${this.layoutDoc._isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => { this.layoutDoc._isLightbox = !this.layoutDoc._isLightbox; }, icon: 'project-diagram' }); // prettier-ignore

            !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' });

            if (!Doc.noviceMode && !this.Document.annotationOn && !this._props.hideClickBehaviors) {
                const existingOnClick = cm.findByDescription('OnClick...');
                const onClicks = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
                const funcs = [
                    { key: 'onChildClick', name: 'On Child Clicked' },
                    { key: 'onChildDoubleClick', name: 'On Child Double Clicked' },
                ];
                funcs.map(func =>
                    onClicks.push({
                        description: `Edit ${func.name} script`,
                        icon: 'edit',
                        event: () => {
                            const embedding = Doc.MakeEmbedding(this.Document);
                            DocUtils.makeCustomViewClicked(embedding, undefined, func.key);
                            this._props.addDocTab(embedding, OpenWhere.addRight);
                        },
                    })
                );
                DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null)?.data).forEach(childClick =>
                    onClicks.push({
                        description: `Set child ${childClick.title}`,
                        icon: 'edit',
                        event: () => {
                            this.dataDoc[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data));
                        },
                    })
                );
                !Doc.IsSystem(this.Document) && !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
            }

            if (!Doc.noviceMode) {
                const more = cm.findByDescription('More...');
                const moreItems = more && 'subitems' in more ? more.subitems : [];
                moreItems.push({ description: 'Export Image Hierarchy', icon: 'columns', event: () => ImageUtils.ExportHierarchyToFileSystem(this.Document) });
                !more && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'hand-point-right' });
            }
        }
    };

    bodyPanelWidth = () => this._props.PanelWidth();

    childLayoutTemplate = () => this._props.childLayoutTemplate?.() || Cast(this.Document.childLayoutTemplate, Doc, null);
    isContentActive = () => this._isContentActive;

    pointerEvents = () =>
        this.layoutDoc._lockedPosition && //
        this.Document?._type_collection === CollectionViewType.Freeform;

    render() {
        TraceMobx();
        const pointerEvents = this.pointerEvents() ? 'none' : undefined;
        const props: SubCollectionViewProps = {
            ...this._props,
            addDocument: this.addDocument,
            moveDocument: this.moveDocument,
            removeDocument: this.removeDocument,
            isContentActive: this.isContentActive,
            isAnyChildContentActive: this.isAnyChildContentActive,
            PanelWidth: this.bodyPanelWidth,
            PanelHeight: this._props.PanelHeight,
            ScreenToLocalTransform: this.screenToLocalTransform,
            childLayoutTemplate: this.childLayoutTemplate,
            whenChildContentsActiveChanged: this.whenChildContentsActiveChanged,
            childLayoutString: StrCast(this.Document.childLayoutString, this._props.childLayoutString),
            childHideResizeHandles: this._props.childHideResizeHandles ?? BoolCast(this.Document.childHideResizeHandles),
            childHideDecorationTitle: this._props.childHideDecorationTitle ?? BoolCast(this.Document.childHideDecorationTitle),
        };
        return (
            <div className="collectionView" onContextMenu={this.onContextMenu} style={{ pointerEvents }}>
                {this.renderSubView(this.collectionViewType, props)}
            </div>
        );
    }
}

Docs.Prototypes.TemplateMap.set(DocumentType.COL, {
    layout: { view: CollectionView, dataField: 'data' },
    options: {
        acl: '',
        _layout_fitWidth: true,
        freeform: '',
        _freeform_panX: 0,
        _freeform_panY: 0,
        _freeform_scale: 1,
        _layout_nativeDimEditable: true,
        _layout_reflowHorizontal: true,
        _layout_reflowVertical: true,
        systemIcon: 'BsFillCollectionFill',
    },
});
