import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { extname } from 'path';
import { DataSym, Doc, DocListCast, Opt, WidthSym } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
import { createSchema } from '../../../fields/Schema';
import { ComputedField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { DashColor, emptyFunction, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { Networking } from '../../Network';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../../views/ContextMenu';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
import { MarqueeAnnotator } from '../MarqueeAnnotator';
import { AnchorMenu } from '../pdf/AnchorMenu';
import { StyleProp } from '../StyleProvider';
import { DocFocusOptions, OpenWhere } from './DocumentView';
import { FaceRectangles } from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import './ImageBox.scss';
import { PinProps, PresBox } from './trails';
import React = require('react');
import Color = require('color');

export const pageSchema = createSchema({
    googlePhotosUrl: 'string',
    googlePhotosTags: 'string',
});
const uploadIcons = {
    idle: 'downarrow.png',
    loading: 'loading.gif',
    success: 'greencheck.png',
    failure: 'redx.png',
};

@observer
export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
    protected _multiTouchDisposer?: import('../../util/InteractionUtils').InteractionUtils.MultiTouchEventDisposer | undefined;
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(ImageBox, fieldKey);
    }
    private _ignoreScroll = false;
    private _forcedScroll = false;
    private _dropDisposer?: DragManager.DragDropDisposer;
    private _disposers: { [name: string]: IReactionDisposer } = {};
    private _getAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = () => undefined;
    @observable _curSuffix = '';
    @observable _uploadIcon = uploadIcons.idle;

    constructor(props: any) {
        super(props);
        this.props.setContentView?.(this);
    }

    protected createDropTarget = (ele: HTMLDivElement) => {
        this._dropDisposer?.();
        ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
    };

    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
        const anchor =
            this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor
            Docs.Create.ImageanchorDocument({
                title: 'ImgAnchor:' + this.rootDoc.title,
                presPanX: NumCast(this.layoutDoc._panX),
                presPanY: NumCast(this.layoutDoc._panY),
                presViewScale: Cast(this.layoutDoc._viewScale, 'number', null),
                presTransition: 1000,
                unrendered: true,
                annotationOn: this.rootDoc,
            });
        if (anchor) {
            if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
            /* addAsAnnotation &&*/ this.addDocument(anchor);
            PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: true } }, this.rootDoc);
            return anchor;
        }
        return this.rootDoc;
    };

    componentDidMount() {
        this._disposers.sizer = reaction(
            () => ({
                forceFull: this.props.renderDepth < 1 || this.layoutDoc._showFullRes,
                scrSize: (this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth) * NumCast(this.rootDoc._viewScale, 1),
                selected: this.props.isSelected(),
            }),
            ({ forceFull, scrSize, selected }) => (this._curSuffix = selected ? '_o' : this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 ? '_l' : '_o'),
            { fireImmediately: true, delay: 1000 }
        );
        const layoutDoc = this.layoutDoc;
        this._disposers.path = reaction(
            () => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
            ({ nativeSize, width }) => {
                if (layoutDoc === this.layoutDoc || !this.layoutDoc._height) {
                    this.layoutDoc._height = (width * nativeSize.nativeHeight) / nativeSize.nativeWidth;
                }
            },
            { fireImmediately: true }
        );
        this._disposers.scroll = reaction(
            () => this.layoutDoc._scrollTop,
            s_top => {
                this._forcedScroll = true;
                !this._ignoreScroll && this._mainCont.current && (this._mainCont.current.scrollTop = NumCast(s_top));
                this._mainCont.current?.scrollTo({ top: NumCast(s_top) });
                this._forcedScroll = false;
            },
            { fireImmediately: true }
        );
    }

    componentWillUnmount() {
        Object.values(this._disposers).forEach(disposer => disposer?.());
    }

    @undoBatch
    @action
    drop = (e: Event, de: DragManager.DropEvent) => {
        if (de.complete.docDragData) {
            if (de.metaKey) {
                de.complete.docDragData.droppedDocuments.forEach(
                    action((drop: Doc) => {
                        Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop);
                        e.stopPropagation();
                    })
                );
            } else if (de.altKey || !this.dataDoc[this.fieldKey]) {
                const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
                const targetField = Doc.LayoutFieldKey(layoutDoc);
                const targetDoc = layoutDoc[DataSym];
                if (targetDoc[targetField] instanceof ImageField) {
                    this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField);
                    Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc), this.fieldKey);
                    Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(targetDoc), this.fieldKey);
                    e.stopPropagation();
                }
            }
        }
    };

    @undoBatch
    resolution = () => (this.layoutDoc._showFullRes = !this.layoutDoc._showFullRes);
    @undoBatch
    setUseAlt = () => (this.layoutDoc[this.fieldKey + '-useAlt'] = !this.layoutDoc[this.fieldKey + '-useAlt']);

    @undoBatch
    setNativeSize = action(() => {
        const scaling = (this.props.DocumentView?.().props.ScreenToLocalTransform().Scale || 1) / NumCast(this.rootDoc._viewScale, 1);
        const nscale = NumCast(this.props.PanelWidth()) / scaling;
        const nh = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
        const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
        this.dataDoc[this.fieldKey + '-nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']) * nh;
        this.dataDoc[this.fieldKey + '-nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']) * nh;
        this.rootDoc._panX = nh * NumCast(this.rootDoc._panX);
        this.rootDoc._panY = nh * NumCast(this.rootDoc._panY);
        this.dataDoc._panXMax = this.dataDoc._panXMax ? nh * NumCast(this.dataDoc._panXMax) : undefined;
        this.dataDoc._panXMin = this.dataDoc._panXMin ? nh * NumCast(this.dataDoc._panXMin) : undefined;
        this.dataDoc._panYMax = this.dataDoc._panYMax ? nw * NumCast(this.dataDoc._panYMax) : undefined;
        this.dataDoc._panYMin = this.dataDoc._panYMin ? nw * NumCast(this.dataDoc._panYMin) : undefined;
    });
    @undoBatch
    rotate = action(() => {
        const nw = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
        const nh = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
        const w = this.layoutDoc._width;
        const h = this.layoutDoc._height;
        this.dataDoc[this.fieldKey + '-rotation'] = (NumCast(this.dataDoc[this.fieldKey + '-rotation']) + 90) % 360;
        this.dataDoc[this.fieldKey + '-nativeWidth'] = nh;
        this.dataDoc[this.fieldKey + '-nativeHeight'] = nw;
        this.layoutDoc._width = h;
        this.layoutDoc._height = w;
    });

    crop = (region: Doc | undefined, addCrop?: boolean) => {
        if (!region) return;
        const cropping = Doc.MakeCopy(region, true);
        Doc.GetProto(region).lockedPosition = true;
        Doc.GetProto(region).title = 'region:' + this.rootDoc.title;
        Doc.GetProto(region).followLinkToggle = true;
        this.addDocument(region);
        const anchx = NumCast(cropping.x);
        const anchy = NumCast(cropping.y);
        const anchw = NumCast(cropping._width);
        const anchh = NumCast(cropping._height);
        const viewScale = NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']) / anchw;
        cropping.title = 'crop: ' + this.rootDoc.title;
        cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
        cropping.y = NumCast(this.rootDoc.y);
        cropping._width = anchw * (this.props.NativeDimScaling?.() || 1);
        cropping._height = anchh * (this.props.NativeDimScaling?.() || 1);
        cropping.isLinkButton = undefined;
        const croppingProto = Doc.GetProto(cropping);
        croppingProto.annotationOn = undefined;
        croppingProto.isPrototype = true;
        croppingProto.backgroundColor = undefined;
        croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
        croppingProto.type = DocumentType.IMG;
        croppingProto.layout = ImageBox.LayoutString('data');
        croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
        croppingProto['data-nativeWidth'] = anchw;
        croppingProto['data-nativeHeight'] = anchh;
        croppingProto.viewScale = viewScale;
        croppingProto.viewScaleMin = viewScale;
        croppingProto.panX = anchx / viewScale;
        croppingProto.panY = anchy / viewScale;
        croppingProto.panXMin = anchx / viewScale;
        croppingProto.panXMax = anchw / viewScale;
        croppingProto.panYMin = anchy / viewScale;
        croppingProto.panYMax = anchh / viewScale;
        if (addCrop) {
            DocUtils.MakeLink(region, cropping, { linkRelationship: 'cropped image' });
            cropping.x = NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]();
            cropping.y = NumCast(this.rootDoc.y);
            this.props.addDocTab(cropping, OpenWhere.inParent);
        }
        DocumentManager.Instance.AddViewRenderedCb(cropping, dv => setTimeout(() => (dv.ComponentView as ImageBox).setNativeSize(), 200));
        this.props.bringToFront(cropping);
        return cropping;
    };

    specificContextMenu = (e: React.MouseEvent): void => {
        const field = Cast(this.dataDoc[this.fieldKey], ImageField);
        if (field) {
            const funcs: ContextMenuProps[] = [];
            funcs.push({ description: 'Rotate Clockwise 90', event: this.rotate, icon: 'redo-alt' });
            funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? 'Dynamic Res' : 'Full Res'}`, event: this.resolution, icon: 'expand' });
            funcs.push({ description: 'Set Native Pixel Size', event: this.setNativeSize, icon: 'expand-arrows-alt' });
            funcs.push({ description: `${this.layoutDoc[this.fieldKey + '-useAlt'] ? 'Show Alternate' : 'Show Primary'}`, event: this.setUseAlt, icon: 'image' });
            funcs.push({ description: 'Copy path', event: () => Utils.CopyText(this.choosePath(field.url)), icon: 'copy' });
            if (!Doc.noviceMode) {
                funcs.push({ description: 'Export to Google Photos', event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: 'caret-square-right' });

                const existingAnalyze = ContextMenu.Instance?.findByDescription('Analyzers...');
                const modes: ContextMenuProps[] = existingAnalyze && 'subitems' in existingAnalyze ? existingAnalyze.subitems : [];
                modes.push({ description: 'Generate Tags', event: this.generateMetadata, icon: 'tag' });
                modes.push({ description: 'Find Faces', event: this.extractFaces, icon: 'camera' });
                //modes.push({ description: "Recommend", event: this.extractText, icon: "brain" });
                !existingAnalyze && ContextMenu.Instance?.addItem({ description: 'Analyzers...', subitems: modes, icon: 'hand-point-right' });
            }

            ContextMenu.Instance?.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
        }
    };

    extractFaces = () => {
        const converter = (results: any) => {
            return results.map((face: CognitiveServices.Image.Face) => Doc.Get.FromJson({ data: face, title: `Face: ${face.faceId}` })!);
        };
        this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + '-faces'], this.url, Service.Face, converter);
    };

    generateMetadata = (threshold: Confidence = Confidence.Excellent) => {
        const converter = (results: any) => {
            const tagDoc = new Doc();
            const tagsList = new List();
            results.tags.map((tag: Tag) => {
                tagsList.push(tag.name);
                const sanitized = tag.name.replace(' ', '_');
                tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`);
            });
            this.dataDoc[this.fieldKey + '-generatedTags'] = tagsList;
            tagDoc.title = 'Generated Tags Doc';
            tagDoc.confidence = threshold;
            return tagDoc;
        };
        this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + '-generatedTagsDoc'], this.url, Service.ComputerVision, converter);
    };

    @computed private get url() {
        const data = Cast(this.dataDoc[this.fieldKey], ImageField);
        return data ? data.url.href : undefined;
    }

    choosePath(url: URL) {
        const lower = url.href.toLowerCase();
        if (url.protocol === 'data') return url.href;
        if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href);
        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return `/assets/unknown-file-icon-hi.png`;

        const ext = extname(url.href);
        return url.href.replace(ext, this._curSuffix + ext);
    }

    considerGooglePhotosLink = () => {
        const remoteUrl = this.dataDoc.googlePhotosUrl;
        return !remoteUrl ? null : <img draggable={false} style={{ transformOrigin: 'bottom right' }} id={'google-photos'} src={'/assets/google_photos.png'} onClick={() => window.open(remoteUrl)} />;
    };

    considerGooglePhotosTags = () => {
        const tags = this.dataDoc.googlePhotosTags;
        return !tags ? null : <img id={'google-tags'} src={'/assets/google_tags.png'} />;
    };

    getScrollHeight = () => (this.props.fitWidth?.(this.rootDoc) !== false && NumCast(this.rootDoc._viewScale, 1) === NumCast(this.rootDoc._viewScaleMin, 1) ? this.nativeSize.nativeHeight : undefined);

    @computed
    private get considerDownloadIcon() {
        const data = this.dataDoc[this.fieldKey];
        if (!(data instanceof ImageField)) {
            return null;
        }
        const primary = data.url.href;
        if (primary.includes(window.location.origin)) {
            return null;
        }
        return (
            <img
                id={'upload-icon'}
                draggable={false}
                style={{ transformOrigin: 'bottom right' }}
                src={`/assets/${this._uploadIcon}`}
                onClick={async () => {
                    const { dataDoc } = this;
                    const { success, failure, idle, loading } = uploadIcons;
                    runInAction(() => (this._uploadIcon = loading));
                    const [{ accessPaths }] = await Networking.PostToServer('/uploadRemoteImage', { sources: [primary] });
                    dataDoc[this.props.fieldKey + '-originalUrl'] = primary;
                    let succeeded = true;
                    let data: ImageField | undefined;
                    try {
                        data = new ImageField(accessPaths.agnostic.client);
                    } catch {
                        succeeded = false;
                    }
                    runInAction(() => (this._uploadIcon = succeeded ? success : failure));
                    setTimeout(
                        action(() => {
                            this._uploadIcon = idle;
                            data && (dataDoc[this.fieldKey] = data);
                        }),
                        2000
                    );
                }}
            />
        );
    }

    @computed get nativeSize() {
        TraceMobx();
        const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth'], 500));
        const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '-nativeHeight'], 1));
        const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '-nativeOrientation'], 1);
        return { nativeWidth, nativeHeight, nativeOrientation };
    }

    @computed get paths() {
        const field = Cast(this.dataDoc[this.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
        const alts = DocListCast(this.dataDoc[this.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
        const altpaths = alts
            .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
            .filter(url => url)
            .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
        const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
        return paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
    }

    @observable _isHovering = false; // flag to switch between primary and alternate images on hover
    @computed get content() {
        TraceMobx();

        const backAlpha = DashColor(this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor)).alpha();
        const srcpath = this.layoutDoc.hideImage ? '' : this.paths[0];
        const fadepath = this.layoutDoc.hideImage ? '' : this.paths.lastElement();
        const { nativeWidth, nativeHeight, nativeOrientation } = this.nativeSize;
        const rotation = NumCast(this.dataDoc[this.fieldKey + '-rotation']);
        const aspect = rotation % 180 ? nativeHeight / nativeWidth : 1;
        let transformOrigin = 'center center';
        let transform = `translate(0%, 0%) rotate(${rotation}deg) scale(${aspect})`;
        if (rotation === 90 || rotation === -270) {
            transformOrigin = 'top left';
            transform = `translate(100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
        } else if (rotation === 180) {
            transform = `rotate(${rotation}deg) scale(${aspect})`;
        } else if (rotation === 270 || rotation === -90) {
            transformOrigin = 'right top';
            transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
        }

        return (
            <div className="imageBox-cont" onPointerEnter={action(() => (this._isHovering = true))} onPointerLeave={action(() => (this._isHovering = false))} key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
                <div className="imageBox-fader" style={{ opacity: backAlpha }}>
                    <img key="paths" src={srcpath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
                    {fadepath === srcpath ? null : (
                        <div
                            className={`imageBox-fadeBlocker${(this._isHovering && this.layoutDoc[this.fieldKey + '-useAlt'] === undefined) || BoolCast(this.layoutDoc[this.fieldKey + '-useAlt']) ? '-hover' : ''}`}
                            style={{ transition: StrCast(this.layoutDoc.viewTransition, 'opacity 1000ms') }}>
                            <img className="imageBox-fadeaway" key="fadeaway" src={fadepath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
                        </div>
                    )}
                </div>
                {!Doc.noviceMode && this.considerDownloadIcon}
                {this.considerGooglePhotosLink()}
                <FaceRectangles document={this.dataDoc} color={'#0000FF'} backgroundColor={'#0000FF'} />
            </div>
        );
    }

    private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
    private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
    @observable _marqueeing: number[] | undefined;
    @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
    @computed get annotationLayer() {
        TraceMobx();
        return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
    }
    screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop) * this.props.ScreenToLocalTransform().Scale);
    marqueeDown = (e: React.PointerEvent) => {
        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale, 1) <= NumCast(this.rootDoc.viewScaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
            setupMoveUpEvents(
                this,
                e,
                action(e => {
                    MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                    this._marqueeing = [e.clientX, e.clientY];
                    return true;
                }),
                returnFalse,
                () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
                false
            );
        }
    };
    @action
    finishMarquee = () => {
        this._getAnchor = AnchorMenu.Instance?.GetAnchor;
        this._marqueeing = undefined;
        this.props.select(false);
    };
    focus = (anchor: Doc, options: DocFocusOptions) => this._ffref.current?.focus(anchor, options);

    _ffref = React.createRef<CollectionFreeFormView>();
    savedAnnotations = () => this._savedAnnotations;
    render() {
        TraceMobx();
        const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
        const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this.props.NativeDimScaling?.() || 1)}px` : borderRad;
        return (
            <div
                className="imageBox"
                onContextMenu={this.specificContextMenu}
                ref={this._mainCont}
                onScroll={action(e => {
                    if (!this._forcedScroll) {
                        if (this.layoutDoc._scrollTop || this._mainCont.current?.scrollTop) {
                            this._ignoreScroll = true;
                            this.layoutDoc._scrollTop = this._mainCont.current?.scrollTop;
                            this._ignoreScroll = false;
                        }
                    }
                })}
                style={{
                    width: this.props.PanelWidth() ? undefined : `100%`,
                    height: this.props.PanelWidth() ? undefined : `100%`,
                    pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
                    borderRadius,
                    overflow: this.layoutDoc.fitWidth || this.props.fitWidth?.(this.rootDoc) ? 'auto' : undefined,
                }}>
                <CollectionFreeFormView
                    ref={this._ffref}
                    {...this.props}
                    setContentView={emptyFunction}
                    NativeWidth={returnZero}
                    NativeHeight={returnZero}
                    renderDepth={this.props.renderDepth + 1}
                    fieldKey={this.annotationKey}
                    styleProvider={this.props.styleProvider}
                    CollectionView={undefined}
                    isAnnotationOverlay={true}
                    annotationLayerHostsContent={true}
                    PanelWidth={this.props.PanelWidth}
                    PanelHeight={this.props.PanelHeight}
                    ScreenToLocalTransform={this.screenToLocalTransform}
                    select={emptyFunction}
                    focus={this.focus}
                    getScrollHeight={this.getScrollHeight}
                    NativeDimScaling={returnOne}
                    isAnyChildContentActive={returnFalse}
                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                    removeDocument={this.removeDocument}
                    moveDocument={this.moveDocument}
                    addDocument={this.addDocument}>
                    {this.content}
                </CollectionFreeFormView>
                {this.annotationLayer}
                {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
                    <MarqueeAnnotator
                        rootDoc={this.rootDoc}
                        scrollTop={0}
                        down={this._marqueeing}
                        scaling={this.props.NativeDimScaling}
                        docView={this.props.docViewPath().slice(-1)[0]}
                        addDocument={this.addDocument}
                        finishMarquee={this.finishMarquee}
                        savedAnnotations={this.savedAnnotations}
                        selectionText={returnEmptyString}
                        annotationLayer={this._annotationLayer.current}
                        mainCont={this._mainCont.current}
                        highlightDragSrcColor={''}
                        anchorMenuCrop={this.crop}
                    />
                )}
            </div>
        );
    }
}
