import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import BingMapsReact from 'bingmaps-react';
// import 'mapbox-gl/dist/mapbox-gl.css';

import { Button, EditableText, IconButton, Type } from 'browndash-components';
import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, flow, toJS} from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc';
import { DocCss, Highlight } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { DocCast, NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
import { Docs, DocUtils } from '../../../documents/Documents';
import { DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager } from '../../../util/DragManager';
import { LinkManager } from '../../../util/LinkManager';
import { SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoable, UndoManager } from '../../../util/UndoManager';
import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
import { SidebarAnnos } from '../../SidebarAnnos';
import { DocumentView } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
import { FormattedTextBox } from '../formattedText/FormattedTextBox';
import { PinProps, PresBox } from '../trails';
import { MapAnchorMenu } from './MapAnchorMenu';
import { 
    Map as MapboxMap,
    MapRef,
    Marker, 
    ControlPosition, 
    FullscreenControl,  
    MapProvider, 
    MarkerProps, 
    NavigationControl, 
    ScaleControl, 
    ViewState, 
    ViewStateChangeEvent,
    useControl,
    GeolocateControl,
    Popup,
    MapEvent,
    Source,
    Layer} from 'react-map-gl';
import MapboxGeocoder, {GeocoderOptions} from '@mapbox/mapbox-gl-geocoder';
import debounce from 'debounce';
import './MapBox.scss';
import { NumberLiteralType } from 'typescript';
// import { GeocoderControl } from './GeocoderControl';
import mapboxgl, { LngLat, MapLayerMouseEvent } from 'mapbox-gl';
import { Feature, FeatureCollection } from 'geojson';
import { MarkerEvent } from 'react-map-gl/dist/esm/types';
import { MapboxApiUtility, TransportationType} from './MapboxApiUtility';
import { Autocomplete, TextField } from '@mui/material';
import { List } from '../../../../fields/List';

// amongus
/**
 * MapBox architecture:
 * Main component: MapBox.tsx
 * Supporting Components: SidebarAnnos, CollectionStackingView
 *
 * MapBox is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content.
 * The main body of MapBox uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view.
 * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available,
 * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map).
 * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts).
 * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
 */

const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/  and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>
const MAPBOX_ACCESS_TOKEN = 'pk.eyJ1IjoiemF1bHRhdmFuZ2FyIiwiYSI6ImNscHgwNDd1MDA3MXIydm92ODdianp6cGYifQ.WFAqbhwxtMHOWSPtu0l2uQ';
const MAPBOX_FORWARD_GEOCODE_BASE_URL = 'https://api.mapbox.com/geocoding/v5/mapbox.places/';

const MAPBOX_REVERSE_GEOCODE_BASE_URL = 'https://api.mapbox.com/geocoding/v5/mapbox.places/';

type PopupInfo = {
    longitude: number,
    latitude: number,
    title: string,
    description: string
}

export type GeocoderControlProps = Omit<GeocoderOptions, 'accessToken' | 'mapboxgl' | 'marker'> & {
    mapboxAccessToken: string,
    marker?: Omit<MarkerProps, 'longitude' | 'latitude'>;
    position: ControlPosition;

    onResult: (...args: any[]) => void;
}

type MapMarker = {
    longitude: number,
    latitude: number
}

/**
 * Consider integrating later: allows for drawing, circling, making shapes on map
 */
// const drawingManager = new window.google.maps.drawing.DrawingManager({
//     drawingControl: true,
//     drawingControlOptions: {
//         position: google.maps.ControlPosition.TOP_RIGHT,
//         drawingModes: [
//             google.maps.drawing.OverlayType.MARKER,
//             // currently we are not supporting the following drawing mode on map, a thought for future development
//             google.maps.drawing.OverlayType.CIRCLE,
//             google.maps.drawing.OverlayType.POLYLINE,
//         ],
//     },
// });

@observer
export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(MapBox, fieldKey);
    }
    private _dragRef = React.createRef<HTMLDivElement>();
    private _sidebarRef = React.createRef<SidebarAnnos>();
    private _ref: React.RefObject<HTMLDivElement> = React.createRef();
    private _mapRef: React.RefObject<MapRef> = React.createRef();
    private _disposers: { [key: string]: IReactionDisposer } = {};
    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void);

    @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
    @computed get allSidebarDocs() {
        return DocListCast(this.dataDoc[this.SidebarKey]);
    }
    // this list contains pushpins and configs
    @computed get allAnnotations() {
        return DocListCast(this.dataDoc[this.annotationKey]);
    }
    @computed get allPushpins() {
        return this.allAnnotations.filter(anno => anno.type === DocumentType.PUSHPIN);
    }
    @computed get allRoutes() {
        return this.allAnnotations.filter(anno => anno.type === DocumentType.MAPROUTE);
    }
    @computed get allRoutesGeoJson() {
        const features = this.allRoutes.map(route => {
            return {
              type: 'Feature',
              properties: {},
              geometry: {
                type: 'LineString',
                coordinates: route.coordinates
              }
            };
          });
        
          return {
            type: 'FeatureCollection',
            features: features
          };
    }
    @computed get SidebarShown() {
        return this.layoutDoc._layout_showSidebar ? true : false;
    }
    @computed get sidebarWidthPercent() {
        return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
    }
    @computed get sidebarColor() {
        return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
    }
    @computed get SidebarKey() {
        return this.fieldKey + '_sidebar';
    }

    componentDidMount() {
        this._unmounting = false;
        this.props.setContentView?.(this);

    }


    _unmounting = false;
    componentWillUnmount(): void {
        this._unmounting = true;
        this.deselectPin();
        this._rerenderTimeout && clearTimeout(this._rerenderTimeout);
        Object.keys(this._disposers).forEach(key => this._disposers[key]?.());
    }

    /**
     * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
     * @param doc
     * @param sidebarKey
     * @returns
     */
    sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
        if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
        const docs = doc instanceof Doc ? [doc] : doc;
        docs.forEach(doc => {
            let existingPin = this.allPushpins.find(pin => pin.latitude === doc.latitude && pin.longitude === doc.longitude) ?? this.selectedPin;
            if (doc.latitude !== undefined && doc.longitude !== undefined && !existingPin) {
                existingPin = this.createPushpin(NumCast(doc.latitude), NumCast(doc.longitude), StrCast(doc.map));
            }
            if (existingPin) {
                setTimeout(() => {
                    // we use a timeout in case this is called from the sidebar which may have just added a link that hasn't made its way into th elink manager yet
                    if (!LinkManager.Instance.getAllRelatedLinks(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) {
                        const anchor = this.getAnchor(true, undefined, existingPin);
                        anchor && DocUtils.MakeLink(anchor, doc, { link_relationship: 'link to map location' });
                        doc.latitude = existingPin?.latitude;
                        doc.longitude = existingPin?.longitude;
                    }
                });
            }
        }); //add to annotation list

        return this.addDocument(doc, sidebarKey); // add to sidebar list
    };

    removeMapDocument = (doc: Doc | Doc[], annotationKey?: string) => {
        const docs = doc instanceof Doc ? [doc] : doc;
        this.allAnnotations.filter(anno => docs.includes(DocCast(anno.mapPin))).forEach(anno => (anno.mapPin = undefined));
        return this.removeDocument(doc, annotationKey, undefined);
    };

    /**
     * Removing documents from the sidebar
     * @param doc
     * @param sidebarKey
     * @returns
     */
    sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => this.removeMapDocument(doc, sidebarKey);

    /**
     * Toggle sidebar onclick the tiny comment button on the top right corner
     * @param e
     */
    sidebarBtnDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(
            this,
            e,
            (e, down, delta) =>
                runInAction(() => {
                    const localDelta = this.props
                        .ScreenToLocalTransform()
                        .scale(this.props.NativeDimScaling?.() || 1)
                        .transformDirection(delta[0], delta[1]);
                    const fullWidth = NumCast(this.layoutDoc._width);
                    const mapWidth = fullWidth - this.sidebarWidth();
                    if (this.sidebarWidth() + localDelta[0] > 0) {
                        this.layoutDoc._layout_showSidebar = true;
                        this.layoutDoc._width = fullWidth + localDelta[0];
                        this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
                    } else {
                        this.layoutDoc._layout_showSidebar = false;
                        this.layoutDoc._width = mapWidth;
                        this.layoutDoc._layout_sidebarWidthPercent = '0%';
                    }
                    return false;
                }),
            emptyFunction,
            () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
        );
    };
    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();

    /**
     * Handles toggle of sidebar on click the little comment button
     */
    @computed get sidebarHandle() {
        return (
            <div
                className="mapBox-overlayButton-sidebar"
                key="sidebar"
                title="Toggle Sidebar"
                style={{
                    display: !this.props.isContentActive() ? 'none' : undefined,
                    top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
                    backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                }}
                onPointerDown={this.sidebarBtnDown}>
                <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
            </div>
        );
    }

    // TODO: Adding highlight box layer to Maps
    @action
    toggleSidebar = () => {
        const prevWidth = this.sidebarWidth();
        this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
        this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
    };

    startAnchorDrag = (e: PointerEvent, ele: HTMLElement) => {
        e.preventDefault();
        e.stopPropagation();

        const sourceAnchorCreator = action(() => {
            const note = this.getAnchor(true);
            if (note && this.selectedPin) {
                note.latitude = this.selectedPin.latitude;
                note.longitude = this.selectedPin.longitude;
                note.map = this.selectedPin.map;
            }
            return note as Doc;
        });

        const targetCreator = (annotationOn: Doc | undefined) => {
            const target = DocUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
            FormattedTextBox.SelectOnLoad = target[Id];
            return target;
        };
        const docView = this.props.DocumentView?.();
        docView &&
            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
                dragComplete: e => {
                    if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
                        e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.Document;
                        e.annoDragData.linkSourceDoc.followLinkZoom = false;
                    }
                },
            });
    };

    createNoteAnnotation = () => {
        const createFunc = undoable(
            action(() => {
                const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(true), ['latitude', 'longitude', LinkedTo]);
                if (note && this.selectedPin) {
                    note.latitude = this.selectedPin.latitude;
                    note.longitude = this.selectedPin.longitude;
                    note.map = this.selectedPin.map;
                }
            }),
            'create note annotation'
        );
        if (!this.layoutDoc.layout_showSidebar) {
            this.toggleSidebar();
            setTimeout(createFunc);
        } else createFunc();
    };
    sidebarDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
    };
    sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
        const bounds = this._ref.current!.getBoundingClientRect();
        this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
        this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
        e.preventDefault();
        return false;
    };

    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => (this._setPreviewCursor = func);

    addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => this.addDocument(doc, annotationKey);

    pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none');

    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
    transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
    opaqueFilter = () => [...this.props.childFilters(), Utils.OpaqueBackgroundFilter];
    infoWidth = () => this.props.PanelWidth() / 5;
    infoHeight = () => this.props.PanelHeight() / 5;
    anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
    savedAnnotations = () => this._savedAnnotations;

    _bingSearchManager: any;
    _bingMap: any;
    get MicrosoftMaps() {
        return (window as any).Microsoft.Maps;
    }
    // uses Bing Search to retrieve lat/lng for a location.  eg.,
    //   const results = this.geocodeQuery(map.map, 'Philadelphia, PA');
    // to move the map to that location:
    //     const location = await this.geocodeQuery(this._bingMap, 'Philadelphia, PA');
    //     this._bingMap.current.setView({
    //        mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
    //        center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
    //     });
    //
    bingGeocode = (map: any, query: string) => {
        return new Promise<{ latitude: number; longitude: number }>((res, reject) => {
            //If search manager is not defined, load the search module.
            if (!this._bingSearchManager) {
                //Create an instance of the search manager and call the geocodeQuery function again.
                this.MicrosoftMaps.loadModule('Microsoft.Maps.Search', () => {
                    this._bingSearchManager = new this.MicrosoftMaps.Search.SearchManager(map.current);
                    res(this.bingGeocode(map, query));
                });
            } else {
                this._bingSearchManager.geocode({
                    where: query,
                    callback: action((r: any) => res(r.results[0].location)),
                    errorCallback: (e: any) => reject(),
                });
            }
        });
    };

    @observable
    bingSearchBarContents: any = this.rootDoc.map; // For Bing Maps: The contents of the Bing search bar (string)

    geoDataRequestOptions = {
        entityType: 'PopulatedPlace',
    };

    // The pin that is selected
    @observable selectedPin: Doc | undefined;

    @action
    deselectPin = () => {
        if (this.selectedPin) {
            // // Removes filter
            // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
            // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
            // Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');

            // const temp = this.selectedPin;
            // if (!this._unmounting) {
            //     this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp));
            // }
            // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(temp.latitude, temp.longitude));
            // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(temp as Doc));
            // if (!this._unmounting) {
            //     this._bingMap.current.entities.push(newpin);
            // }
            // this.map_docToPinMap.set(temp, newpin);
            // this.selectedPin = undefined;
            // this.bingSearchBarContents = this.rootDoc.map;
        }
    };

    getView = async (doc: Doc) => {
        if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar();
        return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
    };
    /*
     * Pushpin onclick
     */
    @action
    pushpinClicked = (pinDoc: Doc) => {
        this.deselectPin();
        this.selectedPin = pinDoc;
        this.bingSearchBarContents = pinDoc.map;

        // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
        // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
        Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');

        this.recolorPin(this.selectedPin, 'green');

        MapAnchorMenu.Instance.Delete = this.deleteSelectedPin;
        MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
        MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation;
        MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag;

        const point = this._bingMap.current.tryLocationToPixel(new this.MicrosoftMaps.Location(this.selectedPin.latitude, this.selectedPin.longitude));
        const x = point.x + (this.props.PanelWidth() - this.sidebarWidth()) / 2;
        const y = point.y + this.props.PanelHeight() / 2 + 32;
        const cpt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y);
        MapAnchorMenu.Instance.jumpTo(cpt[0], cpt[1], true);

        document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
    };

    /**
     * Map OnClick
     */
    @action
    mapOnClick = (e: { location: { latitude: any; longitude: any } }) => {
        this.props.select(false);
        this.deselectPin();
    };
    /*
     * Updates values of layout doc to match the current map
     */
    @action
    mapRecentered = () => {
        if (
            Math.abs(NumCast(this.dataDoc.latitude) - this._bingMap.current.getCenter().latitude) > 1e-7 || //
            Math.abs(NumCast(this.dataDoc.longitude) - this._bingMap.current.getCenter().longitude) > 1e-7
        ) {
            this.dataDoc.latitude = this._bingMap.current.getCenter().latitude;
            this.dataDoc.longitude = this._bingMap.current.getCenter().longitude;
            this.dataDoc.map = '';
            this.bingSearchBarContents = '';
        }
        this.dataDoc.map_zoom = this._bingMap.current.getZoom();
    };
    /*
     * Updates maptype
     */
    @action
    updateMapType = () => (this.dataDoc.map_type = this._bingMap.current.getMapTypeId());

    /*
     * For Bing Maps
     * Called by search button's onClick
     * Finds the geocode of the searched contents and sets location to that location
     **/
    @action
    bingSearch = () => {
        return this.bingGeocode(this._bingMap, this.bingSearchBarContents).then(location => {
            this.dataDoc.latitude = location.latitude;
            this.dataDoc.longitude = location.longitude;
            this.dataDoc.map_zoom = this._bingMap.current.getZoom();
            this.dataDoc.map = this.bingSearchBarContents;
        });
    };

    /*
     * Returns doc w/ relevant info
     */
    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps, existingPin?: Doc) => {
        /// this should use SELECTED pushpin for lat/long  if there is a selection, otherwise CENTER
        const anchor = Docs.Create.ConfigDocument({
            title: 'MapAnchor:' + this.rootDoc.title,
            text: StrCast(this.selectedPin?.map) || StrCast(this.rootDoc.map) || 'map location',
            config_latitude: NumCast((existingPin ?? this.selectedPin)?.latitude ?? this.dataDoc.latitude),
            config_longitude: NumCast((existingPin ?? this.selectedPin)?.longitude ?? this.dataDoc.longitude),
            config_map_zoom: NumCast(this.dataDoc.map_zoom),
            config_map_type: StrCast(this.dataDoc.map_type),
            config_map: StrCast((existingPin ?? this.selectedPin)?.map) || StrCast(this.dataDoc.map),
            layout_unrendered: true,
            mapPin: existingPin ?? this.selectedPin,
            annotationOn: this.rootDoc,
        });
        if (anchor) {
            if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
            addAsAnnotation && this.addDocument(anchor);
            PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc);
            return anchor;
        }
        return this.rootDoc;
    };

    map_docToPinMap = new Map<Doc, any>();
    map_pinHighlighted = new Map<Doc, boolean>();
    /*
     *   Input: pin doc
     *   Adds MicrosoftMaps Pushpin to the map (render)
     */
    @action
    addPushpin = (pin: Doc) => {
        const pushPin = pin.infoWindowOpen
            ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), {})
            : new this.MicrosoftMaps.Pushpin(
                  new this.MicrosoftMaps.Location(pin.latitude, pin.longitude)
                  // {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'}
              );

        this._bingMap.current.entities.push(pushPin);

        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin));
        // this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin));
        this.map_docToPinMap.set(pin, pushPin);
    };

    /*
     *   Input: pin doc
     *   Removes pin from annotations
     */
    @action
    removePushpin = (pinDoc: Doc) => this.removeMapDocument(pinDoc, this.annotationKey);

    /*
     * Removes pushpin from map render
     */
    deletePushpin = (pinDoc: Doc) => {
        if (!this._unmounting) {
            this._bingMap.current.entities.remove(this.map_docToPinMap.get(pinDoc));
        }
        this.map_docToPinMap.delete(pinDoc);
        this.selectedPin = undefined;
    };

    @action
    deleteSelectedPin = undoable(() => {
        if (this.selectedPin) {
            // Removes filter
            Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
            Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
            Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');

            this.removePushpin(this.selectedPin);
        }
        MapAnchorMenu.Instance.fadeOut(true);
        document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
    }, 'delete pin');

    tryHideMapAnchorMenu = (e: PointerEvent) => {
        let target = document.elementFromPoint(e.x, e.y);
        while (target) {
            if (target.id === 'route-destination-searcher-listbox') return;
            if (target === MapAnchorMenu.top.current) return;
            target = target.parentElement;
        }
        e.stopPropagation();
        e.preventDefault();
        MapAnchorMenu.Instance.fadeOut(true);
        document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
    };

    @action
    centerOnSelectedPin = () => {
        if (this.selectedPin) {
            this._mapRef.current?.flyTo({
                center: [NumCast(this.selectedPin.longitude), NumCast(this.selectedPin.latitude)]
            })
        }
        // if (this.selectedPin) {
        //     this.dataDoc.latitude = this.selectedPin.latitude;
        //     this.dataDoc.longitude = this.selectedPin.longitude;
        //     this.dataDoc.map = this.selectedPin.map ?? '';
        //     this.bingSearchBarContents = this.selectedPin.map;
        // }
        MapAnchorMenu.Instance.fadeOut(true);
        document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu);
    };

    /**
     * View options for bing maps
     */
    bingViewOptions = {
        // center: { latitude: this.dataDoc.latitude ?? defaultCenter.lat, longitude: this.dataDoc.longitude ?? defaultCenter.lng },
        zoom: this.dataDoc.latitude ?? 10,
        mapTypeId: 'grayscale',
    };


    /**
     * Map options
     */
    bingMapOptions = {
        navigationBarMode: 'square',
        backgroundColor: '#f1f3f4',
        enableInertia: true,
        supportedMapTypes: ['grayscale', 'canvasLight'],
        disableMapTypeSelectorMouseOver: true,
        // showScalebar:true
        // disableRoadView:true,
        // disableBirdseye:true
        streetsideOptions: {
            showProblemReporting: false,
            showCurrentAddress: false,
        },
    };



    recolorPin = (pin: Doc, color?: string) => {
        // this._bingMap.current.entities.remove(this.map_docToPinMap.get(pin));
        // this.map_docToPinMap.delete(pin);
        // const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), color ? { color } : {});
        // this.MicrosoftMaps.Events.addHandler(newpin, 'click', (e: any) => this.pushpinClicked(pin));
        // this._bingMap.current.entities.push(newpin);
        // this.map_docToPinMap.set(pin, newpin);
    };

    /*
     * Called when BingMap is first rendered
     * Initializes starting values
     */
    @observable _mapReady = false;
    @action
    bingMapReady = (map: any) => {
        this._mapReady = true;
        this._bingMap = map.map;
        if (!this._bingMap.current) {
            alert('NO Map!?');
        }
        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', this.mapOnClick);
        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.mapRecentered, 'Map Layout Change'));
        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change'));

        this._disposers.mapLocation = reaction(
            () => this.rootDoc.map,
            mapLoc => (this.bingSearchBarContents = mapLoc),
            { fireImmediately: true }
        );
        this._disposers.highlight = reaction(
            () => this.allAnnotations.map(doc => doc[Highlight]),
            () => {
                const allConfigPins = this.allAnnotations.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin);
                allConfigPins.forEach(({ doc, pushpin }) => {
                    if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) {
                        this.recolorPin(pushpin);
                        this.map_pinHighlighted.delete(pushpin);
                    }
                });
                allConfigPins.forEach(({ doc, pushpin }) => {
                    if (doc[Highlight] && !this.map_pinHighlighted.get(pushpin)) {
                        this.recolorPin(pushpin, 'orange');
                        this.map_pinHighlighted.set(pushpin, true);
                    }
                });
            },
            { fireImmediately: true }
        );

        this._disposers.location = reaction(
            () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.map_zoom, mapType: this.rootDoc.map_type }),
            locationObject => {
                // if (this._bingMap.current)
                try {
                    locationObject?.zoom &&
                        this._bingMap.current?.setView({
                            mapTypeId: locationObject.mapType,
                            zoom: locationObject.zoom,
                            center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng),
                        });
                } catch (e) {
                    console.log(e);
                }
            },
            { fireImmediately: true }
        );
    };

    dragToggle = (e: React.PointerEvent) => {
        let dragClone: HTMLDivElement | undefined;

        setupMoveUpEvents(
            e,
            e,
            e => { // move event 
                if (!dragClone) {
                    dragClone = this._dragRef.current?.cloneNode(true) as HTMLDivElement; // copy draggable pin 
                    dragClone.style.position = 'absolute';
                    dragClone.style.zIndex = '10000';
                    DragManager.Root().appendChild(dragClone); // add clone to root 
                }
                dragClone.style.transform = `translate(${e.clientX - 15}px, ${e.clientY - 15}px)`;
                return false;
            },
            e => { // up event
                if (!dragClone) return;
                DragManager.Root().removeChild(dragClone);
                let target = document.elementFromPoint(e.x, e.y); // element for specified x and y coordinates
                while (target) {
                    if (target === this._ref.current) { 
                        const cpt = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
                        const x = cpt[0] - (this.props.PanelWidth() - this.sidebarWidth()) / 2;
                        const y = cpt[1] - 20 /* height of search bar */ - this.props.PanelHeight() / 2;
                        const location = this._bingMap.current.tryPixelToLocation(new this.MicrosoftMaps.Point(x, y));
                        this.createPushpin(location.latitude, location.longitude);
                        break;
                    }
                    target = target.parentElement;
                }
            },
            e => {
                const createPin = () => this.createPushpin(this.rootDoc.latitude, this.rootDoc.longitude, this.rootDoc.map);
                if (this.bingSearchBarContents) {
                    this.bingSearch().then(createPin);
                } else createPin();
            }
        );
    };

        // incrementer: number = 0;
    /*
     * Creates Pushpin doc and adds it to the list of annotations
     */
    @action
    createPushpin = undoable((latitude: number, longitude: number, location?: string, wikiData?: string) => {
        // Stores the pushpin as a MapMarkerDocument
        const pushpin = Docs.Create.PushpinDocument(
            NumCast(latitude),
            NumCast(longitude),
            false,
            [],
            {title: location ?? `lat=${latitude},lng=${longitude}`, map: location, description: "", wikiData: wikiData},
            // { title: map ?? `lat=${latitude},lng=${longitude}`, map: map },
            // ,'pushpinIDamongus'+ this.incrementer++
        );
        this.addDocument(pushpin, this.annotationKey);
        console.log(pushpin);
        return pushpin;

        // mapMarker.infoWindowOpen = true;
    }, 'createpin');

    @action
    createMapRoute = undoable((coordinates: List<any>, origin: string, destination: string) => {
        const mapRoute = Docs.Create.MapRouteDocument(
            false,
            [],
            {title: `${origin} -> ${destination}`, routeCoordinates: coordinates},
        );
        this.addDocument(mapRoute, this.annotationKey);
        return mapRoute;

        // mapMarker.infoWindowOpen = true;
    }, 'createmaproute');

    searchbarKeyDown = (e: any) => e.key === 'Enter' && this.bingSearch();




    @observable
    mapboxMapViewState: ViewState = {
        zoom: 9,
        longitude: -71.41,
        latitude: 41.82,
        pitch: 0,
        bearing: 0,
        padding: {
            top: 0,
            bottom: 0,
            left: 0,
            right: 0
        }
    }

    @observable
    featuresFromGeocodeResults: any[] = [];

    @action
    onMapMove = (e: ViewStateChangeEvent) => {
        this.mapboxMapViewState = e.viewState;
    }


    @action
    addMarkerForFeature = (feature: any) => {
        const location = feature.place_name;
        if (feature.center){
            const longitude = feature.center[0];
            const latitude = feature.center[1];
            const wikiData = feature.properties?.wikiData;
    
            this.createPushpin(
                latitude, 
                longitude, 
                location, 
                wikiData
            )
            this.featuresFromGeocodeResults = [];

        } else {
            // TODO: handle error
        }
    }



    /**
     * Makes a forward geocoding API call to Mapbox to retrieve locations based on the search input
     * @param searchText the search input (presumably a location)
     */
    handleSearchChange = async (searchText: string) => {
        const features = await MapboxApiUtility.forwardGeocodeForFeatures(searchText);
        if (features){
            runInAction(() => {
                this.featuresFromGeocodeResults = features;
            })
        }
        // try {
        //     const url = MAPBOX_FORWARD_GEOCODE_BASE_URL + encodeURI(searchText) +'.json' +`?access_token=${MAPBOX_ACCESS_TOKEN}`;
        //     const response = await fetch(url);
        //     const data = await response.json();
        //     runInAction(() => {
        //         this.featuresFromGeocodeResults = data.features;
        //     })
        // } catch (error: any){
        //     // TODO: handle error in better way 
        //     console.log(error);
        // }
    }
    // @action
    // debouncedCall = React.useCallback(debounce(this.debouncedOnSearchBarChange, 300), []);

    /**
     * Makes a reverse geocoding API call to retrieve features corresponding to a map click (based on longitude
     * and latitude). Sets the search results accordingly. 
     * @param e 
     */
    handleMapClick = async (e: MapLayerMouseEvent) => {
        e.preventDefault();
        const lngLat: LngLat = e.lngLat;
        const longitude: number = lngLat.lng;
        const latitude: number = lngLat.lat;

        const features = await MapboxApiUtility.reverseGeocodeForFeatures(longitude, latitude);
        if (features){
            runInAction(() => {
                this.featuresFromGeocodeResults = features;
            })
        }

        // // REVERSE GEOCODE TO GET LOCATION DETAILS 
        // try {
        //     const url = MAPBOX_REVERSE_GEOCODE_BASE_URL + encodeURI(longitude.toString() + "," + latitude.toString()) + '.json' +
        //         `?access_token=${MAPBOX_ACCESS_TOKEN}`;
        //     const response = await fetch(url);
        //     const data = await response.json();
        //     console.log("REV GEOCODE DATA: ", data);
        //     runInAction(() => {
        //         this.featuresFromGeocodeResults = data.features;
        //     })
        // } catch (error: any){
        //     // TODO: handle error in better way
        //     console.log(error);
        // }
    }

    @observable 
    currentPopup: PopupInfo | undefined = undefined;

    @action
    handleMarkerClick = (e: MarkerEvent<mapboxgl.Marker, MouseEvent>, pinDoc: Doc) => {
        this.featuresFromGeocodeResults = [];
        this.deselectPin(); // TODO: check this method
        this.selectedPin = pinDoc;
        // this.bingSearchBarContents = pinDoc.map;

        // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
        // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
        Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');

        this.recolorPin(this.selectedPin, 'green'); // TODO: check this method
       
        
        MapAnchorMenu.Instance.Delete = this.deleteSelectedPin;
        MapAnchorMenu.Instance.Center = this.centerOnSelectedPin;
        MapAnchorMenu.Instance.OnClick = this.createNoteAnnotation;
        MapAnchorMenu.Instance.StartDrag = this.startAnchorDrag;

        // pass in the pinDoc 
        MapAnchorMenu.Instance.setPinDoc(pinDoc);
        MapAnchorMenu.Instance.setAllMapboxPins(
            this.allAnnotations.filter(anno => !anno.layout_unrendered)
        )

        MapAnchorMenu.Instance.DisplayRoute = this.displayRoute;
        MapAnchorMenu.Instance.HideRoute = this.hideRoute;
        MapAnchorMenu.Instance.AddNewRouteToMap = this.createMapRoute;
        MapAnchorMenu.Instance.CreatePin = this.addMarkerForFeature;

        // const longitude = NumCast(pinDoc.longitude);
        // const latitude = NumCast(pinDoc.longitude);
        // const x = longitude + (this.props.PanelWidth() - this.sidebarWidth()) / 2;
        // const y = latitude + this.props.PanelHeight() / 2 + 20;
        // const cpt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y);
        MapAnchorMenu.Instance.jumpTo(e.originalEvent.clientX, e.originalEvent.clientY, true);

        document.addEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
    };

    @observable
    temporaryRouteSource: FeatureCollection = {
        type: 'FeatureCollection',
        features: []
    }

    @action
    displayRoute = (routeInfoMap: Record<TransportationType, any> | undefined, type: TransportationType) => {
        if (routeInfoMap){
            const newTempRouteSource: FeatureCollection = {
                type: 'FeatureCollection',
                features: [
                    {
                        type: 'Feature',
                        properties: {},
                        geometry: {
                            type: 'LineString',
                            coordinates: routeInfoMap[type].coordinates
                        }
                    }
                ]
            }
            // TODO: Create pin for destination 
            // TODO: Fly to point where full route will be shown 
            this.temporaryRouteSource = newTempRouteSource;
        }
    }

    @action
    hideRoute = () => {
        this.temporaryRouteSource = {
            type: 'FeatureCollection',
            features: []
        }
    }




    static _firstRender = true;
    static _rerenderDelay = 500;
    _rerenderTimeout: any;
    render() {
        // bcz:  no idea what's going on here, but bings maps have some kind of bug
        // such that we need to delay rendering a second map on startup until the first map is rendered.
        this.rootDoc[DocCss];
        if (MapBox._rerenderDelay) {
            // prettier-ignore
            this._rerenderTimeout = this._rerenderTimeout ??
                setTimeout(action(() => {
                    if ((window as any).Microsoft?.Maps?.Internal._WorkDispatcher) {
                        MapBox._rerenderDelay = 0;
                    }
                    this._rerenderTimeout = undefined;
                    this.rootDoc[DocCss] = this.rootDoc[DocCss] + 1;
                }), MapBox._rerenderDelay);
            return null;
        }

        const renderAnnotations = (childFilters?: () => string[]) => null;
        return (
            <div className="mapBox" ref={this._ref}>
                <div
                    className="mapBox-wrapper"
                    onWheel={e => e.stopPropagation()}
                    onPointerDown={async e => {
                        e.button === 0 && !e.ctrlKey && e.stopPropagation();
                    }}
                    style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
                    <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                    {renderAnnotations(this.opaqueFilter)}
                    {SnappingManager.GetIsDragging() ? null : renderAnnotations()}

                    <div className="mapBox-searchbar">
                        <TextField 
                            fullWidth
                            placeholder='Enter a location'
                            onChange={(e) => this.handleSearchChange(e.target.value)}
                        />
                        {/* <Autocomplete
                            fullWidth
                            id="map-location-searcher"
                            freeSolo
                            onInputChange={(e, searchText) => this.handleSearchChange(searchText)}
                            onChange={(e, selectedOption) => {
                                this.handleSearchChange(""); // clear input 
                                this.addMarkerForFeature(selectedOption);
                            }}
                            options={this.featuresFromGeocodeResults
                                .filter(feature => feature.place_name)
                                .map(feature => feature)}
                            getOptionLabel={(feature) => feature.place_name}
                            renderInput={(params) => (
                                <TextField
                                    {...params}
                                    placeholder='Enter a location'
                                />
                            )}
                            /> */}
                        {/* <EditableText
                            // editing
                            setVal={(newText: string | number) => typeof newText === 'string' && this.handleSearchChange(newText)}
                            // onEnter={e => this.bingSearch()}
                            onEnter={e => {}}
                            height={32}
                            // placeholder={this.bingSearchBarContents || 'Enter a location'}
                            placeholder='Enter a location'
                            textAlign="center" 
                        /> */}
                        {/* <IconButton
                            icon={
                                <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="magnifying-glass" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" color="#DFDFDF">
                                    <path
                                        fill="currentColor"
                                        d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"></path>
                                </svg>
                            }
                            onClick={this.bingSearch}
                            type={Type.TERT}
                        />
                        <div style={{ width: 30, height: 30 }} ref={this._dragRef} onPointerDown={this.dragToggle}>
                            <Button tooltip="drag to place a pushpin" icon={<FontAwesomeIcon size={'lg'} icon={'bullseye'} />} />
                        </div> */}
                    </div> 
                    <div className='mapbox-geocoding-search-results'>
                        {this.featuresFromGeocodeResults.length > 0 && (
                            <React.Fragment>
                            <h4>Choose a location for your pin: </h4>
                            {this.featuresFromGeocodeResults
                                .filter(feature => feature.place_name)
                                .map((feature, idx) => (
                                <div 
                                    key={idx} 
                                    className='search-result-container'
                                    onClick={() => {
                                        this.handleSearchChange("");
                                        this.addMarkerForFeature(feature);
                                    }}
                                >
                                    <div className='search-result-place-name'> 
                                    {feature.place_name}
                                    </div>
                                </div>
                                ))}
                            </React.Fragment>
                        )}
                    </div>              
                    <MapProvider>
                        <MapboxMap
                            ref={this._mapRef}
                            initialViewState={{
                                longitude: -100,
                                latitude: 40,
                                zoom: 3.5
                              }}
                            mapboxAccessToken={MAPBOX_ACCESS_TOKEN}
                            id="mapbox-map"
                            mapStyle="mapbox://styles/mapbox/streets-v9"
                            style={{height: '100%', width: '100%'}}
                            {...this.mapboxMapViewState}
                            onMove={this.onMapMove}
                            onDblClick={this.handleMapClick}
                            
                            
                        >
                            <Source id='temporary-route' type='geojson' data={this.temporaryRouteSource}/>
                            <Layer
                                id='temporary-route-layer'
                                type='line'
                                source='temporary-route'
                                layout={{"line-join": "round", "line-cap": "round"}}
                                paint={{"line-color": "#36454F", "line-width": 4, "line-dasharray": [1,1]}}
                                />
                            <>
                                {this.allPushpins
                                    // .filter(anno => !anno.layout_unrendered)
                                    .map((pushpin, idx) => (
                                        <Marker 
                                            key={idx} 
                                            longitude={NumCast(pushpin.longitude)} 
                                            latitude={NumCast(pushpin.latitude)}
                                            anchor='bottom'
                                            onClick={(e: MarkerEvent<mapboxgl.Marker, MouseEvent>) => this.handleMarkerClick(e, pushpin)}
                                        />
                                ))}
                            </>
                            
                            {/* {this.mapMarkers.length > 0 && this.mapMarkers.map((marker, idx) => (
                                <Marker key={idx} longitude={marker.longitude} latitude={marker.latitude}/>
                            ))} */}

                        </MapboxMap>
                    </MapProvider>

                    {/* <BingMapsReact
                        onMapReady={this.bingMapReady} //
                        bingMapsKey={bingApiKey}
                        height="100%"
                        mapOptions={this.bingMapOptions}
                        width="100%"
                        viewOptions={this.bingViewOptions}
                    /> */}
                    {/* <div>
                        {!this._mapReady
                            ? null
                            : this.allAnnotations
                                  .filter(anno => !anno.layout_unrendered)
                                  .map((pushpin, i) => (
                                      <DocumentView
                                          key={i}
                                          {...this.props}
                                          renderDepth={this.props.renderDepth + 1}
                                          Document={pushpin}
                                          DataDoc={undefined}
                                          PanelWidth={returnOne}
                                          PanelHeight={returnOne}
                                          NativeWidth={returnOne}
                                          NativeHeight={returnOne}
                                          onKey={undefined}
                                          onDoubleClick={undefined}
                                          onBrowseClick={undefined}
                                          childFilters={returnEmptyFilter}
                                          childFiltersByRanges={returnEmptyFilter}
                                          searchFilterDocs={returnEmptyDoclist}
                                          isDocumentActive={returnFalse}
                                          isContentActive={returnFalse}
                                          addDocTab={returnFalse}
                                          ScreenToLocalTransform={Transform.Identity}
                                          fitContentsToBox={undefined}
                                          focus={returnOne}
                                      />
                                  ))}
                    </div> */}
                    {/* <MapBoxInfoWindow
                                    key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}
                                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
                                    place={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})}
                                    markerMap={this.markerMap}
                                    PanelWidth={this.infoWidth}
                                    PanelHeight={this.infoHeight}
                                    moveDocument={this.moveDocument}
                                    isAnyChildContentActive={this.isAnyChildContentActive}
                                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                                /> */}
                </div>
                {/* </LoadScript > */}
                <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                    <SidebarAnnos
                        ref={this._sidebarRef}
                        {...this.props}
                        fieldKey={this.fieldKey}
                        rootDoc={this.rootDoc}
                        layoutDoc={this.layoutDoc}
                        dataDoc={this.dataDoc}
                        usePanelWidth={true}
                        showSidebar={this.SidebarShown}
                        nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                        PanelWidth={this.sidebarWidth}
                        sidebarAddDocument={this.sidebarAddDocument}
                        moveDocument={this.moveDocument}
                        removeDocument={this.sidebarRemoveDocument}
                    />
                </div>
                {this.sidebarHandle}
            </div>
        );
    }
}
