import { computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast } from '../../../fields/Doc';
import { ObjectField } from '../../../fields/ObjectField';
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
import { returnEmptyString } from '../../../Utils';
import { DocUtils } from '../../documents/Documents';
import { CollectionViewType } from '../../documents/DocumentTypes';
import { ImageUtils } from '../../util/Import & Export/ImageUtils';
import { InteractionUtils } from '../../util/InteractionUtils';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
import { OpenWhere } from '../nodes/DocumentView';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import { CollectionCarousel3DView } from './CollectionCarousel3DView';
import { CollectionCarouselView } from './CollectionCarouselView';
import { CollectionDockingView } from './CollectionDockingView';
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 { CollectionNoteTakingView } from './CollectionNoteTakingView';
import { CollectionPileView } from './CollectionPileView';
import { CollectionSchemaView } from './collectionSchema/CollectionSchemaView';
import { CollectionStackingView } from './CollectionStackingView';
import { SubCollectionViewProps } from './CollectionSubView';
import { CollectionTimeView } from './CollectionTimeView';
import { CollectionTreeView } from './CollectionTreeView';
import './CollectionView.scss';
export const COLLECTION_BORDER_WIDTH = 2;
const path = require('path');

interface CollectionViewProps_ extends FieldViewProps {
    isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
    isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
    layoutEngine?: () => string;
    setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
    setBrushViewer?: (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => void;
    ignoreUnrendered?: boolean;

    // property overrides for child documents
    childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
    childDocumentsActive?: () => boolean | undefined; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
    childFitWidth?: (child: Doc) => boolean;
    childShowTitle?: () => string;
    childOpacity?: () => number;
    childContextMenuItems?: () => { script: ScriptField; label: string }[];
    childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
    childHideDecorationTitle?: () => boolean;
    childHideResizeHandles?: () => boolean;
    childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection
    childCanEmbedOnDrag?: boolean;
    childXPadding?: number;
    childYPadding?: number;
    childLayoutString?: string;
    childIgnoreNativeSize?: boolean;
    childClickScript?: ScriptField;
    childDoubleClickScript?: ScriptField;
    //TODO: [AL] add these fields
    AddToMap?: (treeViewDoc: Doc, index: number[]) => void;
    RemFromMap?: (treeViewDoc: Doc, index: number[]) => void;
    hierarchyIndex?: number[]; // hierarchical index of a document up to the rendering root (primarily used for tree views)
}
export interface CollectionViewProps extends React.PropsWithChildren<CollectionViewProps_> {}
@observer
export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps>() {
    public static LayoutString(fieldStr: string) {
        return FieldView.LayoutString(CollectionView, fieldStr);
    }

    @observable private static _safeMode = false;
    public static SetSafeMode(safeMode: boolean) {
        this._safeMode = safeMode;
    }

    protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;

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

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

    screenToLocalTransform = () => (this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth()));
    // prettier-ignore
    private renderSubView = (type: CollectionViewType | undefined, props: SubCollectionViewProps) => {
        TraceMobx();
        if (type === undefined) return null;
        switch (type) {
            default:
            case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />;
            case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
            case CollectionViewType.Schema: return <CollectionSchemaView 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.Staff: return <CollectionStaffView key="collview" {...props} />;
        }
    };

    setupViewTypes(category: string, func: (viewType: CollectionViewType) => Doc) {
        if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && !this.rootDoc.isGroup && !this.rootDoc.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)._autoHeight = true), icon: 'ellipsis-v' },
                { description: 'Notetaking',  event: () => (func(CollectionViewType.NoteTaking)._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('UI Controls...', vtype => {
                    const newRendition = Doc.MakeAlias(this.rootDoc);
                    newRendition._viewType = 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.rootDoc.forceActive ? 'Select' : 'Force'} Contents Active`, event: () => (this.rootDoc.forceActive = !this.rootDoc.forceActive), icon: 'project-diagram' }) : null;
            if (this.rootDoc.childLayout instanceof Doc) {
                optionItems.push({ description: 'View Child Layout', event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, OpenWhere.addRight), icon: 'project-diagram' });
            }
            if (this.rootDoc.childClickedOpenTemplateView instanceof Doc) {
                optionItems.push({ description: 'View Child Detailed Layout', event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, OpenWhere.addRight), icon: 'project-diagram' });
            }
            !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc._isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => (this.rootDoc._isLightbox = !this.rootDoc._isLightbox), icon: 'project-diagram' });

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

            if (!Doc.noviceMode && !this.rootDoc.annotationOn) {
                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: (obj: any) => {
                            const alias = Doc.MakeAlias(this.rootDoc);
                            DocUtils.makeCustomViewClicked(alias, undefined, func.key);
                            this.props.addDocTab(alias, OpenWhere.addRight);
                        },
                    })
                );
                DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null)?.data).forEach(childClick =>
                    onClicks.push({
                        description: `Set child ${childClick.title}`,
                        icon: 'edit',
                        event: () => (Doc.GetProto(this.rootDoc)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data))),
                    })
                );
                !Doc.IsSystem(this.rootDoc) && !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.rootDoc) });
                !more && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'hand-point-right' });
            }
        }
    };

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

    childHideResizeHandles = () => this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles);
    childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle);
    childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null);
    isContentActive = (outsideReaction?: boolean) => this.props.isContentActive() || this.isAnyChildContentActive();

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